diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..7bcc29a --- /dev/null +++ b/.gitignore @@ -0,0 +1,5 @@ +.DS_Store +GenerateCodeTable.class +GenerateCodeTable$TableRow.class +gh-md-toc +Solution.class diff --git a/GenerateCodeTable.class b/GenerateCodeTable.class deleted file mode 100644 index da90633..0000000 Binary files a/GenerateCodeTable.class and /dev/null differ diff --git a/GenerateCodeTable.java b/GenerateCodeTable.java index d89f687..ea8173a 100644 --- a/GenerateCodeTable.java +++ b/GenerateCodeTable.java @@ -1,113 +1,451 @@ -import java.io.*; -/* -Used to generate table of contents. -1. No args: generate GitHub table -2. args == 'word', generate WordPress table. -3. args == 'all', genereate both table -*/ -public class GenerateCodeTable { - public static void main(String[] args) { - //Read Java Solution Folder - File folder = new File("./Java");//"." = current path - if (!folder.exists() || !folder.isDirectory()) { - System.out.println("Check Directory:1"); - return; - } - File[] listOfFiles = folder.listFiles(); - if (listOfFiles == null) { - System.out.println("Check Directory:2"); - return; - } - - String outputContent = ""; - File outFile; - - if (args.length == 0){ - outputContent = generateREADME(listOfFiles); - printTable("README.md", outputContent); - } else if (args != null && args[0].contains("word")) {//Wordpress - outputContent = generateWordPressPage(listOfFiles); - printTable("WordPress.txt", outputContent); - } else if (args != null && args[0].contains("all")) { - outputContent = generateREADME(listOfFiles); - printTable("README.md", outputContent); - outputContent = generateWordPressPage(listOfFiles); - printTable("WordPress.txt", outputContent); - } else { - return; - } - - - } - - public static String generateWordPressPage(File[] listOfFiles) { - //Assemble output - String outputContent = "Java Solutions to problems from LintCode(http://LintCode.com).\n" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - "" + - ""; - - int count = 0; - for (File file : listOfFiles) { - if (file.getName().contains(".java")) { - //outputContent += "|" + count + "|[" + file.getName() + "](https://github.com/shawnfan/LintCode/blob/master/Java/"+ file.getName() +")| |" + "Java|\n"; - outputContent+= - "" + - "" + - "" + - "" + - "" + - ""; - count++; - } - } - - outputContent += "
#Problem      Level  Language
" + count + "" + file.getName() + "Java
"; - return outputContent; - } - - - /* - Generate GitHub ReadMe file - */ - public static String generateREADME(File[] listOfFiles) { - //Assemble output - String outputContent = "# LintCode\n\n" + - "To host Java Solutions to problems from LintCode(http://LintCode.com).\n" + - "I Will try to revise the solutions once new problem or new testing case occurs.\n\n" + - "| Squence | Problem | Level | Language |\n" + - "|:-------:|:--------------|:---------------|:---------:|\n"; - int count = 0; - for (File file : listOfFiles) { - if (file.getName().contains(".java")) { - outputContent += "|" + count + "|[" + file.getName() + "](https://github.com/shawnfan/LintCode/blob/master/Java/"+ file.getName() +")| |" + "Java|\n"; - count++; - } - } - return outputContent; - } - - public static void printTable(String fileName, String outputContent) { - System.out.println(outputContent); - //Write to README.md - try { - File outFile = new File(fileName); - FileOutputStream fop = new FileOutputStream(outFile); - byte[] contentInBytes = outputContent.getBytes(); - fop.write(contentInBytes); - fop.flush(); - fop.close(); - System.out.println("Mission Accomplished. Now go ahead and commit"); - } catch (IOException e) { - e.printStackTrace(); - } - } - +import java.io.*; +import java.util.*; +/* +Used to generate table of contents. + - No args: generate GitHub table + - args == 'wordpress', generate WordPress table. + - args == 'review', generate Review Page + - args == 'all', genereate both table +*/ +public class GenerateCodeTable { + private static final String NOT_AVAILABLE = "N/A"; + /* + TableRow, used to hold table object and sort by date. + */ + private static class TableRow { + private long date = 0; + private String fileName; + private String level; + private String tutorialLink; + private String note; + private List tags; + + public TableRow( + long date, + String fileName, + String level, + String tutorialLink, + String note, + List tags) { + this.date = date; + this.fileName = fileName; + this.level = level; + this.tutorialLink = tutorialLink; + this.note = note; + this.tags = tags; + } + + public long getDate() { + return this.date; + } + + public String getFileName() { + return this.fileName; + } + + public String getLevel() { + return this.level; + } + + public String getTutorialLink() { + return this.tutorialLink; + } + + public String getNote() { + return this.note; + } + + public List getTags() { + return this.tags; + } + + public String getTableComposedLine() { + return "|[" + this.fileName + "](https://github.com/awangdev/LintCode/blob/master/Java/" + fileName.replace(" ", "%20") + + ")|" + this.level + "|Java|" + this.tags + "|"+ this.tutorialLink + "|\n"; + } + } + + public final static String TUTORIAL_KEY_WORD = "tutorial:"; + public final static String TAGS_KEY_WORD = "tags:"; + public static void main(String[] args) { + GenerateCodeTable gct = new GenerateCodeTable(); + //Read Java Solution Folder + File folder = new File("./Java");//"." = current path + if (!folder.exists() || !folder.isDirectory()) { + System.out.println("Check Directory:1"); + return; + } + File[] listOfFiles = folder.listFiles(); + if (listOfFiles == null) { + System.out.println("Check Directory:2"); + return; + } + + String outputContent = ""; + File outFile; + + if (args.length == 0){ + outputContent = gct.generateREADME(listOfFiles); + gct.printTable("README.md", outputContent); + } else if (args != null && args[0].contains("wordpress")) {//Wordpress + outputContent = gct.generateWordPressPage(listOfFiles); + gct.printTable("WordPress.txt", outputContent); + } else if (args != null && args[0].contains("review")) {//Review Page + outputContent = gct.generateReviewPage(listOfFiles); + gct.printTable("ReviewPage.md", outputContent); + } else if (args != null && args[0].contains("tags")) {//Wordpress + outputContent = gct.generateTagREADME(listOfFiles); + gct.printTable("TagREADME.md", outputContent); + } else if (args != null && args[0].contains("all")) { + outputContent = gct.generateREADME(listOfFiles); + gct.printTable("README.md", outputContent); + //outputContent = generateWordPressPage(listOfFiles); + //printTable("WordPress.txt", outputContent); + outputContent = gct.generateReviewPage(listOfFiles); + gct.printTable("ReviewPage.md", outputContent); + outputContent = gct.generateTagREADME(listOfFiles); + gct.printTable("TagREADME.md", outputContent); + outputContent = gct.generateTagReviewPage(listOfFiles); + gct.printTable("TagReviewPage.md", outputContent); + outputContent = gct.generateLevelReviewPage(listOfFiles); + gct.printTable("LevelReviewPage.md", outputContent); + } + + System.out.println("Mission Accomplished. Now go ahead and commit"); + } + + /* + Output the content into file + */ + public void printTable(String fileName, String outputContent) { + //System.out.println(outputContent); + //Write to README.md + try { + File outFile = new File(fileName); + FileOutputStream fop = new FileOutputStream(outFile); + byte[] contentInBytes = outputContent.getBytes(); + fop.write(contentInBytes); + fop.flush(); + fop.close(); + } catch (IOException e) { + e.printStackTrace(); + } + } + + /* + Generate Wordpress contents + */ + public String generateWordPressPage(File[] listOfFiles) { + //Assemble output + String outputContent = "Java Solutions to algorithm problems from LintCode, LeetCode...etc.\n" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + + int count = 0; + for (File file : listOfFiles) { + if (file.getName().contains(".java")) { + //outputContent += "|" + count + "|[" + file.getName() + "](https://github.com/awangdev/LintCode/blob/master/Java/"+ file.getName() +")| |" + "Java|\n"; + outputContent+= + "" + + "" + + "" + + "" + + ""; + count++; + } + } + + outputContent += "
#Problem Language
" + count + "" + file.getName() + "Java
"; + return outputContent; + } + + + /* + Generate GitHub ReadMe contents + */ + public String generateREADME(File[] listOfFiles) { + //Assemble output + String outputContent = "# Java Algorithm Problems\n\n" + + "### 程序员的一天\n" + + "从开始这个Github已经有将近两年时间, 很高兴这个repo可以帮到有需要的人. 我一直认为, 知识本身是无价的, 因此每逢闲暇, 我就会来维护这个repo, 给刷题的朋友们一些我的想法和见解. 下面来简单介绍一下这个repo:\n\n" + + "**README.md**: 所有所做过的题目\n\n" + + "**ReviewPage.md**: 所有题目的总结和归纳(不断完善中)\n\n" + + "**KnowledgeHash2.md**: 对所做过的知识点的一些笔记\n\n" + + "**SystemDesign.md**: 对系统设计的一些笔记\n\n" + + "**Future Milestone**: 我准备将一些有意思的题目,做成视频的形式给大家参考\n\n" + + //"**借此机会, 正式介绍一下自己, 以及我背后的大老板**\n\n" + + //"[![介绍一下自己!](https://img.youtube.com/vi/3keMZsV1I1U/0.jpg)](https://youtu.be/3keMZsV1I1U)\n\n" + + "希望大家学习顺利, 对未来充满希望(程序员也是找到好老板的!)\n" + + "有问题可以给我写邮件(wangdeve@gmail.com), 或者在GitHub上发issue给我.\n\n" + + "| Squence | Problem | Level | Language | Tags | Video Tutorial|\n" + + "|:-------:|:--------------|:------:|:---------:|:----:|:-------------:|\n"; + final List tableRows = getTableRows(listOfFiles); + for (int i = 0; i < tableRows.size(); i++) { + outputContent += "|" + i + tableRows.get(i).getTableComposedLine(); + } + return outputContent; + } + + /* Generate the tags Table*/ + public String generateTagREADME(File[] listOfFiles) { + String outputContent = generateTableOfContent("TagREADME.md") + "\n\n"; + String header = "| Squence | Problem | Level | Language | Tags | Video Tutorial|\n" + + "|:-------:|:--------------|:------:|:---------:|:----:|:-------------:|\n"; + final List tableRows = getTableRows(listOfFiles); + final HashMap> tagToRows = new HashMap<>(); + tableRows.forEach(tableRow -> { + for (String tag: tableRow.getTags()) { + if (tag == null || tag.length() == 0) { + continue; + } + if (!tagToRows.containsKey(tag)) { + tagToRows.put(tag, new ArrayList()); + } + tagToRows.get(tag).add(tableRow); + } + }); + // Build View + List>> entries = new ArrayList<>(tagToRows.entrySet()); + entries.sort(Comparator.comparing(entry -> -entry.getValue().size())); + + for (Map.Entry> entry : entries) { + StringBuffer sb = new StringBuffer(" \n \n \n## " + entry.getKey() + " (" + entry.getValue().size() + ")\n"); + sb.append(header); + List entryTableRows = entry.getValue(); + entryTableRows.sort(Comparator.comparing(row -> String.join(",", row.getTags()))); + for (int i = 0; i < entryTableRows.size(); i++) { + sb.append("|" + i + entryTableRows.get(i).getTableComposedLine()); + } + outputContent += sb.toString() + "\n\n\n"; + } + return outputContent; + } + + // Generate review files by tags + public String generateTagReviewPage(File[] listOfFiles) { + final List tableRows = getTableRows(listOfFiles); + final HashMap> tagToRows = new HashMap<>(); + // Group by tags: + tableRows.forEach(tableRow -> { + for (String tag: tableRow.getTags()) { + if (tag == null || tag.length() == 0) { + continue; + } + if (!tagToRows.containsKey(tag)) { + tagToRows.put(tag, new ArrayList()); + } + tagToRows.get(tag).add(tableRow); + } + }); + // Build View + String outputContent = ""; + for (Map.Entry> entry : tagToRows.entrySet()) { + StringBuffer sb = new StringBuffer(" \n \n \n## " + entry.getKey() + " (" + entry.getValue().size() + ")\n"); + sb.append(buildReviewSection(entry.getValue())); + outputContent += sb.toString() + "\n\n\n"; + printTable("review/" + entry.getKey() + ".md", sb.toString()); + } + return outputContent; + } + + // Generate review files by levels + public String generateLevelReviewPage(File[] listOfFiles) { + final List tableRows = getTableRows(listOfFiles); + final HashMap> levelToRows = new HashMap<>(); + // Group by levels: + tableRows.forEach(tableRow -> { + String level = tableRow.getLevel(); + if (NOT_AVAILABLE.equals(tableRow.getLevel())) { + level = "NA"; + } + if (!levelToRows.containsKey(level)) { + levelToRows.put(level, new ArrayList()); + } + levelToRows.get(level).add(tableRow); + }); + // Build View + String outputContent = ""; + for (Map.Entry> entry : levelToRows.entrySet()) { + StringBuffer sb = new StringBuffer(" \n \n \n## " + entry.getKey() + " (" + entry.getValue().size() + ")\n"); + sb.append(buildReviewSection(entry.getValue())); + outputContent += sb.toString() + "\n\n\n"; + printTable("review/level/" + entry.getKey() + ".md", sb.toString()); + } + return outputContent; + } + + /* + Generate Review Page contents + Review Page content: + 1. Sequence + 2. Name + 3. Difficulty + 4. Summary of solution, key points. + */ + public String generateReviewPage(File[] listOfFiles) { + //Assemble output + String outputContent = "# Review Page\n\n" + + "This page summarize the solutions of all problems. For thoughts,ideas written in English, refer to deach individual solution. \n" + + "New problems will be automatically updated once added.\n\n"; + + final List tableRows = getTableRows(listOfFiles); + int count = 0; + int countMiss = 0; + outputContent += buildReviewSection(tableRows); + return outputContent; + } + + private String buildReviewSection(List tableRows) { + int count = 0; + int countMiss = 0; + StringBuffer sb = new StringBuffer(); + for (TableRow tableRow: tableRows) { + // Skip non-ready items + if (NOT_AVAILABLE.equals(tableRow.getLevel())) { + System.out.println(countMiss + ". [File not yet formatted]: " + tableRow.getFileName() + " [Skipping. Please revisit]"); + countMiss++; + continue; + } + //System.out.println(count + ". " + tableRow.getLevel() + " [File not yet formatted]: " + tableRow.getFileName()); + sb.append("**" + count + ". [" + tableRow.getFileName()); + sb.append("](https://github.com/awangdev/LintCode/blob/master/Java/"); + sb.append(tableRow.getFileName().replace(" ", "%20") +")**"); + + sb.append(" Level: " + tableRow.getLevel() + " Tags: " + tableRow.getTags() + "\n"); + sb.append(" " + tableRow.getTutorialLink() + "\n"); + sb.append(tableRow.getNote() + "\n"); + sb.append("\n---\n\n"); + count++; + } + return sb.toString(); + } + + + + private List getTableRows(File[] listOfFiles) { + final ArrayList tableRows = new ArrayList<>(); + for (File file : listOfFiles) { + if (file.getName().contains(".java")) { + tableRows.add(getTableRow(file.getName())); + } + } + Collections.sort(tableRows, Comparator.comparing(TableRow::getDate)); + return tableRows; + } + + private TableRow getTableRow(String fileName) { + TableRow tableRow = null; + String tutorialLink = ""; + String calculatedLevel = NOT_AVAILABLE; + long timestamp = 0; + List tags = new ArrayList<>(); + try { + final BufferedReader reader = new BufferedReader(new InputStreamReader( + new FileInputStream("Java/" + fileName), "UTF-8")); + // Get level + String line = reader.readLine().trim(); + if (line.length() == 1 && !calculateLevel(line).isEmpty()){ + calculatedLevel = calculateLevel(line.toUpperCase()); + line = reader.readLine().trim(); + } + + // Get timestamp + if (line.length() != 0) { + try{ + timestamp = Long.parseLong(line); + line = reader.readLine().trim(); + }catch(final Exception e){ + System.out.println("Timestamp Not added yet: " + fileName); + } + } + + // Get tutorial + if (line.contains(TUTORIAL_KEY_WORD)) { + tutorialLink = "[Link](" + line.substring(TUTORIAL_KEY_WORD.length()) + ")"; + line = reader.readLine().trim(); + } + + // Get Tags + if (line.contains(TAGS_KEY_WORD)) { + // Do something + String tagLine = line.substring(TAGS_KEY_WORD.length()); + for (String tag : tagLine.split(",")) { + tags.add(tag.trim()); + } + Collections.sort(tags); + } + + // Get Note + String note = ""; + while ((line = reader.readLine()) != null && !line.equals("```") && !line.equals("/*")) { + note += line + "\n"; + } + + // Get result + tableRow = new TableRow(timestamp, fileName, calculatedLevel, tutorialLink, note, tags); + } catch (final Exception e) { + System.err.format("IOException: %s%n", e); + } + return tableRow; + } + + private String calculateLevel(final String level) { + switch(level) { + case "N" : + return "Naive"; + case "E" : + return "Easy"; + case "M" : + return "Medium"; + case "H" : + return "Hard"; + case "S" : + return "Super"; + case "R" : + return "Review"; + } + return NOT_AVAILABLE; + } + + // Build the table of contents of the page. Need to have 'gh-md-toc' installed + private String generateTableOfContent(String fileName) { + StringBuffer sb = new StringBuffer(); + try { + Runtime rt = Runtime.getRuntime(); + String[] commands = {"./gh-md-toc", fileName}; + Process proc = rt.exec(commands); + + BufferedReader stdInput = new BufferedReader(new + InputStreamReader(proc.getInputStream())); + + BufferedReader stdError = new BufferedReader(new + InputStreamReader(proc.getErrorStream())); + + // read the output from the command + System.out.println("Here is the standard output of the command:\n"); + String s = null; + while ((s = stdInput.readLine()) != null) { + if (!s.contains("[gh-md-toc]") && !s.contains("table-of-contents")) { + sb.append(s.trim() + "\n"); + } + } + + // read any errors from the attempted command + System.out.println("Here is the standard error of the command (if any):\n"); + while ((s = stdError.readLine()) != null) { + System.out.println(s); + } + } catch (final Exception e) { + System.err.format("IOException: %s%n", e); + } + System.out.println(sb.toString()); + return sb.toString(); + } } \ No newline at end of file diff --git a/Java/1-bit and 2-bit Characters.java b/Java/1-bit and 2-bit Characters.java new file mode 100644 index 0000000..f7977e9 --- /dev/null +++ b/Java/1-bit and 2-bit Characters.java @@ -0,0 +1,95 @@ +E +1517474043 +tags: Array + +方法1: +Greedy. +从第一个bit开始数: 如果遇到1, 一定是跳两位;如果遇到0, 一定是跳一位. +loop to end, and see if index reaches the end. + +方法2: +用DP硬做了一下: +1. 如果i位是0, 那么前面dp[i-1]或者dp[i-2] true就够了. +2. 如果i位是1, 那么i-1位必须是1才满足规则, 并且 dp[i-2]需要true. + +``` +/* +We have two special characters. The first character can be represented by one bit 0. The second character can be represented by two bits (10 or 11). + +Now given a string represented by several bits. Return whether the last character must be a one-bit character or not. The given string will always end with a zero. + +Example 1: +Input: +bits = [1, 0, 0] +Output: True +Explanation: +The only way to decode it is two-bit character and one-bit character. So the last character is one-bit character. +Example 2: +Input: +bits = [1, 1, 1, 0] +Output: False +Explanation: +The only way to decode it is two-bit character and two-bit character. So the last character is NOT one-bit character. +Note: + +1 <= len(bits) <= 1000. +bits[i] is always 0 or 1. +*/ +/* +Thoughts: +Greedy. +从第一个bit开始数: 如果遇到1, 一定是跳两位;如果遇到0, 一定是跳一位. +loop to end, and see if index reaches the end. +*/ +class Solution { + public boolean isOneBitCharacter(int[] bits) { + if (bits == null || bits.length == 0) { + return false; + } + int index = 0; + while (index < bits.length - 1) { + index += bits[index] % 2 == 1 ? 2 : 1; + } + return index == bits.length - 1; + } +} + +/* +Thoughts: +check if last bit must be '0': check can it be decoded if not single-bit-0 ending? +Should check if bits with less 2 bits still valid? +DP. +dp[i]: can be decoded at i +we only actaully need to know up to dp[i - 1] + +if bits[i] == 0: dp[i] = dp[i - 2] || dp[i - 1]; +if bits[i] == 1: dp[i] = dp[i - 2] && bits[i - 1] == 1; + +*/ +class Solution { + public boolean isOneBitCharacter(int[] bits) { + if (bits == null || bits.length == 0) { + return false; + } + int n = bits.length; + if (n == 1) { + return bits[0] == 0; + } + if (bits[n - 1] != 0) { + return false; + } + + boolean[] dp = new boolean[n]; + dp[0] = bits[0] == 0; + dp[1] = bits[0] != 0 || bits[1] != 1; + for (int i = 2; i < n - 1; i++) { + if (bits[i] == 0) { + dp[i] = dp[i - 2] || dp[i - 1]; + } else if (bits[i] == 1) { + dp[i] = dp[i - 2] && bits[i - 1] == 1; + } + } + return dp[n - 2]; + } +} +``` \ No newline at end of file diff --git a/Java/2 Sum II.java b/Java/2 Sum II.java new file mode 100755 index 0000000..8daceb4 --- /dev/null +++ b/Java/2 Sum II.java @@ -0,0 +1,126 @@ +M +1516439472 +tags: Array, Two Pointers, Binary Search + +与 2sum II - input array is sorted类似. 都是sort array, 然后two pointer. + +LintCode的题. 注意找的是greater/bigger than target。 + +由于给定条件允许O(nLogn): + sort + two pointer + +while里面two pointer移动。每次如果num[left]+num[right] > target,那么其中所有num[left++]的加上num[right]都>target. +也就是,num[right]不动,计算加入挪动left能有多少组,那就是: right-left这么多。 全部加到count上去。 +然后right--.换个right去和前面的left部分作比较。 + +``` +/* +Given an array of integers, find how many pairs in the array such that +their sum is bigger than a specific target number. Please return the number of pairs. +Example +numbers=[2, 7, 11, 15], target=24 + +return 1 + +Challenge +Either of the following solutions are acceptable: + +O(1) Space, O(nlogn) Time +Tags Expand +Two Pointers + +*/ + +/* +Thoughts: +After doing Trigle Count...Can we just do the same for this, move while pointers to center? +OMG. The original idea was sooooooo complicated. +*/ +public class Solution { + public int twoSum2(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return 0; + } + int count = 0; + int left = 0; + int right = nums.length - 1; + Arrays.sort(nums); + while (left < right) { + if (nums[left] + nums[right] > target) { + count += (right - left); + right--; + } else { + left++; + } + } + return count; + } +} + +//Below are bad solutions. Don't worry about them + +/* + +Thoughts: +1. For loop to try each element from (i ~ end). O(n) +2. In for, do binary search on nums[i] + nums[j] > target, +3. count += (length - j) + +Note: when not found, return nums.length, because at then end, (length - length) == 0, which will be added to count. +Note: Always pin target down, and move mid to compare. Don't get confused +Also, take care of corner cases. +*/ + +public class Solution { + public int twoSum2(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return 0; + } + int count = 0; + Arrays.sort(nums); + for (int i = 1; i < nums.length; i++) { + int index = binarySearch(nums, target - nums[i - 1], i, nums.length - 1); + count += nums.length - index; + } + return count; + } + + public int binarySearch(int[] nums, int target, int start, int end) { + int mid; + int sum; + while (start + 1 < end) { + mid = start + (end - start) /2; + if (mid - 1 >= 0 && nums[mid-1] <= target && target < nums[mid]) { + return mid; + } else if (mid + 1 < nums.length && nums[mid] <= target && target < nums[mid + 1]) { + return mid + 1; + } else if (target < nums[mid]) { + end = mid; + } else { + start = mid; + } + } + if (nums[start] > target) { + return start; + } + return (nums[end] > target) ? end : nums.length; + } +} + +//Brutle force, O(n^2) +public class Solution { + public int twoSum2(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return 0; + } + int count = 0; + for (int i = 0; i < nums.length - 1; i++) { + for (int j = i + 1; j < nums.length; j++) { + count += (nums[i] + nums[j] > target) ? 1 : 0; + } + } + } +} + +``` \ No newline at end of file diff --git a/Java/2 Sum.java b/Java/2 Sum.java deleted file mode 100644 index 06fea20..0000000 --- a/Java/2 Sum.java +++ /dev/null @@ -1,62 +0,0 @@ -/* -Given an array of integers, find two numbers such that they add up to a specific target number. - -The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based. - -Note -You may assume that each input would have exactly one solution - -Example -numbers=[2, 7, 11, 15], target=9 - -return [1, 2] - -Challenge -1. O(1) Space, O(nlogn) Time - -2. O(n) Space, O(n) Time - -Tags Expand -Array Two Pointers - - -Using a HashMap, O(n) space and O(n) time. -Thinking process: -Push everything into a HashMap. -Check if one element exist in the HashMap, if so save it. Meanwhile, save the other one. -Trick: after adding into the HashMap, we are looking for the 2nd index first. This is particularly because the way we write the code in optimized form: - always check (target - current) from the HashMap. If exist, that means index0 has already been pushed into the HashMap and current value is at index1. -(key, value) = (numbers[i], i) -Note: return index+1 because this is not 0-based. -*/ - -public class Solution { - /* - * @param numbers : An array of Integer - * @param target : target = numbers[index1] + numbers[index2] - * @return : [index1 + 1, index2 + 1] (index1 < index2) - */ - //Using HashMap - public int[] twoSum(int[] numbers, int target) { - if (numbers == null || numbers.length == 0) { - return null; - } - int[] rst = new int[2]; - HashMap map = new HashMap(); - for (int i = 0; i < numbers.length; i++) { - if (map.containsKey(target - numbers[i])) { - rst[0] = map.get(target - numbers[i]) + 1; - rst[1] = i + 1; - } else { - map.put(numbers[i], i); - } - } - return rst; - } -} - - - -//2. O(1) Space O(nlogn) time -//TODO - diff --git a/Java/3 Sum Closest.java b/Java/3 Sum Closest.java old mode 100644 new mode 100755 index ea4b234..317ca21 --- a/Java/3 Sum Closest.java +++ b/Java/3 Sum Closest.java @@ -1,51 +1,65 @@ -/* -Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. +M +1516610949 +tags: Array, Two Pointers + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 +``` +/* +Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. +Return the sum of the three integers. Note You may assume that each input would have exactly one solution. - Example For example, given array S = {-1 2 1 -4}, and target = 1. The sum that is closest to the target is 2. (-1 + 2 + 1 = 2). - Tags Expand Two Pointers Sort Array -Thinking process: -Similar to 3 SUM. -Starting from the left-element, assume it's the solution. Move the 2 pointers in the right-side-array. -Using the two pointers, trying to find ele1 + ele2 + ele3 = closest number to target. -Note: for comparing closet, use initial value Integer.MAX_VALUE. Be aware of the overflow of integer, use long to handle. */ -public class Solution { - /** - * @param numbers: Give an array numbers of n integer - * @param target : An integer - * @return : return the sum of the three integers, the sum closest target. - */ - public int threeSumClosest(int[] num, int target) { - if (num == null || num.length < 3) { - return Integer.MAX_VALUE; +/* +Thoughts: +3 SUM = for loop + 2SUM. Normally it'd be O(n^2). +Two pointer in the inner 2SUM.. +Note: result should be initialized with first 3 indexes. +*/ +class Solution { + public int threeSumClosest(int[] nums, int target) { + if (nums == null || nums.length < 3) { + return 0; } - Arrays.sort(num); - long closest = (long) Integer.MAX_VALUE; - for (int i = 0; i < num.length - 2; i++) { - int left = i + 1; - int right = num.length - 1; - while (left < right) { - int sum = num[i] + num[left] + num[right]; - if (sum == target) { - return sum; - } else if (sum < target) { - left++; + Arrays.sort(nums); // nLog(n) + long result = nums[0] + nums[1] + nums[2]; + for (int i = 0; i < nums.length - 2; i++) { + int start = i + 1; + int end = nums.length - 1; + while (start < end) { + long sum = nums[start] + nums[end] + nums[i]; + if (sum > target) { + end--; } else { - right--; + start++; } - closest = Math.abs(sum - target) < Math.abs(closest - target) - ? (long) sum : closest; - }//while - }//for - return (int) closest; + result = Math.abs(sum - target) < Math.abs(result - target) ? sum : result; + } + } + return (int)result; } } + + +/* +Previous notes +Thoughts: + Similar to 3 SUM. + Starting from the left-element, assume it's the solution. Move the 2 pointers in the right-side-array. + Using the two pointers, trying to find ele1 + ele2 + ele3 = closest number to target. + Note: for comparing closet, use initial value Integer.MAX_VALUE. Be aware of the overflow of integer, use long to handle. + +*/ +``` \ No newline at end of file diff --git a/Java/3 Sum.java b/Java/3 Sum.java deleted file mode 100644 index df0a63e..0000000 --- a/Java/3 Sum.java +++ /dev/null @@ -1,124 +0,0 @@ -/* -Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero. - -Note -Elements in a triplet (a,b,c) must be in non-descending order. (ie, a = b = c) - -The solution set must not contain duplicate triplets. - -Example -For example, given array S = {-1 0 1 2 -1 -4}, A solution set is: - -(-1, 0, 1) - -(-1, -1, 2) - -Tags Expand -Two Pointers Sort Array - -Thinking process: -Cannot use HashMap for this problem because of the duplicates. See the bottom of this file for the failed version. -Remember to check for null and edge-soluton. -Before everything, Arrays.sort() the given array, in order to effectively handle the duplicates. -At 3SUM level, takes 1 element out and do 2SUM on the rest of the front elements of the array. Note, 2SUM has multitple solutions (need to handle duplicates) -Cross-match the 2SUM solution with the selected element from 3SUM level. -*/ - - -public class Solution { - /** - * @param numbers : Give an array numbers of n integer - * @return : Find all unique triplets in the array which gives the sum of zero. - */ - public ArrayList> threeSum(int[] numbers) { - ArrayList> rst = new ArrayList>(); - if (numbers == null && numbers.length <= 2) {// Length at least >= 3 - return rst; - } - Arrays.sort(numbers);//Sort in order to handle duplicates - for (int i = numbers.length - 1; i >= 2; i--) {// i >=2 because at least 3 element in result. - if (i < numbers.length - 1 && numbers[i] == numbers[i + 1]) { - continue;//The case of numbers[i + 1] should have already covered all possibilities of the case numbers[i], so safe to skip - } - ArrayList> twoSum = calTwoSum(numbers, i - 1, 0 - numbers[i]);//Pick the 3rd element numbers[i] - for (int j = 0; j < twoSum.size(); j++) {//Find two sum of rest-front elements. Cross add them with numbers[i] - twoSum.get(j).add(numbers[i]); - } - rst.addAll(twoSum); - } - return rst; - } - //Two Sum. Multiple answer - public ArrayList> calTwoSum(int[] num, int end, int target) { - ArrayList> rst = new ArrayList>(); - if (num == null || num.length <= 1) {//Length at least >= 2 - return rst; - } - int left = 0; - int right = end; - while (left < right) { - if (num[left] + num[right] == target) { - ArrayList match = new ArrayList(); - match.add(num[left]); - match.add(num[right]); - rst.add(match); - left++; - right--; - //For unique number A, there is only 1 unique number B such that A + B == target. - //Therefore, once found the match, erase all numbers that's equal to A or equal to B - while (left < right && num[left] == num[left - 1]) { - left++; - } - while (left < right && num[right] == num[right + 1]) { - right--; - } - } else if (num[left] + num[right] < target) {//Since int[] num is sorted: move L to right-side to get larger value. - left++; - } else { - right--; - } - } - return rst; - } -} - - - - - - - -/* -The following is a exceeding time version. -I believe the concept is clear, but it does not handle duplicates well. So we can't use this version. - - -public class Solution { - public ArrayList> threeSum(int[] numbers) { - ArrayList> rst = new ArrayList>(); - if (numbers.length <= 2) { - return rst; - } - Arrays.sort(numbers); - for (int i = 0; i < numbers.length; i++){ - HashMap map = new HashMap(); - for (int j = i; j < numbers.length; j++) { - int remain = 0 - numbers[i] - numbers[j]; - if (map.containsKey(remain) && map.get(remain) != i) { - ArrayList list = new ArrayList(); - list.add(numbers[i]); - list.add(remain); - list.add(numbers[j]); - if (!rst.contains(list)){ - rst.add(list); - } - } else { - map.put(numbers[j], j); - } - } - } - return rst; - } -} - -*/ \ No newline at end of file diff --git a/Java/3Sum Smaller.java b/Java/3Sum Smaller.java new file mode 100755 index 0000000..6efb30f --- /dev/null +++ b/Java/3Sum Smaller.java @@ -0,0 +1,91 @@ +M +1517465602 +tags: Two Pointers, Array + +一般的O(n3)肯定不行。在此基础上优化。 +发现j,k满足条件时候,(k - j)就是所有 sum target, 又因为j不能后退,只能k--,那么问题就被锁定了. 这样可以做到O(n2) + +``` +/* +Given an array of n integers nums and a target, find the number of index triplets i, j, k with 0 <= i < j < k < n +that satisfy the condition nums[i] + nums[j] + nums[k] < target. + +For example, given nums = [-2, 0, 1, 3], and target = 2. + +Return 2. Because there are two triplets which sums are less than 2: + +[-2, 0, 1] +[-2, 0, 3] + +Follow up: +Could you solve it in O(n2) runtime? + +Tags: Array Two Pointers +Similar Problems:(M) 3Sum, (M) 3Sum Closest + +*/ + +/* +Thoughts: +For loop over initial selection. Have 2sum solution within (2 pointer). +When < target, count+= end-start, start++. +When >= target, end--. +*/ +class Solution { + public int threeSumSmaller(int[] nums, int target) { + if (nums == null || nums.length <= 2) { + return 0; + } + Arrays.sort(nums); + int count = 0; + for (int i = 0; i < nums.length - 2; i++) { // first num, num[i] is the fixed item + int start = i + 1, end = nums.length - 1; // move start, end + while (start < end) { + if (nums[i] + nums[start] + nums[end] < target) { + count += end - start; + start++; + } else { + end--; + } + } + } + return count; + } +} + +/* +Thoughts: +Similar to 3 sum, but ofcourse, this one check on '<' so we can not use HashMap anymore. +Basic concept is to fix first number, then check for the rest two numbers, see if they addup < target. +When checking j and k, realize something nice: + if nums[j] + nums[k] < target - nums[i], that means for all index <= k will work, so directly add (k - j) to result (that's: index = j+1, j+2, ....,k) + also, move j forward for next round. +OR, if three-add-up >= target, since j can only increase, we do k-- to make the three-add-up smaller + +Note: +Don't forget to sort, otherwise the sequence/order is unpredictable +*/ +public class Solution { + public int threeSumSmaller(int[] nums, int target) { + if (nums == null || nums.length <= 2) { + return 0; + } + Arrays.sort(nums); + int rst = 0; + for (int i = 0; i < nums.length - 2; i++) { + int j = i + 1; + int k = nums.length - 1; + while (j < k) { + if (nums[i] + nums[j] + nums[k] >= target) { + k--; + } else { + rst += (k - j); + j++; + } + } + }//END for + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/3Sum.java b/Java/3Sum.java new file mode 100755 index 0000000..c6447b0 --- /dev/null +++ b/Java/3Sum.java @@ -0,0 +1,257 @@ +M +1516689562 +tags: Array, Two Pointers + + +#### sort array, for loop + two pointer. O(n^2) +- 处理duplicate wthin triplets: +- 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. +- 如果是triplet内有重复, 用完start point, 移动到结尾. + +Previous notes: +注意: + 1. 找 value triplets, 多个结果。注意,并非找index。 + 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 + +步骤: + 1. For loop 挑个数字A. + 2. 2Sum 出一堆2个数字的结果 + 3. Cross match 步骤1里面的A. + +时间 O(n^2), 两个nested loop + +另外, 还是可以用HashMap来做2Sum。稍微短点。还是要注意handle duplicates. + + +``` +/* +Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? +Find all unique triplets in the array which gives the sum of zero. + +Example +For example, given array S = {-1 0 1 2 -1 -4}, A solution set is: + +(-1, 0, 1) +(-1, -1, 2) +Note +Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c) + +The solution set must not contain duplicate triplets. + +Tags Expand +Two Pointers Sort Array Facebook +*//* +Thoughts: +Sort the list, do a for loop and two pointer within. +Make sure to skip duplicated index value: +when 'start' is duplicated, start++ until no duplicates. +when i is duplicated, continue in for loop and get to end of last duplicate and use that as i. + +O(n) * O(n) -> O(n^2) +*/ +class Solution { + public List> threeSum(int[] nums) { + List> result = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return result; + } + Arrays.sort(nums); + for (int i = 2; i < nums.length; i++) { + if (i + 1 < nums.length && nums[i] == nums[i + 1]) { + continue; + } + int start = 0; + int end = i - 1; + while (start < end) { + if (nums[start] + nums[end] + nums[i] == 0) { + result.add(Arrays.asList(nums[start], nums[end], nums[i])); + start++; + while (start < end && nums[start - 1] == nums[start]) { // skip duplicates + start++; + } + } else if (nums[start] + nums[end] + nums[i] < 0) { + start++; + } else { + end--; + } + } + } + return result; + } +} + +/* +Thoughts: + Remember to check for null and edge-soluton. + Before everything, Arrays.sort() the given array, in order to effectively handle the duplicates. + At 3SUM level, takes 1 element out and do 2SUM on the rest of the front elements of the array. Note, 2SUM has multitple solutions (need to handle duplicates) + Cross-match the 2SUM solution with the selected element from 3SUM level. +*/ + +public class Solution { + public ArrayList> threeSum(int[] numbers) { + ArrayList> rst = new ArrayList>(); + if (numbers == null && numbers.length <= 2) {// Length at least >= 3 + return rst; + } + Arrays.sort(numbers);//Sort in order to handle duplicates + for (int i = numbers.length - 1; i >= 2; i--) {// i >=2 because at least 3 element in result; starting from end, ensures non-descending order + if (i < numbers.length - 1 && numbers[i] == numbers[i + 1]) { + continue;//The case of numbers[i + 1]: should have already covered all possibilities of the case numbers[i], so safe to skip + } + ArrayList> twoSum = calTwoSum(numbers, i - 1, 0 - numbers[i]);//Pick the 3rd element numbers[i] + for (int j = 0; j < twoSum.size(); j++) {//Find two sum of rest-front elements. Cross add them with numbers[i] + twoSum.get(j).add(numbers[i]); + } + rst.addAll(twoSum); + } + return rst; + } + //Two Sum. Multiple answer + public ArrayList> calTwoSum(int[] num, int end, int target) { + ArrayList> rst = new ArrayList>(); + int left = 0; + int right = end; + while (left < right) { + if (num[left] + num[right] == target) { + ArrayList match = new ArrayList(); + match.add(num[left]); + match.add(num[right]); + rst.add(match); + left++; + right--; + //For unique number A, there is only 1 unique number B such that A + B == target. + //Therefore, once found the match, erase all numbers that's equal to A or equal to B + while (left < right && num[left] == num[left - 1]) { + left++; + } + while (left < right && num[right] == num[right + 1]) { + right--; + } + } else if (num[left] + num[right] < target) {//Since int[] num is sorted: move L to right-side to get larger value. + left++; + } else { + right--; + } + } + return rst; + } +} + + +/* + Thoughts: + Exact same approach, except using HashMap in 2Sum +*/ +//With HashMap 2Sum +public class Solution { + public ArrayList> threeSum(int[] numbers) { + ArrayList> rst = new ArrayList>(); + if (numbers == null && numbers.length <= 2) {// Length at least >= 3 + return rst; + } + Arrays.sort(numbers);//Sort in order to handle duplicates + for (int i = numbers.length - 1; i >= 2; i--) {// i >=2 because at least 3 element in result; starting from end, ensures non-descending order + if (i < numbers.length - 1 && numbers[i] == numbers[i + 1]) { + continue;//The case of numbers[i + 1]: should have already covered all possibilities of the case numbers[i], so safe to skip + } + ArrayList> twoSum = calTwoSum(numbers, i - 1, 0 - numbers[i]);//Pick the 3rd element numbers[i] + for (int j = 0; j < twoSum.size(); j++) {//Find two sum of rest-front elements. Cross add them with numbers[i] + twoSum.get(j).add(numbers[i]); + } + rst.addAll(twoSum); + } + return rst; + } + //Two Sum. Multiple answer, with HashMap + public ArrayList> calTwoSum(int[] num, int end, int target) { + ArrayList> rst = new ArrayList>(); + ArrayList match; + HashMap map = new HashMap(); + for (int i = 0; i <= end; i++) { + if (map.containsKey(num[i])) { + match = new ArrayList(); + match.add(num[map.get(num[i])]); + match.add(num[i]); + if (!rst.contains(match)) { + rst.add(new ArrayList(match)); + } + } else { + map.put(target - num[i], i); + } + //Skip duplicate + if (i < end && num[i] == num[i + 1]) { + continue; + } + } + return rst; + } +} + + +/* + From LeetCode Solution + Thoughts: + sort list. O(nLogn) + end: n^2 + for (i = 0 ~ n) { + int target = 0 - nums[i]; + while (start + 1 < end) { + start + end == target { + rst.add(i, star, end); + keep looking: start ++, end-- + } + else start + end < target? + start++ + else + end--; + } + } + } + +Note: + Check duplicates. Compute a unique string to savei set +*/ + +public class Solution { + public List> threeSum(int[] nums) { + List> rst = new ArrayList>(); + if (nums == null || nums.length == 0) { + return rst; + } + + Arrays.sort(nums); + HashSet set = new HashSet(); + //use old target to check duplicates. instead of set. + for (int i = 0; i < nums.length - 2; i++) { + int target = 0 - nums[i]; + int start = i + 1; + int end = nums.length - 1; + + ArrayList list = new ArrayList(); + while (start < end) { + if (nums[start] + nums[end] == target && + !set.contains(nums[i] + "," + nums[start] + "," + nums[end])) { + list.add(nums[i]); + list.add(nums[start]); + list.add(nums[end]); + rst.add(list); + set.add(nums[i] + "," + nums[start] + "," + nums[end]); + list = new ArrayList(); + start++; + end--; + } else if (nums[start] + nums[end] < target) { + start++; + } else { + end--; + } + }//end while + } + + return rst; + } +} + + +``` \ No newline at end of file diff --git a/Java/4 Sum.java b/Java/4 Sum.java deleted file mode 100644 index 7426ba2..0000000 --- a/Java/4 Sum.java +++ /dev/null @@ -1,81 +0,0 @@ -/* -Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target. - -Example -For example, given array S = {1 0 -1 0 -2 2}, and target = 0. A solution set is: - -(-1, 0, 0, 1) - -(-2, -1, 1, 2) - -(-2, 0, 0, 2) - -Note -Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d) - -The solution set must not contain duplicate quadruplets. - -Tags Expand -Two Pointers Sort Hash Table Array - -Thinking process: -Perform another layer outsideo of 3SUM. - -Note: If try to divide and perform two 2SUM, it will be a bit difficult. Refer to http://blog.csdn.net/linhuanmars/article/details/24826871 -*/ - -public class Solution { - /** - * @param numbers : Give an array numbersbers of n integer - * @param target : you need to find four elements that's sum of target - * @return : Find all unique quadruplets in the array which gives the sum of - * zero. - */ - public ArrayList> fourSum(int[] numbers, int target) { - ArrayList> rst = new ArrayList>(); - if(numbers == null || numbers.length < 4) { - return rst; - } - Arrays.sort(numbers); - //Pick 1st element - for (int i = 0; i < numbers.length - 3; i++) { - if (i != 0 && numbers[i] == numbers[i - 1]) {//Check for duplicate of 1st element - continue; - } - //Pick 2nd element - for (int j = i + 1; j < numbers.length - 2; j++) { - if (j != i + 1 && numbers[j] == numbers[j - 1]) {//Check for duplicate of 2nd element - continue; - } - //Pick 3rd and 4th element - int third = j + 1; - int fourth = numbers.length - 1; - while (third < fourth) { - int sum = numbers[i] + numbers[j] + numbers[third] + numbers[fourth]; - if (sum < target) { - third++; - } else if (sum > target) { - fourth--; - } else {//sum == target - ArrayList list = new ArrayList(); - list.add(numbers[i]); - list.add(numbers[j]); - list.add(numbers[third]); - list.add(numbers[fourth]); - rst.add(list); - third++; - fourth--; - while (third < fourth && numbers[third] == numbers[third - 1]) { - third++; - } - while (third < fourth && numbers[fourth] == numbers[fourth + 1]){ - fourth--; - } - } - } - } - } - return rst; - } -} - diff --git a/Java/4Sum.java b/Java/4Sum.java new file mode 100755 index 0000000..0d15e7e --- /dev/null +++ b/Java/4Sum.java @@ -0,0 +1,228 @@ +M +1533626601 +tags: Hash Table + +#### Based on 2sum +- 1. 利用2Sum的原理,把4Sum分为连个2Sum。左一个pair,右一个pair,每个pair里面放2个数字。 +- 2. 以一个点,i,作为分界口,也要列举出所有i之前的pair,作为基础。 +- 3. 再尝试从所有i+1后面,找合适的2nd pair。 +- Time: O(n^2 * x), where x = # of candidates, still slow +- 可以用HashSet, 可以直接比较list里面每一个元素, 保证set不重复. +- Previous Notes: 在造class Pair时候,要做@override的function: hashCode(), equals(Object d). 平时不太想得起来用。 +- 参见 http://lifexplorer.me/leetcode-3sum-4sum-and-k-sum/ + +#### Based on 3Sum +- 3Sum外面再加一层. 参考3Sum. 时间O(n^3)。 但此方法在k-sum时候,无疑过于费时间. O(n^k) + +``` +/* +Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? + +Find all unique quadruplets in the array which gives the sum of target. + +Example +Given array S = {1 0 -1 0 -2 2}, and target = 0. A solution set is: + +(-1, 0, 0, 1) +(-2, -1, 1, 2) +(-2, 0, 0, 2) +Note +Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d) +The solution set must not contain duplicate quadruplets. + +Tags Expand +Two Pointers Sort Hash Table Array + +*/ + +/* +Thoughts: +Can't do for loop over 3sum, which is O(n^3), not doable. +We can break the nums into 2 major parts by index i. +1st part: from [0 ~ i], we'll try all possible ways to pair [x, i] and store the sum of nums[x]+nums[i] in map. +Note: key is sum, and the value is a hashSet of ArrayList. Where the hashset has itself functions to tell duplicate of pairs. +2nd part: try [i + 1, end], see if any combination sum that makes: target - sum exist in map. -> becomes same old 2sum problem. +O(n) +*/ +/** + Why having below front-pair-building after previous for end-pair-checking for loop? + Here: we build [0 ~ i], and in next round, when we processs [i + 1, end], pairs built among [0~i] below will all be used. + Rather: if we have lift the for loop below to calculate [0~i] before the end-pair-checking of same [i~end], + there is one index at [i] will overlap, which turns to be incorrect. + Therefore, we build front-pairs aftwarwards: it's building [0~i] here, which aims to help next round of end-pair-checking on [i+1, end]. +*/ +class Solution { + public List> fourSum(int[] nums, int target) { + List> result = new ArrayList<>(); + if (nums == null || nums.length <= 3) return result; + + int n = nums.length; + Arrays.sort(nums); + Map> map = new HashMap<>(); + Set cache = new HashSet<>(); + for (int i = 0; i < n; i++) { + // Check if [i + 1, end] can be added up to target + for (int k = i + 1; k < n; k++) { + int sum = nums[i] + nums[k]; + if (map.containsKey(target - sum)) {// Try to match up the 4 pairs + for (List frontPair : map.get(target - sum)) { + List list = Arrays.asList(frontPair.get(0), frontPair.get(1), nums[i], nums[k]); + String key = buildKey(list); + if (!cache.contains(key)) { + result.add(list); + cache.add(key); + } + } + } + } + + // Build up the pair from [0 ~ i] + for (int j = 0; j < i; j++) { + int sum = nums[j] + nums[i]; + map.putIfAbsent(sum, new HashSet<>()); + map.get(sum).add(Arrays.asList(nums[j], nums[i])); + } + } + return result; + } + + private String buildKey(List list) { + StringBuffer sb = new StringBuffer(); + for (int num : list) sb.append(num + "@"); + return sb.toString(); + } +} + + + +/* +Thoughts +Perform another layer outside of 3SUM. O(n^3). +Note: If try to divide and perform two 2SUM, it will be a bit difficult. Refer to http://blog.csdn.net/linhuanmars/article/details/24826871 + +*/ +public class Solution { + + public ArrayList> fourSum(int[] numbers, int target) { + ArrayList> rst = new ArrayList>(); + if(numbers == null || numbers.length < 4) { + return rst; + } + Arrays.sort(numbers); + //Pick 1st element + for (int i = 0; i < numbers.length - 3; i++) { + if (i != 0 && numbers[i] == numbers[i - 1]) {//Check for duplicate of 1st element + continue; + } + //Pick 2nd element + for (int j = i + 1; j < numbers.length - 2; j++) { + if (j != i + 1 && numbers[j] == numbers[j - 1]) {//Check for duplicate of 2nd element + continue; + } + //Pick 3rd and 4th element + int third = j + 1; + int fourth = numbers.length - 1; + while (third < fourth) { + int sum = numbers[i] + numbers[j] + numbers[third] + numbers[fourth]; + if (sum < target) { + third++; + } else if (sum > target) { + fourth--; + } else {//sum == target + ArrayList list = new ArrayList(); + list.add(numbers[i]); + list.add(numbers[j]); + list.add(numbers[third]); + list.add(numbers[fourth]); + rst.add(list); + third++; + fourth--; + while (third < fourth && numbers[third] == numbers[third - 1]) { + third++; + } + while (third < fourth && numbers[fourth] == numbers[fourth + 1]){ + fourth--; + } + } + } + } + } + return rst; + } +} + + +/* +NOT Complete yet. Has a order issue in HashSet +http://lifexplorer.me/leetcode-3sum-4sum-and-k-sum/ +Thoughts: +Utilize 2Sum. + +Notes 2017: no need to create a new class Pair. We can just use list in HashSet, which compairs the list element and eleminate duplicated list. +*/ + +public class Solution { + //Create class Pair for HashSet to use + class Pair { + Integer x; + Integer y; + + public Pair(int x, int y){ + this.x = x; + this.y = y; + } + + @Override + public int hashCode(){ + return this.x.hashCode() + this.y.hashCode(); + } + + @Override + public boolean equals(Object d) { + if (!(d instanceof Pair)) { + return false; + } + Pair p = (Pair)d; + return (this.x == p.x) && (this.y == p.y); + } + } + + public ArrayList> fourSum(int[] numbers, int target) { + ArrayList> rst = new ArrayList>(); + if (numbers == null || numbers.length < 4) { + return rst; + } + Arrays.sort(numbers); + HashMap> map = new HashMap>(); + for (int i = 0; i < numbers.length; i++) { + for (int j = i + 1; j < numbers.length; j++) { + int sum = numbers[i] + numbers[j]; + if (map.containsKey(target - sum)) { + for (Pair p : map.get(target - sum)) { + ArrayList list = new ArrayList(); + list.add(p.x); + list.add(p.y); + list.add(numbers[i]); + list.add(numbers[j]); + if (!rst.contains(list)) { + rst.add(list); + } + } + } + } + //Add all pairs up to i + for (int j = 0; j < i; j++) { + int sum = numbers[i] + numbers[j]; + if (!map.containsKey(sum)) { + map.put(sum, new HashSet()); + } + map.get(sum).add(new Pair(numbers[j], numbers[i])); + } + } + + return rst; + } + +} + +``` \ No newline at end of file diff --git a/Java/A+B.java b/Java/A+B.java deleted file mode 100644 index 61eaebc..0000000 --- a/Java/A+B.java +++ /dev/null @@ -1,37 +0,0 @@ -/* -For given numbers a and b in function aplusb, return the sum of them. - -Note -You don't need to parse the input and output. Just calculate and return. - -Example -If a=1 and b=2 return 3 - -Challenge -Can you do it with out + operation? - -Clarification -Are a and b both 32-bit integers? - - - Yes. - -Thinking process: -Bit operation. Just to remmeber this problem, doing A+B using bit. -*/ - -class Solution { - /* - * param a: The first integer - * param b: The second integer - * return: The sum of a and b - */ - public int aplusb(int a, int b) { - while (b != 0) { - int carry = a & b; - a = a ^ b; - b = carry << 1; - } - return a; - } -}; - diff --git a/Java/Accounts Merge.java b/Java/Accounts Merge.java new file mode 100644 index 0000000..160c27e --- /dev/null +++ b/Java/Accounts Merge.java @@ -0,0 +1,185 @@ +M +1532588768 +tags: DFS, Union Find, Hash Table, Hash Table + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + +``` +/* +Given a list accounts, each element accounts[i] is a list of strings, +where the first element accounts[i][0] is a name, +and the rest of the elements are emails representing emails of the account. + +Now, we would like to merge these accounts. Two accounts definitely belong to the same person +if there is some email that is common to both accounts. +Note that even if two accounts have the same name, +they may belong to different people as people could have the same name. +A person can have any number of accounts initially, but all of their accounts definitely have the same name. + +After merging the accounts, return the accounts in the following format: +the first element of each account is the name, and the rest of the elements are emails in sorted order. +The accounts themselves can be returned in any order. + +Example 1: +Input: +accounts = [["John", "johnsmith@mail.com", "john00@mail.com"], ["John", "johnnybravo@mail.com"], ["John", "johnsmith@mail.com", "john_newyork@mail.com"], ["Mary", "mary@mail.com"]] +Output: [["John", 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com'], ["John", "johnnybravo@mail.com"], ["Mary", "mary@mail.com"]] +Explanation: +The first and third John's are the same person as they have the common email "johnsmith@mail.com". +The second John and Mary are different people as none of their email addresses are used by other accounts. +We could return these lists in any order, for example the answer [['Mary', 'mary@mail.com'], ['John', 'johnnybravo@mail.com'], +['John', 'john00@mail.com', 'john_newyork@mail.com', 'johnsmith@mail.com']] would still be accepted. +Note: + +The length of accounts will be in the range [1, 1000]. +The length of accounts[i] will be in the range [1, 10]. +The length of accounts[i][j] will be in the range [1, 30]. + +*/ + +// Hash Table +class Solution { + class Account { + String name; + Set emails; + public Account(String name, Set emails) { + this.name = name; + this.emails = emails; + } + } + + private boolean validate(List> accounts) { + return accounts == null || accounts.size() == 0 || accounts.get(0) == null || accounts.get(0).size() == 0; + } + public List> accountsMerge(List> accounts) { + List> rst = new ArrayList<>(); + if (validate(accounts)) return rst; + + // map, list, populate + Map map = new HashMap<>(); + Set mergedAccounts = new HashSet<>(); + for (List row : accounts) { + Account account = new Account(row.get(0), new HashSet<>()); + Set existingAccounts = new HashSet<>(); + for (int i = 1; i < row.size(); i++) { + account.emails.add(row.get(i)); + if (map.containsKey(row.get(i))) { + Account existingAccount = map.get(row.get(i)); + mergedAccounts.remove(existingAccount); + account.emails.addAll(existingAccount.emails); + } + } + mergedAccounts.add(account); + for (String email: account.emails) { + map.put(email, account); + } + } + + // output as needed + for (Account account : mergedAccounts) { + List row = new ArrayList<>(); + List emails = new ArrayList<>(account.emails); + Collections.sort(emails); + row.add(account.name); + row.addAll(emails); + rst.add(row); + } + + return rst; + } +} + + +/* +Union Find +Approach similar to LintCode(Find the weak connected component in directed graph) +UnionFind: Map +1. Group account into union. ( don't forget to preserve email -> name mapping) +2. Group parent -> children +3. output +*/ +class Solution { + Map accountMap = new HashMap<>(); + Map parentMap = new HashMap<>(); + + public List> accountsMerge(List> accounts) { + List> rst = new ArrayList<>(); + if (validate(accounts)) return rst; + + buildUnionFind(accounts); + + for (List account : accounts) { + for (int i = 1; i < account.size() - 1; i++) { + union(account.get(i), account.get(i + 1)); // email across acounts will be auto-merged/linked in unionFind + } + } + + Map> result = new HashMap<>(); + for (String email : parentMap.keySet()) { + String parent = find(email); + result.putIfAbsent(parent, new ArrayList<>()); + result.get(parent).add(email); + } + + for (List list: result.values()) { + Collections.sort(list); + list.add(0, accountMap.get(list.get(0))); + rst.add(list); + } + + return rst; + } + + private boolean validate(List> accounts) { + return accounts == null || accounts.size() == 0 || accounts.get(0) == null || accounts.get(0).size() == 0; + } + + private void buildUnionFind(List> accounts) { + for (List account : accounts) { + String name = account.get(0); + for (int i = 1; i < account.size(); i++) { + accountMap.put(account.get(i), name); // email -> name mapping + parentMap.put(account.get(i), account.get(i)); // union parent map + } + } + } + + private String find(String email) { + String parent = parentMap.get(email); + if (parent.equals(parentMap.get(parent))) { + return parent; + } + return find(parent); + } + + private void union(String a, String b) { + String parentA = find(a); + String parentB = find(b); + if (!parentA.equals(parentB)) { + parentMap.put(parentA, parentB); + } + } +} +``` \ No newline at end of file diff --git a/Java/Add Binary.java b/Java/Add Binary.java old mode 100644 new mode 100755 index e8d1f35..8d600f8 --- a/Java/Add Binary.java +++ b/Java/Add Binary.java @@ -1,4 +1,21 @@ +E +1533603550 +tags: Math, String, Two Pointers + +#### Two pointers +- Use two pointers i, j to track the 2 strings +- Add when i and j are applicable. While (i >= 0 || j >= 0) +- StringBuffer.insert(0, x); +- handle carry + +#### wrong: convert to int +- 土办法没技术,把binary换成数字,加起来,再换成binary +- 如果input很大,那么很可能int,long都hold不住。不保险。 + +``` /* +Add Binary + Given two binary strings, return their sum (also a binary string). Example @@ -9,10 +26,93 @@ Given two binary strings, return their sum (also a binary string). Return 100 Tags Expand -String Binary +String Binary Facebook +*/ +public class Solution { + public String addBinary(String a, String b) { + StringBuilder sb = new StringBuilder(); + int i = a.length() - 1, j = b.length() -1, carry = 0; + while (i >= 0 || j >= 0) { + int sum = carry; + if (i >= 0) sum += a.charAt(i--) - '0'; + if (j >= 0) sum += b.charAt(j--) - '0'; + sb.insert(0, sum % 2); + carry = sum / 2; + } + if (carry != 0) sb.insert(0, carry); + return sb.toString(); + } +} + +/* +Thoughts: +Can't just convert to int because of Integer.MAX_VALUE limitation. +Convert to char, and add up all chars +*/ +class Solution { + public String addBinary(String a, String b) { + if (a == null || b == null) { + return a == null ? b : a; + } + int m = a.length(); + int n = b.length(); + int size = Math.max(m, n); + char[] result = new char[size]; + char[] longArray = m > n ? a.toCharArray() : b.toCharArray(); + char[] shortArray = m > n ? b.toCharArray() : a.toCharArray(); + int diff = longArray.length - shortArray.length; // important + int carry = 0; + for (int i = size - 1; i >= 0; i--) { + int sum = carry + (longArray[i] - '0'); + if (i - diff >= 0) { + sum += (shortArray[i - diff] - '0'); + } + carry = sum / 2; + result[i] = (char)(sum % 2 + '0'); + } + + if (carry != 0) { + return "1" + new String(result); + } + return new String(result); + } +} +/* + Thought: + Use binary property, add all and move carry-on + String to charArray +*/ + +public class Solution { + public String addBinary(String a, String b) { + if (a == null || b == null || a.length() == 0 || b.length() == 0) { + return null; + } + char[] shortArr = a.length() < b.length() ? a.toCharArray() : b.toCharArray(); + char[] longArr = a.length() < b.length() ? b.toCharArray() : a.toCharArray(); + int carry = 0; + int shortVal = 0; + int nextCarry = 0; + int diff = longArr.length - shortArr.length; + for (int i = longArr.length - 1; i >= 0; i--) { + shortVal = (i - diff) >= 0 ? shortArr[i - diff] - '0': 0; + nextCarry = (longArr[i] - '0' + shortVal + carry) / 2; + longArr[i] =(char)((longArr[i] - '0' + shortVal + carry) % 2 + '0'); + carry = nextCarry; + } + + if (carry != 0) { + return "1" + new String(longArr); + } + + return new String(longArr); + } +} + +/* //Thougths: 1. Turn string binary format into integer 2. add integer @@ -20,7 +120,6 @@ Given two binary strings, return their sum (also a binary string). Note: this just test if we know how to manipulate string/binary/Integer */ - public class Solution { /** * @param a a number @@ -39,3 +138,7 @@ public String addBinary(String a, String b) { return Integer.toBinaryString(sum); } } + + + +``` diff --git a/Java/Add Digits.java b/Java/Add Digits.java new file mode 100644 index 0000000..f38d6fd --- /dev/null +++ b/Java/Add Digits.java @@ -0,0 +1,48 @@ +E +1519709532 +tags: Math + +方法1: 普通做法就是按照题意, double-while loop把数字加起来. 第一层循环是O(n), 然后第二层循环就少很多数位, overall O(n) + +方法2: 找数学规律, 每过9个数字, 取mod就会开始重复, 所以给所有数字取mod 就可以间接找到答案. O(1) + +``` +/* +Given a non-negative integer num, repeatedly add all its digits until the result has only one digit. + +For example: + +Given num = 38, the process is like: 3 + 8 = 11, 1 + 1 = 2. Since 2 has only one digit, return it. + +Follow up: +Could you do it without any loop/recursion in O(1) runtime? +*/ + +/* +Thoughts: +Keep adding until result < 10 +Double-while loop: start with a num, calculate result, then num = result. +*/ +class Solution { + public int addDigits(int num) { + if (num < 10) { + return num; + } + while (num > 9) { + int temp = 0; + while (num != 0) { + temp += num % 10; + num = num / 10; + } + num = temp; + } + return num; + } +} + +class Solution { + public int addDigits(int num) { + return (num - 1) % 9 + 1; + } +} +``` \ No newline at end of file diff --git a/Java/Add Two Numbers II.java b/Java/Add Two Numbers II.java old mode 100644 new mode 100755 index 55d3fba..7d9a42e --- a/Java/Add Two Numbers II.java +++ b/Java/Add Two Numbers II.java @@ -1,9 +1,15 @@ -方向相反。巧用stack. +M +1519711895 +tags: Linked List + +Singly-linked list需要reverse, 用stack. +最终结果要恢复成input list 那样的sequence方向, 用stack一个个pop()刚好就可以做到. + +加法都一样: + 1. sum = carry + 2. carry = sum / 10 + 3. sum = sum % 10; -做加法都一样: -1. carrier -2. carrier = (rst + carrier) / 10 -3. rst = (rst + carrier) % 10 ``` /* You have two numbers represented by a linked list, where each node contains a single digit. @@ -19,6 +25,67 @@ Linked List High Precision */ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +/* +Thoughts: +Reverse the items in stack. +Add two stacks and save the result in stack as well. +Use top of the result stack as header of the result ListNode +Time, Space: O(n) +*/ +class Solution { + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + if (l1 == null || l2 == null) { + return l1 == null ? l2 : l1; + } + Stack s1 = new Stack(); + Stack s2 = new Stack(); + Stack result = new Stack(); + while (l1 != null) { + s1.push(l1); + l1 = l1.next; + } + while (l2 != null) { + s2.push(l2); + l2 = l2.next; + } + + // sum up + int carry = 0; + while (!s1.isEmpty() || !s2.isEmpty()) { + int sum = carry; + if (!s1.isEmpty()) { + sum += s1.pop().val; + } + if (!s2.isEmpty()) { + sum += s2.pop().val; + } + carry = sum / 10; + sum = sum % 10; + result.push(new ListNode(sum)); + } + if (carry != 0) { + result.push(new ListNode(carry)); + } + + // Convert to list + ListNode node = new ListNode(-1); + ListNode dummy = node; + while (!result.isEmpty()) { + node.next = result.pop(); + node = node.next; + } + return dummy.next; + } +} + /* Thoughts: Different from Add Two Numbers I, which is in reverse order. 6 1 7 diff --git a/Java/Add Two Numbers.java b/Java/Add Two Numbers.java new file mode 100755 index 0000000..6867a92 --- /dev/null +++ b/Java/Add Two Numbers.java @@ -0,0 +1,119 @@ +M +1519711343 +tags: Linked List, Math + +LinkedList都已经反转好了,直接做. +遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + +跟Add Binary的理解方式一模一样. + +注意: +Linked List 没有天然size. +用DummyNode(-1).next来hold住结果. + + +``` +/* +You have two numbers represented by a linked list, +where each node contains a single digit. +The digits are stored in reverse order, +such that the 1's digit is at the head of the list. +Write a function that adds the two numbers and returns the sum as a linked list. + +Example +Given 7->1->6 + 5->9->2. That is, 617 + 295. + +Return 2->1->9. That is 912. + +Given 3->1->5 and 5->9->2, return 8->0->8. + +Tags Expand +Cracking The Coding Interview Linked List High Precision +*/ + + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ + + /* +Thoughts: +The list has been reversed, just add them up. +Append one more ListNode if there is an carry. +*/ +class Solution { + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + if (l1 == null || l2 == null) { + return l1 == null ? l2 : l1; + } + int carry = 0; + ListNode node = new ListNode(-1); + ListNode head = node; + // Add l1 and l2 + while (l1 != null || l2 != null) { + int sum = carry; + if (l1 != null) { + sum += l1.val; + l1 = l1.next; + } + if (l2 != null) { + sum += l2.val; + l2 = l2.next; + } + carry = sum / 10; + sum = sum % 10; + node.next = new ListNode(sum); + node = node.next; + } + + // Finish adding carry + if (carry != 0) { + node.next = new ListNode(carry); + } + + return head.next; + } +} + +// Previous solution +public class Solution { + /** + * @param l1: the first list + * @param l2: the second list + * @return: the sum list of l1 and l2 + */ + public ListNode addLists(ListNode l1, ListNode l2) { + ListNode rst = new ListNode(0); + ListNode dummy = rst; + int carrier = 0; + //while + while (l1 != null || l2 != null) { + if (l1 != null) { + carrier += l1.val; + l1 = l1.next; + } + if (l2 != null) { + carrier += l2.val; + l2 = l2.next; + } + rst.next = new ListNode(carrier % 10); + carrier = carrier / 10; + rst = rst.next; + } + //check the carrier + if (carrier == 1) { + rst.next = new ListNode(1); + } + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Java/Add and Search Word - Data structure design.java b/Java/Add and Search Word - Data structure design.java new file mode 100755 index 0000000..321f72d --- /dev/null +++ b/Java/Add and Search Word - Data structure design.java @@ -0,0 +1,181 @@ +M +1520491896 +tags: Backtracking, Design, Trie + +Trie结构, prefix tree的变形: '.'可以代替任何字符,那么就要iterate这个node所有的children. + +节点里面有char, isEnd, HashMap +Build trie = Insert word:没node就加,有node就移动。 +Search word:没有node就报错. 到结尾return true + +这题因为'.'可以代替任何possible的字符,没一种都是一个新的path,所以recursive做比较好些。 +(iterative就要queue了,麻烦点) + +``` +/* +Design a data structure that supports the following two operations: addWord(word) and search(word) + +search(word) can search a literal word or a regular expression string containing only letters a-z or .. + +A . means it can represent any one letter. + +Example +addWord("bad") +addWord("dad") +addWord("mad") +search("pad") // return false +search("bad") // return true +search(".ad") // return true +search("b..") // return true +Note +You may assume that all words are consist of lowercase letters a-z. + +Tags Expand +Trie +*/ + +/* +Thougths: +Use Trie to store the letters and mark end letter with end = true +Trie structure: +class TrieNode { + HashMap map; + boolean isEnd; +} +During search, when facing '.', try all possible characters with the given TrieNode.map +*/ +class WordDictionary { + class TrieNode { + HashMap map; + boolean end; + TrieNode() { + this.map = new HashMap<>(); + this.end = false; + } + public HashMap getMap() { + return map; + } + public boolean isEnd() { + return this.end; + } + } + + TrieNode root; + /** Initialize your data structure here. */ + public WordDictionary() { + root = new TrieNode(); + } + + /** Adds a word into the data structure. */ + public void addWord(String word) { + char[] arr = word.toCharArray(); + TrieNode node = root; + for (char c : arr) { + HashMap map = node.getMap(); + if (!map.containsKey(c)) { + map.put(c, new TrieNode()); + } + node = map.get(c); + } + node.end = true; + } + + /** Returns if the word is in the data structure. A word could contain the dot character '.' to represent any one letter. */ + public boolean search(String word) { + return searchHelper(root, word, 0); + } + + public boolean searchHelper(TrieNode root, String word, int index) { + if (index == word.length()) { + return root.isEnd(); + } + TrieNode node = root; + char c = word.charAt(index); + HashMap map = node.getMap(); + if (map.containsKey(c)) { + return searchHelper(map.get(c), word, index + 1); + } else if (c == '.') { + for (Map.Entry entry : map.entrySet()) { + if (searchHelper(entry.getValue(), word, index + 1)) { + return true; + } + } + return false; + } + return false; + } +} + +/* +Build the WordDictionary like a TrieTree. +Note: the '.' indicates any letter, which means we'd have to traverse through all possible letters in current level. +Only one of the returning search results needs to be true + +Note: +TrieNode contains that char, boolean, and HashMap of children +*/ + +public class WordDictionary { + class TrieNode{ + HashMap children; + boolean isEnd; + + public TrieNode() { + this.children = new HashMap(); + this.isEnd = false; + } + } + + TrieNode root; + public WordDictionary(){ + this.root = new TrieNode(); + } + + // Adds a word into the data structure. + public void addWord(String word) { + TrieNode node = root; + for (int i =0; i < word.length(); i++) { + char c = word.charAt(i); + if (!node.children.containsKey(c)) { + node.children.put(c, new TrieNode()); + } + node = node.children.get(c); + } + node.isEnd = true; + } + + // Returns if the word is in the data structure. A word could + // contain the dot character '.' to represent any one letter. + public boolean search(String word) { + return searchHelper(root, word, 0); + } + + public boolean searchHelper(TrieNode root, String word, int index) { + if (index == word.length()) { + return root.isEnd; + } + TrieNode node = root; + char c = word.charAt(index); + //border conditon: + if (node.children.containsKey(c)) { + return searchHelper(node.children.get(c), word, index + 1); + } else if (c == '.'){ + for (Map.Entry entry : node.children.entrySet()) { + if (searchHelper(entry.getValue(), word, index + 1)) { + return true; + } + } + return false; + } else { + return false; + } + } +} + +// Your WordDictionary object will be instantiated and called as such: +// WordDictionary wordDictionary = new WordDictionary(); +// wordDictionary.addWord("word"); +// wordDictionary.search("pattern"); + + +``` \ No newline at end of file diff --git a/Java/Alien Dictionary.java b/Java/Alien Dictionary.java new file mode 100755 index 0000000..08fe8d9 --- /dev/null +++ b/Java/Alien Dictionary.java @@ -0,0 +1,228 @@ +H +1533444111 +tags: Graph, Topological Sort, DFS, BFS, Backtracking + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + +``` + +/* + +There is a new alien language which uses the latin alphabet. +However, the order among letters are unknown to you. +You receive a list of non-empty words from the dictionary, +where words are sorted lexicographically by the rules of this new language. +Derive the order of letters in this language. + +Example 1: +Given the following words in dictionary, + +[ + "wrt", + "wrf", + "er", + "ett", + "rftt" +] +The correct order is: "wertf". + +Example 2: +Given the following words in dictionary, + +[ + "z", + "x" +] +The correct order is: "zx". + +Example 3: +Given the following words in dictionary, + +[ + "z", + "x", + "z" +] +The order is invalid, so return "". + +Note: +You may assume all letters are in lowercase. +You may assume that if a is a prefix of b, then a must appear before b in the given dictionary. +If the order is invalid, return an empty string. +There may be multiple valid order of letters, return any one of them is fine. + +*/ + +Check: +https://leetcode.com/problems/alien-dictionary/discuss/70119/Java-AC-solution-using-BFS + +/* +Thoughts: +Topological sort with BFS. The tricky part is: how to construct the node-edge relationship? +For the same index of two strings: if the word1[index] != word2[index], +that means c1 is at the leading position than c2 in topological order. + +Use this feature to build the edges. + +1. Build graph +2. Calculate indegree +3. BFS +*/ +class Solution { + public String alienOrder(String[] words) { + if (words == null || words.length == 0) return ""; + + // Build graph && indegree map + Map> graph = new HashMap<>(); + Map inDegree = new HashMap<>(); + build(graph, inDegree, words); + + // Topological sort with BFS + return topoSort(graph, inDegree); + } + + private void build(Map> graph, Map inDegree, String[] words) { + // Init graph, inDegree map + for (String word : words) { + for (char c : word.toCharArray()) { + graph.putIfAbsent(c, new ArrayList<>()); + inDegree.putIfAbsent(c, 0); + } + } + // Build graph: find char diff between curr row and next row => build graph edge and increase inDegree relationship + // always compare same index on: words[i] -> words[i+1] + // if c1 != c2, build graph and inDegree map and break: later index does not have any more relationship. + for (int i = 0; i < words.length - 1; i++) { + int index = 0; + while (index < words[i].length() && index < words[i + 1].length()) { + char c1 = words[i].charAt(index); + char c2 = words[i + 1].charAt(index); + if (c1 != c2) { + graph.get(c1).add(c2); + inDegree.put(c2, inDegree.get(c2) + 1); + break; + } + index++; + } + } + } + + private String topoSort(Map> graph, Map inDegree) { + Queue queue = new LinkedList<>(); + for (char c : inDegree.keySet()) { // Build queue with item of inDegree==0: means no edge points towards it. + if (inDegree.get(c) == 0) queue.offer(c); + } + + StringBuffer sb = new StringBuffer(); + while (!queue.isEmpty()) { + char c = queue.poll(); + sb.append(c); + for (char edgeNode : graph.get(c)) { // reduce edge degrees count since node:graph.get(c) has been processed + inDegree.put(edgeNode, inDegree.get(edgeNode) - 1); + if (inDegree.get(edgeNode) == 0) queue.offer(edgeNode); + } + } + + if (sb.length() != graph.size()) return ""; + return sb.toString(); + } +} + + +/* +Thoughts: +DFS, mark visited. When dfs down to an leaf element, +it'll be the last element of the final output. (reverse order) +*/ +class Solution { + Map> graph = new HashMap<>(); + Map visited = new HashMap<>(); + StringBuffer sb = new StringBuffer(); + + public String alienOrder(String[] words) { + if (words == null || words.length == 0) { + return ""; + } + + // Build graph, and visited map. + buildGraph(words); + + // Topological sort with dfs + for (char c : graph.keySet()) { + if (!dfs(c)) { + return ""; + } + } + + return sb.toString(); + } + + private boolean dfs(Character c) { + if (visited.get(c) == 1) return true; + if (visited.get(c) == -1) return false; + + visited.put(c, -1); + for (char edgeNode : graph.get(c)) { + if (!dfs(edgeNode)) { + return false; + } + } + visited.put(c, 1); + sb.insert(0, c); // leaf element, add to buffer + return true; + } + + private void buildGraph (String[] words) { + // Create nodes + for (String word: words) { + for (char c : word.toCharArray()) { + if (!graph.containsKey(c)) { + graph.put(c, new ArrayList<>()); + visited.put(c, 0); + } + } + } + + // Build edges + for (int i = 0; i < words.length - 1; i++) { + int index = 0; + while (index < words[i].length() && index < words[i + 1].length()) { + char c1 = words[i].charAt(index); + char c2 = words[i + 1].charAt(index); + if (c1 != c2) { + graph.get(c1).add(c2); + break; + } + index++; + } + } + } +} + + + +``` diff --git a/Java/Anagrams.java b/Java/Anagrams.java old mode 100644 new mode 100755 index 2d1395c..b60c2cd --- a/Java/Anagrams.java +++ b/Java/Anagrams.java @@ -1,24 +1,37 @@ -hashtable 的做法。 -toCharArray -Arrays.sort -Stirng.valueOf(char[]) +M +1531455963 +tags: Array, Hash Table +把anagram找到并output +#### HashMap +- 存在int[26], Arrays.toString(arr) 就是 string key: character frequency map +- anagram都有一样的key, 存进hashmap +- output anagrams -http://www.jiuzhang.com/solutions/anagrams/ -做法不太一样 lOl -1. take each string, count the occurrance of the 26 letters. save in int[]count. -2. hash the int[] count and output a unique hash value. - hash = hash * a + num - a = a * b. -3. save to hashmap in the same way as we do. +#### HashMap + Sort +- HashMap 的做法. sort每个string, 存进HashMap, 重复的就是anagrams,最后输出。 +- toCharArray +- Arrays.sort +- Stirng.valueOf(char[]) +- 时间n*L*O(logL),L是最长string的长度。 + +#### Previous Notes +- Arrays.toString(arr)的做法。arr是int[26], assuming only have 26 lowercase letters. +- Count occurrance, 然后convert to String,作为map的key. +- Time complexity: nO(L) +- 另一种做法:http://www.jiuzhang.com/solutions/anagrams/ +- 1. take each string, count the occurrance of the 26 letters. save in int[]count. +- 2. hash the int[] count and output a unique hash value; hash = hash * a + num; a = a * b. +- 3. save to hashmap in the same way as we do. +- 这一步把for s: strs 里面的时间复杂度降到了O(L). L = s.length(). +- Need to work on the getHash() function. +- 时间变成n*O(L). Better. -这一步把for s: strs -里面的时间复杂度降到了O(L). L = s.length() -而普通的,for 里面的时间复杂度是 Llog(L) ``` /* +LintCode Given an array of strings, return all groups of strings that are anagrams. Example @@ -34,6 +47,63 @@ */ +public class Solution { + public List anagrams(String[] strs) { + List rst = new ArrayList<>(); + if (strs == null || strs == null) return rst; + Map> map = new HashMap<>(); + for (String word : strs){ + int[] arr = new int[26]; + for (char c : word.toCharArray()) arr[c - 'a']++; + String key = Arrays.toString(arr); + map.putIfAbsent(key, new ArrayList<>()); + map.get(key).add(word); + } + + for (List list : map.values()) { + if (list.size() >= 2) rst.addAll(list); + } + return rst; + } +} + +/* +//2.29.2016 + use int[26] assuming it's all lowercase letters + count each string char in a letter array int[], convert the array into string. + HashMap carray string as key, and actualy string as value + outupt all values +*/ +public class Solution { + public List anagrams(String[] strs) { + List rst = new ArrayList(); + if (strs == null || strs.length == 0) { + return rst; + } + HashMap> map = new HashMap>(); + + for (int i = 0; i < strs.length; i++) { + int[] arr = new int[26]; + for (int j = 0; j < strs[i].length(); j++) { + arr[strs[i].charAt(j) - 'a'] += 1; + } + String arrString = Arrays.toString(arr); + if (!map.containsKey(arrString)) { + map.put(arrString, new ArrayList()); + } + map.get(arrString).add(strs[i]); + } + + //Output + for (Map.Entry> entry : map.entrySet()) { + if (entry.getValue().size() >= 2) + rst.addAll(entry.getValue()); + } + + return rst; + } +} + /* diff --git a/Java/Array Partition I.java b/Java/Array Partition I.java new file mode 100644 index 0000000..582ac3a --- /dev/null +++ b/Java/Array Partition I.java @@ -0,0 +1,53 @@ +E +1517467099 +tags: Array + +给串数字, size=2n, 找pairs, 然后需要sum of min(pair) 最大. + +(a1, b1), (a2, b2), ..., (an, bn) which makes sum of min(ai, bi) for all i from 1 to n as large as possible. + +#### Sort, basics +- 从结果出发, 只需要找到加法的结果,而不强调具体配对. +- 写一写example就能做 +- 找到排列取单数位的规律,再考虑负数和正数的相同规律,即可找到排列求解的方法。 +- sort, O(nlogn) + + +``` +/* +Given an array of 2n integers, your task is to group these integers into n pairs of integer, say (a1, b1), (a2, b2), ..., (an, bn) which makes sum of min(ai, bi) for all i from 1 to n as large as possible. + +Example 1: +Input: [1,4,3,2] + +Output: 4 +Explanation: n is 2, and the maximum sum of pairs is 4 = min(1, 2) + min(3, 4). +Note: +n is a positive integer, which is in the range of [1, 10000]. +All the integers in the array will be in the range of [-10000, 10000]. +*/ + +/* +Thoughts: goal is to find the half of the numbers' sum, and always pick the min value of the pair. +Also, need to make the overall sum as large as possible: can't always choose the smallest numbers, but we can choose numbers at ascending order. +1. sort array. +2. only pick the even ones (starting from index 0) +Note: +1. use long to save result: never know what sum can occur in the process. +2. sort the array +O(nlogn) +*/ +class Solution { + public int arrayPairSum(int[] nums) { + if (nums == null || nums.length <= 1) { + return 0; + } + Arrays.sort(nums); + long result = 0; + for (int i = 0; i < nums.length; i++) { + result += i % 2 == 0 ? nums[i] : 0; + } + return (int)result; + } +} +``` \ No newline at end of file diff --git a/Java/Backpack II.java b/Java/Backpack II.java old mode 100644 new mode 100755 index c780c23..049d41c --- a/Java/Backpack II.java +++ b/Java/Backpack II.java @@ -1,16 +1,32 @@ -做了backpack I, 这个就如出一辙。 -想法还是,选了A[i-1] 或者没选A[i]. -一路往前跑不回头。就出来了。 +M +1524202756 +tags: DP, Backpack DP + +给i本书, 每本书有自己的重量 int[] A, 每本书有value int[] V + +背包有自己的大小M, 看最多能放多少value的书? + +#### Backpack DP +- 做了Backpack I, 这个就如出一辙, 只不过: dp存的不是max weight, 而是 value的最大值. +- 想法还是,选了A[i - 1] 或者没选A[i - 1]时候不同的value值. +- 时间空间O(mn) +- Rolling Array, 空间O(m) + +#### Previous DP Solution +- 如果无法达到的w, 应该mark as impossible. 一种简单做法是mark as -1 in dp. +- 如果有负数value, 就不能这样, 而是要开一个can[i][w]数组, 也就是backpack I 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 -O(m)的做法。想想,的确我们只care 最后一行,所以一个存value的就够了。 注意:和bakcpackI的 O(m)一样的,j是倒序的。如果没有更好的j,就不要更新。就是这个道理。 ``` /* -Given n items with size Ai and value Vi, and a backpack with size m. What's the maximum value can you put into the backpack? +Given n items with size Ai and value Vi, and a backpack with size m. +What's the maximum value can you put into the backpack? Example -Given 4 items with size [2, 3, 5, 7] and value [1, 5, 2, 4], and a backpack with size 10. The maximum value is 9. +Given 4 items with size [2, 3, 5, 7] and value [1, 5, 2, 4], and a backpack with size 10. +The maximum value is 9. Note You cannot divide item into small pieces and the total size of items you choose should smaller or equal to m. @@ -22,8 +38,200 @@ LintCode Copyright Dynamic Programming Backpack */ +/** +Thoughts: +dp[i][j]: 前i item, 放进weight/size = j 的袋子里的最大value. +constraint: weight +result: aggregate item value + */ +public class Solution { + public int backPackII(int m, int[] A, int V[]) { + if (A == null || V == null || A.length != V.length) { + return 0; + } + int n = A.length; + int[][] dp = new int[n + 1][m + 1]; + + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= m; j++) { + dp[i][j] = dp[i - 1][j]; + if (j - A[i - 1] >= 0) { + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + V[i - 1]); + } + + } + } + + return dp[n][m]; + } +} + +// Rolling array: +public class Solution { + public int backPackII(int m, int[] A, int V[]) { + if (A == null || V == null || A.length != V.length) { + return 0; + } + int n = A.length; + int[][] dp = new int[2][m + 1]; + + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= m; j++) { + dp[i % 2][j] = dp[(i - 1) % 2][j]; + if (j - A[i - 1] >= 0) { + dp[i % 2][j] = Math.max(dp[i % 2][j], dp[(i - 1) % 2][j - A[i - 1]] + V[i - 1]); + } + + } + } + + return dp[n % 2][m]; + } +} /* +Thoughts: +Dealing with value, the dp[i][w] = max value that can be formed over i tems at weight w. +Two conditions: +1. didn't pick A[i - 1]: dp[i - 1][w], value sum does not change. +2. Picked A[i - 1]: dp[i - 1][w - A[i - 1]] + V[i - 1]; +Find the max of the above two, and record. +Initialize with dp[0][0] = -1: not possible to form w, so mark as -1, impossible. +*/ + +public class Solution { + public int backPackII(int m, int[] A, int V[]) { + if (A == null || V == null || A.length != V.length) { + return 0; + } + int n = A.length; + int[][] dp = new int[n + 1][m + 1]; // [5][5] + + for (int j = 0; j <= m; j++) { + dp[0][j] = -1; // 0 items cannot form weight j, hence value -1, marking impossible + } + dp[0][0] = 0; // 0 items, 0 weight -> 0 value + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + dp[i][j] = dp[i - 1][j]; // 0 + if (j - A[i - 1] >= 0 && dp[i - 1][j - A[i - 1]] != -1) { + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + V[i - 1]); + } + + } + } + + int rst = 0; + for (int j = 0; j <= m; j++) { + if (dp[n][j] != -1) { + rst = Math.max(rst, dp[n][j]); + } + } + return rst; + } +} + +// Rolling array +public class Solution { + public int backPackII(int m, int[] A, int V[]) { + if (A == null || V == null || A.length != V.length) { + return 0; + } + int n = A.length; + int[][] dp = new int[2][m + 1]; + for (int j = 0; j <= m; j++) { + dp[0][j] = -1; // 0 items cannot form weight j, hence value -1, mark as impossible + } + dp[0][0] = 0; // 0 items, 0 weight -> 0 value + int curr = 0, prev; + + for (int i = 1; i <= n; i++) { + // rolling index + prev = curr; + curr = 1 - prev; + for (int j = 1; j <= m; j++) { + dp[curr][j] = dp[prev][j]; // 0 + if (j - A[i - 1] >= 0 && dp[prev][j - A[i - 1]] != -1) { + dp[curr][j] = Math.max(dp[curr][j], dp[prev][j - A[i - 1]] + V[i - 1]); + } + } + } + + int rst = 0; + for (int j = 0; j <= m; j++) { + if (dp[curr][j] != -1) { + rst = Math.max(rst, dp[curr][j]); + } + } + return rst; + } +} + + + + + +/* +Initialize with dp[0][0] = 0. +This will pass the test, however it's not 100% explicit +*/ +public class Solution { + public int backPackII(int m, int[] A, int V[]) { + if (A == null || V == null || A.length != V.length) { + return 0; + } + int n = A.length; + int[][] dp = new int[n + 1][m + 1]; // [5][5] + dp[0][0] = 0; // 0 items, 0 weight -> 0 value + for (int j = 0; j <= m; j++) { + dp[0][j] = 0; // 0 items cannot form weight j, hence value 0 + } + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + dp[i][j] = dp[i - 1][j]; // 0 + if (j - A[i - 1] >= 0) { + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + V[i - 1]); + } + } + } + return dp[n][m]; + } +} + +// Rolling array +public class Solution { + public int backPackII(int m, int[] A, int V[]) { + if (A == null || V == null || A.length != V.length) { + return 0; + } + int n = A.length; + int[][] dp = new int[2][m + 1]; // [5][5] + dp[0][0] = 0; // 0 items, 0 weight -> 0 value + for (int j = 0; j <= m; j++) { + dp[0][j] = 0; // 0 items cannot form weight j, hence value 0 + } + int curr = 0, prev; + + for (int i = 1; i <= n; i++) { + // rolling index + prev = curr; + curr = 1 - prev; + for (int j = 1; j <= m; j++) { + dp[curr][j] = dp[prev][j]; // 0 + if (j - A[i - 1] >= 0) { + dp[curr][j] = Math.max(dp[curr][j], dp[prev][j - A[i - 1]] + V[i - 1]); + } + } + } + return dp[curr][m]; + } +} + + +/* +Previous Notes. Thoughts: In Backpack I, we store true/false to indicate the largest j in last dp row. Here, we can store dp[i][j] == max value. @@ -79,11 +287,12 @@ public int backPackII(int m, int[] A, int V[]) { /* To use just O(m) sapce. - Just like in Backpack I, at the end, we only care about the last row. Why not just maintain a row, always keep the max value. + Just like in Backpack I, at the end, we only care about the last row. + Why not just maintain a row, always keep the max value. Note: Only update dp[j] if adding A[i-1] would be greater than current dp[j] - It's a bit hard to come up with this... but it's good exercise to build brain a bit. + It's a bit hard to come up with this... but it's good exercise. */ public class Solution { @@ -110,6 +319,4 @@ public int backPackII(int m, int[] A, int V[]) { - - ``` \ No newline at end of file diff --git a/Java/Backpack III.java b/Java/Backpack III.java new file mode 100644 index 0000000..4b62fa6 --- /dev/null +++ b/Java/Backpack III.java @@ -0,0 +1,227 @@ +H +1524723994 +tags: DP, Backpack DP + +给n种不同的物品, int[] A weight, int[] V value, 每种物品可以用无限次 + +问最大多少value可以装进size是 m 的包? + +#### DP +- 可以无限使用物品, 就失去了last i, last unique item的意义: 因为可以重复使用. +- 所以可以转换一个角度: +- 1. 用i **种** 物品, 拼出w, 并且满足题目条件(max value). 这里因为item i可以无限次使用, 所以考虑使用了多少次K. +- 2. K虽然可以无限, 但是也被 k*A[i]所限制: 最大不能超过背包大小. +- dp[i][w]: 前i种物品, fill weight w 的背包, 最大价值是多少. +- dp[i][w] = max {dp[i - 1][w - k*A[i-1]] + kV[i-1]}, k >= 0 +- Time O(nmk) +- 如果k = 0 或者 1, 其实就是 Backpack II: 拿或者不拿 + +#### 优化 +- 优化时间复杂度, 画图发现: +- 所计算的 (dp[i - 1][j - k*A[i - 1]] + k * V[i - 1]) +- 其实跟同一行的 dp[i][j-A[i-1]] 那个格子, 就多出了 V[i-1] +- 所以没必要每次都 loop over k times +- 简化: dp[i][j] 其中一个可能就是: dp[i][j - A[i - 1]] + V[i - 1] +- Time O(mn) + +#### 空间优化到1维数组 +- 根据上一个优化的情况, 画出 2 rows 网格 +- 发现 dp[i][j] 取决于: 1. dp[i - 1][j], 2. dp[i][j - A[i - 1]] +- 其中: dp[i - 1][j] 是上一轮 (i-1) 的结算结果, 一定是已经算好, ready to be used 的 +- 然而, 当我们 i++,j++ 之后, 在之前 row = i - 1, col < j的格子, 全部不需要. +- 降维简化: 只需要留着 weigth 这个 dimension, 而i这个dimension 可以省略: +- (i - 1) row 不过是需要用到之前算出的旧value: 每一轮, j = [0 ~ m], 那么dp[j]本身就有记录旧值的功能. +- 变成1个一位数组 +- 降维优化的重点: 看双行的左右计算方向 +- Time(mn). Space(m) + +``` +/* +Given n kind of items with size Ai and value Vi +(each item has an infinite number available) +and a backpack with size m. + +What's the maximum value can you put into the backpack? + +Notice +You cannot divide item into small pieces and the total size of items +you choose should smaller or equal to m. +*/ + + +/* +Thoughts: +dp[i][w]: first i types of items to fill weight w, find the max value. +1st loop: which type of item to pick from A +2nd loop: weight from 0 ~ m +3rd loop: # times when A[i] is used. + +Goal: dp[n][m] + +Condition1: didn't pick A[i - 1], dp[i][j] = dp[i - 1][j]; +Condition2: pickced A[i - 1], dp[i][j] = dp[i - 1][j - k * A[i - 1]] + k * V[i - 1]; + +O(nmk) +*/ +public class Solution { + public int backPackIII(int[] A, int[] V, int m) { + if (A == null || A.length == 0 || V == null || V.length == 0 || m <= 0) { + return 0; + } + int n = A.length; + int[][] dp = new int[n + 1][m + 1]; + dp[0][0] = 0; // 0 items to fill 0 weight, value = 0 + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + dp[i][j] = dp[i - 1][j]; + for (int k = 1; k * A[i - 1] <= j; k++) { + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * A[i - 1]] + k * V[i - 1]); + } + } + } + + return dp[n][m]; + } +} + +/** +Optimization1: +- 优化时间复杂度, 画图发现: +- 所计算的 (dp[i - 1][j - k*A[i - 1]] + k * V[i - 1]) +- 其实跟同一行的 dp[i][j-A[i-1]] 那个格子, 就多出了 V[i-1] +- 所以没必要每次都 loop over k times +- 简化: dp[i][j] 其中一个可能就是: dp[i][j - A[i - 1]] + V[i - 1] + +*/ +public class Solution { + public int backPackIII(int[] A, int[] V, int m) { + if (A == null || A.length == 0 || V == null || V.length == 0 || m <= 0) { + return 0; + } + int n = A.length; + int[][] dp = new int[n + 1][m + 1]; + dp[0][0] = 0; // 0 items to fill 0 weight, value = 0 + + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= m; j++) { + dp[i][j] = dp[i - 1][j]; + if (j >= A[i - 1]) { + dp[i][j] = Math.max(dp[i][j], dp[i][j - A[i - 1]] + V[i - 1]); + } + } + } + return dp[n][m]; + } +} + +/** +Optimization2: +- 根据上一个优化的情况, 画出 2 rows 网格 +- 发现 dp[i][j] 取决于: 1. dp[i - 1][j], 2. dp[i][j - A[i - 1]] +- 其中: dp[i - 1][j] 是上一轮 (i-1) 的结算结果, 一定是已经算好, ready to be used 的 +- 然而, 当我们 i++,j++ 之后, 在之前 row = i - 1, col < j的格子, 全部不需要. +- 降维简化: 只需要留着 weigth 这个 dimension, 而i这个dimension 可以省略: +- (i - 1) row 不过是需要用到之前算出的旧value: 每一轮, j = [0 ~ m], 那么dp[j]本身就有记录旧值的功能. +*/ +public class Solution { + public int backPackIII(int[] A, int[] V, int m) { + if (A == null || A.length == 0 || V == null || V.length == 0 || m <= 0) { + return 0; + } + int n = A.length; + int[] dp = new int[m + 1]; // DP on weight + dp[0] = 0; // 0 items to fill 0 weight, value = 0 + + for (int i = 1; i <= n; i++) { + for (int j = A[i - 1]; j <= m && j >= A[i - 1]; j++) { + dp[j] = Math.max(dp[j], dp[j - A[i - 1]] + V[i - 1]); + } + } + return dp[m]; + } +} + +/* +Thoughts: +Can pick any item for infinite times: there is no indicator of what's being picked last. +We don't know which item && how many times it was picked. +We should consider tracking: what type of items was picked how many times +(consider once done with 1 type of item, move on to others and never re-pick) +If A[i-1] was picked 0, 1, 2 ...., k times, then +dp[i][w] = max{dp[i - 1][j - k*A[i - 1]] + k*V[i - 1]}, where k >= 0 -> infinite + +Space: O(mn) +Time: O(m * m * n) = O(nm^2) +*/ +public class Solution { + public int backPackIII(int[] A, int[] V, int m) { + if (A == null || V == null || A.length != V.length) { + return 0; + } + int n = A.length; + int[][] dp = new int[n + 1][m + 1]; // max value with i items of weight w. + for (int j = 0; j <= m; j++) { + dp[0][j] = -1; // 0 items cannot form j weight, hence value = 0 + } + dp[0][0] = 0; + + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= m; j++) { // for all weight j at i items + for (int k = 0; k * A[i - 1] <= m; k++) { // use A[i-1] for k times + if (j - k * A[i - 1] >= 0 && dp[i - 1][j - k * A[i - 1]] != -1) { + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - k * A[i - 1]] + k * V[i - 1]); + } + } + } + } + int rst = 0; + for (int j = 0; j <= m; j++) { + rst = Math.max(rst, dp[n][j]); + } + return rst; + } +} + + + +// Optimization +// curve up +/* +dp[i][w] = max{dp[i - 1][j - k*A[i - 1]] + k*V[i - 1]}, where k >= 0 -> infinite +1. Every position, we are adding k*V[i - 1] +2. If we draw out how V[i-1] was being added alone with k = [0 ~ ...], we realize: + the next i is basically: max{...all k's possibilities} + V[i - 1] +So it reduces to: +dp[i][w] = max{dp[i - 1][w], dp[i][w - A[i-1]] + V[i-1]} + +*/ + +public class Solution { + public int backPackIII(int[] A, int[] V, int m) { + if (A == null || V == null || A.length != V.length) { + return 0; + } + int n = A.length; + int[][] dp = new int[n + 1][m + 1]; // max value with i items of weight w. + for (int j = 0; j <= m; j++) { + dp[0][j] = -1; // 0 items cannot form j weight, hence value = 0 + } + dp[0][0] = 0; + + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= m; j++) { // for all weight j at i items + dp[i][j] = dp[i - 1][j]; + if (j - A[i - 1] >= 0) { + dp[i][j] = Math.max(dp[i][j], dp[i][j - A[i - 1]] + V[i - 1]); + } + } + } + int rst = 0; + for (int j = 0; j <= m; j++) { + rst = Math.max(rst, dp[n][j]); + } + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/Backpack V.java b/Java/Backpack V.java new file mode 100644 index 0000000..45f5b99 --- /dev/null +++ b/Java/Backpack V.java @@ -0,0 +1,152 @@ +M +1524205822 +tags: DP, Backpack DP + +#### Backpack DP +- 与背包1不同: 这里不是check可能性(OR)或者最多能装的size是多少; 而是计算有多少种正好fill的可能性. +- dp[i][w]: 用前i本书, 正好fill到 w weight的可能性. +- 对于末尾, 还是两种情况: +- 1. i-1位置没有加bag +- 2. i-1位置加了bag +- 两种情况可以fill满w的情况加起来, 就是我们要的结果. +- 如常: dp[n + 1][w + 1] +- 重点: dp[0][0] 表示0本书装满weight=0的包, 这里我们必须 dp[0][0] = 1, 给后面的 dp function 做base +- Space, time: O(MN) +- Rolling array, 空间优化, 滚动数组. Space: O(M) + +#### 降维打击, 终极优化 +- 分析row(i-1)的规律, 发现所有row(i)的值, 都跟row(i-1)的左边element相关, 而右边element是没用的. +- 所以可以被override. +- Space: O(M), 真*一维啊! +- Time: O(MN) + +``` +/* +Given n items with size nums[i] which an integer array and all positive numbers. +An integer target denotes the size of a backpack. +Find the number of possible ways of filling the backpack. + +Each item may only be used once +*/ + + +/* +Thoughts: +We want to know with i items, how many ways can we add up to equal target? +dp[i][w]: the # of ways to add up to w with i items. +track back to i - 1: +1. nums[i] was not picked : dp[i][w] = dp[i - 1][w] +2. nums[i] was picked: dp[i][w] = dp[i - 1][w - nums[i]]; + +initialization: +dp[0][0~w] = 0; +dp[0][0] = 1; + +Space: O(MN) +Time: O(MN) +*/ +public class Solution { + public int backPackV(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[][] dp = new int[n + 1][target + 1]; + dp[0][0] = 1; // 0 items to form 0 weight + for (int j = 1; j <= target; j++) { + dp[0][j] = 0; + } + + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= target; j++) {// (int j = target; j >= 0; j--) works as well, it doesn't matter + // Condition1: + dp[i][j] = dp[i - 1][j]; + // Condition2: + if (j - nums[i - 1] >= 0) { + dp[i][j] += dp[i - 1][j - nums[i - 1]]; + } + } + } + return dp[n][target]; + } +} + + +// Improvement1: rolling array +// Space O(M) +public class Solution { + public int backPackV(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[][] dp = new int[2][target + 1]; + dp[0][0] = 1; // 0 items to form 0 weight + for (int j = 1; j <= target; j++) { + dp[0][j] = 0; + } + int curr = 0, prev = 1; + for (int i = 1; i <= n; i++) { + prev = curr; + curr = 1 - curr; + for (int j = 0; j <= target; j++) {// (int j = target; j >= 0; j--) works as well, it doesn't matter + // Condition1: + dp[curr][j] = dp[prev][j]; + // Condition2: + if (j - nums[i - 1] >= 0) { + dp[curr][j] += dp[prev][j - nums[i - 1]]; + } + } + } + return dp[curr][target]; + } +} + +/* +Improvement 降维 +Inner loop of weight needs to iterate from j = M -> 0 +We always use dp[i-1][w] or dp[i - 1][w - nums[i - 1]]: +always using old values from last row right above at w index, or on the left side of the w index. + +All indexes on right side of w is not needed. + +Therefore, we can reduce the dp[][] into 1-D array. +Note: j has to iterate from M -> 0, because we know on i - 1 row all indexes +on right side of w on the right side can be overriden. + +if j = 0 -> M, it will override useful indexes. + +Space: O(n) +Time: O(MN) +*/ + +public class Solution { + public int backPackV(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] dp = new int [target + 1]; + dp[0] = 1; // 0 items to form 0 weight + for (int j = 1; j <= target; j++) { + dp[j] = 0; + } + + for (int i = 1; i <= n; i++) { + for (int j = target; j >= 0; j--) {// have to loop from M -> 0, for optimization + // Condition1: dp[j] = dp[j]; + // Condition2: + if (j - nums[i - 1] >= 0) { + dp[j] += dp[j - nums[i - 1]]; + } + } + // further simplify + //for (int j = target; j >= nums[i - 1]; j--) { + // dp[j] += dp[j - nums[i - 1]]; + //} + } + return dp[target]; + } +} + +``` \ No newline at end of file diff --git a/Java/Backpack VI.java b/Java/Backpack VI.java new file mode 100644 index 0000000..237a233 --- /dev/null +++ b/Java/Backpack VI.java @@ -0,0 +1,64 @@ +M +1518420788 +tags: DP, Backpack DP + +给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法. + +nums 里的数字, 可以重复使用. 不同的order可以算作不同的拼法. + +#### Backpack DP +- dp[i] 表示: # of ways to fill weight i +- 1维: dp[w]: fill weigth w 有多少种方法. 前面有多少种可能性, 就sum多少个: +- dp[w] = sum{dp[w - nums[i]]}, i = 0~n + +##### 分析 +- 拼背包时, 可以有重复item, 所以考虑'最后被放入的哪个unique item' 就没有意义了. +- 背包问题, 永远和weight分不开关系. +- 这里很像coin chagne: 考虑最后被放入的东西的value/weigth, 而不考虑是哪个. + + + + +``` +/* +Given an integer array nums with all positive numbers and no duplicates, +find the number of possible combinations that add up to a positive integer target. + +Notice +A number in the array can be used multiple times in the combination. +Different orders are counted as different combinations. +*/ + +/* +Thoughts: +All backpack problems should have a status of weight. However, now the items can be reused, so there is no +unique last item. We can't do [w - nums[i]]. +Instead of banking on last unique item, we should consider what's the last weight being added? +Very similar to Coin Change. + +dp[w] = how many combinations to form weight w? +Cound all ways: +dp[w] = dp[w - nums[0]] + dp[w - nums[1]] + dp[w - nums[2]] + .... dp[w - nums[ n - 1]] +*/ +public class Solution { + public int backPackVI(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return 0; + } + + int n = nums.length; + int[] dp = new int[target + 1]; + dp[0] = 1; // fill 0 weight, always possible. + + for (int i = 1; i <= target; i++) { // calc for all weights + for (int j = 0; j < n; j++) { // iterate over all nums + if (i - nums[j] >= 0) { + dp[i] += dp[i - nums[j]]; + } + } + } + + return dp[target]; + } +} +``` \ No newline at end of file diff --git a/Java/Backpack.java b/Java/Backpack.java old mode 100644 new mode 100755 index 713728e..fac5121 --- a/Java/Backpack.java +++ b/Java/Backpack.java @@ -1,29 +1,50 @@ -记得这个DP。 -row是item大小: 0, A[0], A[1] ... A[A.length -1] -col是背包累积的size: 0, 1, 2, ... m. +M +1524200994 +tags: DP, Backpack DP + +给i本书, 每本书有自己的重量 int[] A, 背包有自己的大小M, 看最多能放多少重量的书? + +#### Backpack DP 1 +- 简单直白的思考 dp[i][m]: 前i本书, 背包大小为M的时候, 最多能装多种的书? +- **注意**: 背包问题, 重量weight一定要是一维. +- dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + A[i - 1]); +- 每一步都track 最大值 +- 最后return dp[n][m] +- 时间空间 O(mn) +- Rolling array, 空间O(m) + +#### Backpack DP 2 +- true/false求解, 稍微曲线救国: 重点是, 最后, 按照weight从大到小遍历, 第一个遇到true的, index就是最大值. +- 考虑: 用i个item (可跳过地取), 是否能装到weight w? +- 需要从'可能性'的角度考虑, 不要搞成单一的最大值问题. +- 1. 背包可装的物品大小和总承重有关. +- 2. 不要去找dp[i]前i个物品的最大总重, 找的不是这个. + dp[i]及时找到可放的最大sum, 但是i+1可能有更好的值, 把dp[i+1]变得更大更合适. + +##### 做法 +- boolean[][] dp[i][j]表示: 有前i个item, 用他们可否组成size为j的背包? true/false. +- (反过来考虑了,不是想是否超过size j, 而是考虑是否能拼出exact size == j) +- **注意**: 虽然dp里面一直存在i的位置, 实际上考虑的是在i位置的时候, 看前i-1个item. + +##### 多项式规律 +- 1. picked A[i-1]: 就是A[i-1]被用过, weight j 应该减去A[i-1]. 那么dp[i][j]就取决于dp[i-1][j-A[i-1]]的结果. +- 2. did not pick A[i-1]: 那就是说, 没用过A[i-1], 那么dp[i][j]就取决于上一行d[i-1][j] +- dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]] + +##### 结尾 +- 跑一遍dp 最下面一个row. 从末尾开始找, 最末尾的一个j (能让dp[i][j] == true)的, 就是最多能装的大小 :) +- 时间,空间都是:O(mn) -想法是这样: -dp[i][j]有这么i-1个item, 用他们可否组成size为j的背包?true/false. (反过来考虑了,不是想是否超过size j, 而是考虑是否能拼出exact size == j)。 -注意注意:虽然dp里面一直存在i的位置,实际上考虑的是在i位置的时候,看前i-1个item. -看一遍code,会发现: - 1. picked A[i-1] 如果上一个item, A[i-1]被加了上来, 用j-A[i-1]看看,是否这再前一步也true. true就好啦! - 2. did not pick A[i-1]. 那就是说,不加上A[i-1], 上一行d[i-1][j]还是需要是true。 -最后: -跑一边dp 最下面一个row. 从末尾开始找,最末尾的一个j (能让dp[i][j] == true)的,就是最多能装的大小 :) - - - - -再有: -O(m)的做法,注意j是倒序的啊! ``` /* -Given n items with size Ai, an integer m denotes the size of a backpack. +Given n items with size A[i], an integer m denotes the size of a backpack. How full you can fill this backpack? Example -If we have 4 items with size [2, 3, 5, 7], the backpack size is 11, we can select [2, 3, 5], so that the max size we can fill this backpack is 10. If the backpack size is 12. we can select [2, 3, 7] so that we can fulfill the backpack. +If we have 4 items with size [2, 3, 5, 7], the backpack size is 11, we can select [2, 3, 5], +so that the max size we can fill this backpack is 10. +If the backpack size is 12. we can select [2, 3, 7] so that we can fulfill the backpack. You function should return the max size we can fill in the given backpack. @@ -41,12 +62,176 @@ */ +/* +Thoughts: +Backpack problem, always consider a dimension with weight/size. +int dp[i][j]: with i items and backpack size/weight-limit of j, what's max weight can we put in? + +dp[i][j] depends on last item's size && what's the state with [i-1] items. + +dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + A[i - 1]); + +dp[0][0] = 0; no book, 0 weight limit +dp[0][j] = 0; no book, can't fill bag +dp[i][0] = 0; i books, but weight-limit = 0, can't fill +*/ +public class Solution { + public int backPack(int m, int[] A) { + if (A == null || A.length < 0) { + return 0; + } + int n = A.length; + int[][] dp = new int[n + 1][m + 1]; + + // Calculcate possibility for i items to fill up w weight + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= m; j++) { + // default: item(i-1) not used: + dp[i][j] = dp[i - 1][j]; + if (j - A[i - 1] >= 0) { // possible to use item(i-1) + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + A[i - 1]); // use item(i-1) + } + } + } + + return dp[n][m]; + } +} + +// Rolling array +public class Solution { + public int backPack(int m, int[] A) { + if (A == null || A.length < 0) { + return 0; + } + int n = A.length; + int[][] dp = new int[2][m + 1]; + + // Calculcate possibility for i items to fill up w weight + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= m; j++) { + // default: item(i-1) not used: + dp[i % 2][j] = dp[(i - 1) % 2][j]; + if (j - A[i - 1] >= 0) { // possible to use item(i-1) + dp[i % 2][j] = Math.max(dp[i % 2][j], dp[(i - 1) % 2][j - A[i - 1]] + A[i - 1]); // use item(i-1) + } + } + } + return dp[n % 2][m]; + } +} + +/* +Thoughts: +DO NOT try to find the maxSum using given values: this approach f[i] only returns max sum, +but does not guarantee to pick the most sutible items that maximize f[n]. + +1. Always consider weight +2. Consider it as a possibility problem based on weight 0 ~ m + +Using given items, can we fill backpack with size 0, size 1, size 2... size m - 1, and size m? +We save these values with boolean dp[i][w]: using i items to fill size w, where i is the A index. + +Two conditions for calculating dp[i][w] +1. i - 1 items filled up w, so dp[i][w] = dp[i - 1][w]. No need to add A[i - 1]. +2. i - 1 items fileld up w - A[i - 1]; once adding A[i - 1], it'll fill up w. dp[i][w] = dp[i - 1][w - A[i - 1]], so we are counting on if i - 1 items filled up weight: w - A[i - 1]. + +We'll loop over j = 1 ~ m, and attempt to fill all sizes with i items. + +init: +dp[0][0]: using 0 items to fill 0 space, sure. True. +dp[0][1~m]: using 0 items to fill 1~m space, not gonna work. False. + + +*/ +public class Solution { + + public int backPack(int m, int[] A) { + if (A == null || A.length < 0) { + return 0; + } + int n = A.length; + boolean[][] dp = new boolean[n + 1][m + 1]; + + // weight 0 is a valid value. + // items does not have 0's item, so we need to init dp based for all entries where i == 0 + dp[0][0] = true; + for (int j = 1; j <= m; j++) { + dp[0][j] = false; + } + + // Calculcate possibility for i items to fill up w weight + for (int i = 1; i <= n; i++) { + for (int j = 0; j <= m; j++) { + // default: item(i-1) not used: + dp[i][j] = dp[i - 1][j]; + if (j - A[i - 1] >= 0) { // possible to use item(i-1) + dp[i][j] |= dp[i - 1][j - A[i - 1]]; // use item(i-1) + } + } + } + + // Find max weight size that makes dp[i][j] true + for (int j = m; j >= 0; j--) { + if (dp[n][j]) { + return j; + } + } + return 0; + } +} + +/* +Thoughts: rolling array. +Always use i, and i - 1 dp. can replace the two index with +curr, pre +*/ +public class Solution { + + public int backPack(int m, int[] A) { + if (A == null || A.length < 0) { + return 0; + } + int n = A.length; + boolean[][] dp = new boolean[2][m + 1]; + int curr = 0; + int pre = 1; + // weight 0 is a valid value. + // items does not have 0's item, so we need to init dp based for all entries where i == 0 + dp[curr][0] = true; + for (int j = 1; j <= m; j++) { + dp[curr][j] = false; + } + + // Calculcate possibility for i items to fill up w weight + for (int i = 1; i <= n; i++) { + curr = pre; + pre = 1 - curr; + for (int j = 0; j <= m; j++) { + // default: item(i-1) not used: + dp[curr][j] = dp[pre][j]; + if (j - A[i - 1] >= 0) { // possible to use item(i-1) + dp[curr][j] |= dp[pre][j - A[i - 1]]; // use item(i-1) + } + } + } + + // Find max weight size that makes dp[i][j] true + for (int j = m; j >= 0; j--) { + if (dp[curr][j]) { + return j; + } + } + return 0; + } +} + /* Thoughts: Recap on 12.02.2015 State DP[i][j]: i is the index of Ai, and j is the size from (0 ~ m). - It means: depending if we added A[i-1], can we add up to j-space?Return ture/false. - Note: that is, even j == 0, and I have a item with size == 2, I can still choose not to add, which means the backpack can reach j ==0. True. + It means: depending if we added A[i-1], can we full-fill j-space? Return ture/false. + Note: that is, even j == 0, and I have a item with size == 0. There is nothing to add, which means the backpack can reach j == 0. True. However, if we have a item with size == 2, but I need to fill space == 1. I will either pick/not pick item of size 2; either way, can't fill a backpack with size 1. False; Function: @@ -68,16 +253,16 @@ public int backPack(int m, int[] A) { for (int i = 1; i <= A.length; i++) { for (int j = 0; j <= m; j++) { - //added A[i - 1] + //j is large enough: if (j - A[i - 1] >= 0) { + //not added A[i - 1] or added A[i - 1] dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]]; - } else { - //not added A[i - 1]; + } else {// j not large enough, ofcourse not adding A[i-1] dp[i][j] = dp[i - 1][j]; } } } - + //Largest possible size with dp[j] == true for (int j = m; j >= 0; j--) { if (dp[A.length][j]) { return j; @@ -89,53 +274,6 @@ public int backPack(int m, int[] A) { -/* -Thoughts: -Well, I kind of forgot about how we did this in algorithm class, but here we go, after a bit research: -DP[i][j] means: if i number of items in A, can we fill the bag size of j? Then, save a boolean in DP[i][j]. That means, if i items are too much for j? - -So, there are two cases: -1. A[i] is unfortunately too big to fit into size j, so skip item A[i] and use DP[i-1][j]. -2. OR, the other case: A[i] fits in well. We realize 2 things: - a. first (i-1)th items much fit in well into the bag size of (j - A[i - 1]): DP[i-1][j - A[i-1]]. Basically says items must be fit in (true) before adding A[i] - b. AND (j - A[i - 1]) must >= 0 to have space for next item i. - -End: -Iterate through j:(m ~ 0), and return j, if DP[A.length][j] is true. We are starting from m, because we need the largest number j. - -This is 2D array version: memory mxn, space mxn - -*/ - -public class Solution { - /** - * @param m: An integer m denotes the size of a backpack - * @param A: Given n items with size A[i] - * @return: The maximum size - */ - public int backPack(int m, int[] A) { - if (A == null || m == 0) { - return 0; - } - - boolean[][] DP = new boolean[A.length + 1][m + 1]; - DP[0][0] = true; - for (int i = 1; i <= A.length; i++) { - for (int j = 0; j <= m; j++) { - DP[i][j] = DP[i - 1][j] || (j - A[i - 1] >= 0 && DP[i - 1][j - A[i - 1]]); - } - } - - for (int j = m; j >= 0; j--) { - if (DP[A.length][j]) { - return j; - } - } - return 0; - } -} - - /* 1D array: memory mxn, space m. Tricky tho ... diff --git a/Java/Backspace String Compare.java b/Java/Backspace String Compare.java new file mode 100644 index 0000000..48428ed --- /dev/null +++ b/Java/Backspace String Compare.java @@ -0,0 +1,69 @@ +E +1533516441 +tags: Two Pointers, Stack + +``` +/* +Given two strings S and T, return if they are equal when both are typed into empty text editors. +# means a backspace character. + +Example 1: + +Input: S = "ab#c", T = "ad#c" +Output: true +Explanation: Both S and T become "ac". +Example 2: + +Input: S = "ab##", T = "c#d#" +Output: true +Explanation: Both S and T become "". +Example 3: + +Input: S = "a##c", T = "#a#c" +Output: true +Explanation: Both S and T become "c". +Example 4: + +Input: S = "a#c", T = "b" +Output: false +Explanation: S becomes "c" while T becomes "b". +Note: + +1 <= S.length <= 200 +1 <= T.length <= 200 +S and T only contain lowercase letters and '#' characters. +Follow up: + +Can you solve it in O(N) time and O(1) space? + +*/ + +/* +Stack to take elements, and pop when # if reached. +compare the two stacks at the end. +time/space: O(n) +*/ +class Solution { + public boolean backspaceCompare(String S, String T) { + if (S == null || T == null) return false; + + return type(S).equals(type(T)); + } + + private String type(String s) { + Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + if (c == '#') { + if (!stack.isEmpty()) stack.pop(); + } else { + stack.push(c); + } + } + StringBuffer sb = new StringBuffer(); + while (!stack.isEmpty()) { + sb.append(stack.pop()); + } + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/Balanced Binary Tree.java b/Java/Balanced Binary Tree.java old mode 100644 new mode 100755 index 076d297..d036d7b --- a/Java/Balanced Binary Tree.java +++ b/Java/Balanced Binary Tree.java @@ -1,17 +1,25 @@ -1. DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. - 一旦有-1, 就全部返回。 - 最后比较返回结果是不是-1. 是-1,那就false +M +1519713672 +tags: Tree, DFS -2. 从基本的题目理解考虑,想到leaf node的情况。如果判断了leaf node, 那其他node应该就是可以recursive。 - 直接在isBalanced上面recursive. - 然后这个可能是个小小的优化,因为不需要计算所有的depth.一旦发现一个false,其他的就不需要计算,直接返回了。 +给一个binary tree, 看是否是height-balanced + +#### DFS +- DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. +- 一旦有 <0 或者差值大于1, 就全部返回Integer.MIN_VALUE. Integer.MIN_VALUE比较极端, 确保结果的正确性。 +- 最后比较返回结果是不是<0. 是<0,那就false. +- Traverse 整个tree, O(n) + + +#### DFS, maxDepth function +- Same concept as in 1, but cost more traversal efforts. ``` /* -46% Accepted Given a binary tree, determine if it is height-balanced. -For this problem, a height-balanced binary tree is defined as a binary tree in which the depth of the two subtrees of every node never differ by more than 1. +For this problem, a height-balanced binary tree is defined as a binary tree, +in which the depth of the two subtrees of every node never differ by more than 1. Example Given binary tree A={3,9,20,#,#,15,7}, B={3,#,20,15,7} @@ -24,66 +32,47 @@ The binary tree A is a height-balanced binary tree, but B is not. Tags Expand -Tree Depth First Search +Binary Search Divide and Conquer Recursion */ /** - * Definition of TreeNode: + * Definition for a binary tree node. * public class TreeNode { - * public int val; - * public TreeNode left, right; - * public TreeNode(int val) { - * this.val = val; - * this.left = this.right = null; - * } + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } * } */ - /* - 12.11.2015 - Recap: - The original way of marking depth and -1 is good. However, that has to traverse entire tree. - - Today, let's think about the leaf case, and see if we can directly recurse on isBalanced itself. - leaf case: - root == null, return true; - left = root.left; right = root.right; - left == null && right == null : true; - - left == null && right != null && (right.left != null || right.right != null) { - false; - } - - need to isBalance(left) && isBalance(right). +DFS: find each subtree's depth, and compare the two. +However, making DFS for every node is very costly: the recursive calculations of depth are done repeatedly, so we want to at least tell, if a path has failed, no need to dive deep -> need a boolean-ish signature. +However, we can't return both boolean && depth (we actually don't need other depth valuese greater than 1). +Combine the boolean && depth signature to mark the failed case: by using a negative number. */ - -public class Solution { - /** - * @param root: The root of binary tree. - * @return: True if this Binary tree is Balanced, or false. - */ +class Solution { public boolean isBalanced(TreeNode root) { - if (root == null || (root.left == null && root.right == null)) { + if (root == null) { return true; } - - TreeNode left = root.left; - TreeNode right = root.right; - //One of left or right is null. - if (left == null && (right.left != null || right.right != null)) { - return false; - } - if (right == null && (left.left != null || left.right != null)) { - return false; + return markDepth(root) > 0; + } + + private int markDepth(TreeNode node) { + if (node == null) { + return 0; } - //none of left or right is null - return isBalanced(left) && isBalanced(right); + int leftDepth = markDepth(node.left); + int rightDepth = markDepth(node.right); + if (leftDepth < 0 || rightDepth < 0 || (Math.abs(leftDepth - rightDepth)) > 1) { + return Integer.MIN_VALUE; + } + return Math.max(leftDepth, rightDepth) + 1; } } - /* Thinking process: @@ -94,31 +83,90 @@ same process as maxDepth() method. at the top return, check if -1. */ -public class Solution { - /** - * @param root: The root of binary tree. - * @return: True if this Binary tree is Balanced, or false. - */ +/* 3.3.2016 recap: + Recursive 1: + Use helper to calculate depth, and also check if left/right depth differ by 1. If all good, return actual depth +*/ + + public class Solution { public boolean isBalanced(TreeNode root) { - return maxDepth(root) != -1; + if (root == null) { + return true; + } + return helper(root) > 0; } - public int maxDepth(TreeNode root) { - if (root == null) { + public int helper(TreeNode node) { + if (node == null) { return 0; } + int leftDepth = helper(node.left); + int rightDepth = helper(node.right); - int left = maxDepth(root.left); - int right = maxDepth(root.right); + if (leftDepth < 0 || rightDepth < 0 || Math.abs(leftDepth - rightDepth) > 1) { + return Integer.MIN_VALUE; + } - if (Math.abs(left - right) > 1 || left == -1 || right == -1) { - return -1; + return Math.max(leftDepth, rightDepth) + 1; + } +} + + +/* + Recursive 2: + Calculate a node's maxDepth. Compare a parent node's sub tree for maxDepth +*/ +public class Solution { + public boolean isBalanced(TreeNode root) { + if (root == null) { + return true; } + int leftDepth = maxDepth(root.left); + int rightDepth = maxDepth(root.right); - return Math.max(left, right) + 1; + if (Math.abs(leftDepth - rightDepth) > 1) { + return false; + } + return isBalanced(root.left) && isBalanced(root.right); + } + + public int maxDepth(TreeNode node) { + if (node == null) { + return 0; + } + return Math.max(maxDepth(node.left), maxDepth(node.right)) + 1; } } + +/* + Failed Solution: + check cases: + root == null, return true; + left = root.left; right = root.right; + left == null && right == null : true; + left == null && right != null && (right.left != null || right.right != null) { + false; + } + return isBalance(left) && isBalance(right). + + failed case:[1,2,2,3,3,null,null,4,4] + 1 + 2 2 + 3 3 + 4 4 +Previous notes: +2. 从基本的题目理解考虑,想到leaf node的情况。如果判断了leaf node, 那其他node应该就是可以recursive。 + 直接在isBalanced上面recursive. + 关键return false的判断情况:如果有个node是null, 那么同一行相邻的那个,一旦有了children,那么就说明两个分支的depth已经是>=2了,那么就return false. + + 然后这个可能是个小小的优化,因为不需要计算所有的depth.一旦发现一个false,其他的就不需要计算,直接返回了。 + + + +*/ + + ``` \ No newline at end of file diff --git a/Java/Basic Calculator.java b/Java/Basic Calculator.java new file mode 100644 index 0000000..b0151b8 --- /dev/null +++ b/Java/Basic Calculator.java @@ -0,0 +1,154 @@ +H +1526882596 +tags: Stack, Math, Expression Tree, Binary Tree, Minimum Binary Tree + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + +``` +/* +Implement a basic calculator to evaluate a simple expression string. + +The expression string may contain open ( and closing parentheses ), +the plus + or minus sign -, non-negative integers and empty spaces . + +You may assume that the given expression is always valid. + +Some examples: +"1 + 1" = 2 +" 2-1 + 2 " = 3 +"(1+(4+5+2)-3)+(6+8)" = 23 +Note: Do not use the eval built-in library function. + +*/ + +/* +build expression tree to evaluate expression +two functions: +1. build tree + parse string + skip space + identify operator + calculate weight of operator + add parentheses to base weight +2. evaluate with post-order traversal +*/ +class Solution { + class TreeNode { + int weight; + String str; + TreeNode left, right; + public TreeNode(int weight, String str) { + this.weight = weight; + this.str = str; + } + } + + public int calculate(String s) { + if (s == null || s.length() == 0) return 0; + TreeNode root = buildTree(s); // build expression tree + return (int)evaluate(root); // post-order traversal of the tree + } + + // build tree based on input string, min-tree. return root + private TreeNode buildTree(String s) { + int n = s.length(); + char[] chars = s.trim().toCharArray(); + Stack stack = new Stack<>(); + int base = 0; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < n; i++) { + char c = chars[i]; + if (c == ' ') { + continue; + } else if (c == '(') { // '()' are used to add weight, not stored in tree + base = getWeight(base, c); + continue; + } else if (c == ')') { + base = getWeight(base, c); + continue; + } else if (i < n - 1 && isDigit(chars[i]) && isDigit(chars[i + 1])) { // continue to get remaining of the int + sb.append(c); + continue; + } + String str; + if (isDigit(c)) { + sb.append(c); + str = sb.toString(); + sb.setLength(0); // clean up + } else { + str = c + ""; + } + + // use monotonuous stack to build min-tree + TreeNode node = new TreeNode(getWeight(base, c), str); + while (!stack.isEmpty() && node.weight <= stack.peek().weight) { + node.left = stack.pop(); + } + if (!stack.isEmpty()) { + stack.peek().right = node; + } + stack.push(node); + } + TreeNode root = null; + while (!stack.isEmpty()) { + root = stack.pop(); + } + + return root; // it's the root of tree, always a operator + } + + // post-order traversal to evaluate the expression + private long evaluate(TreeNode root) { + if (root == null) return 0; + long left = evaluate(root.left); + long right = evaluate(root.right); + long result = 0; + switch(root.str) { + case "+": + result = left + right; + break; + case "-": + result = left - right; + break; + case "*": + result = left * right; + break; + case "/": + result = left / right; + break; + default: + result = Long.parseLong(root.str); + } + return result; + } + + // get weight of the character. integer weights the most and will be leaf. + // Remember to store the result using long + private int getWeight(int base, char c) { + if (c == '(') return base + 10; + if (c == ')') return base - 10; + if (c == '+' || c == '-') return base + 1; + if (c == '*' || c == '/') return base + 2; + return Integer.MAX_VALUE; + } + + private boolean isDigit(char c) { + return c >= '0' && c <= '9'; + } +} + +``` diff --git a/Java/Best Time to Buy and Sell Stock II.java b/Java/Best Time to Buy and Sell Stock II.java old mode 100644 new mode 100755 index 90196d9..f8c6bb5 --- a/Java/Best Time to Buy and Sell Stock II.java +++ b/Java/Best Time to Buy and Sell Stock II.java @@ -1,16 +1,131 @@ -找涨幅最大的区间,买卖。 -飞似得涨,到峰顶,就卖。 +E +1531717353 +tags: Array, Greedy, DP, Sequence DP, Status DP +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + ``` /* Say you have an array for which the ith element is the price of a given stock on day i. -Design an algorithm to find the maximum profit. You may complete as many transactions as you like (ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). +Design an algorithm to find the maximum profit. You may complete as many transactions as you like +(ie, buy one and sell one share of the stock multiple times). +However, you may not engage in multiple transactions +at the same time (ie, you must sell the stock before you buy again). Example +Given an example [2,1,2,0,1], return 2 + Tags Expand -Greedy Array +Greedy Enumeration Array +*/ + + +/* +Thoughts: +Draw a curve and realize that, only when prices[i] > prices[i - 1], +we complete buy/sell and take the profit. +Adding more slopes can be greater than 0~N overall height diff. + +// Greedy +*/ +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int profit = 0; + for (int i = 1; i < prices.length; i++) { + if (prices[i] > prices[i - 1]) { + profit += prices[i] - prices[i - 1]; + } + } + return profit; + } +} + +//DP: See details at notes above +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int n = prices.length; + int[][] dp = new int[n + 1][2]; + dp[0][0] = dp[0][1] = 0; + dp[1][1] = 0; + dp[1][0] = - prices[0];// -2 + for (int i = 2; i <= n; i++) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 1][1] - prices[i - 1]); + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i - 1]); + } + return dp[n][1]; //return Math.max(dp[n][1], dp[n][0]); + } +}; + +// Rolling array +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int n = prices.length; + int[][] dp = new int[2][2]; + dp[0][0] = dp[0][1] = 0; + dp[1][0] = - prices[0]; + for (int i = 2; i <= n; i++) { + dp[i % 2][0] = Math.max(dp[(i - 1) % 2][0], dp[(i - 1) % 2][1] - prices[i - 1]); + dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], dp[(i - 1) % 2][0] + prices[i - 1]); + } + return dp[n % 2][1]; + } +}; + + +/* +Previous notes about the above solution: +Greedy: when seeing a increase, sell, accumulate the delta benefits. +For instance, at a inclining slope, the sum of delta changes between (i-1, i) equals to the increase of (0 , n), so it's okay to do greedy. +But this is less inteligent, and not very applicable +*/ + -Thinking process: +/* +Thought: In this case, since we know the entire stock price for all days in the array, we want to this: Sell it at nearest peek price and buy it on the next dropped price, then sell again at next peek. Two pointers, start and peek, to track the starting point and the peek. @@ -19,32 +134,91 @@ Design an algorithm to find the maximum profit. You may complete as many transac Inner while loop that finds the peek from start. Note: peek always has index >= start+1. Sum up all profit and return it. -Tricky thing: we are looking for max profit, but does not require to sell the stock by end of array. So if the price is dropping, we are not selling and we are not losing/winning anything. -*/ +Tricky thing: we are looking for max profit, but does not require to sell the stock by end of array. +So if the price is dropping, we are not selling and we are not losing/winning anything. +*/ class Solution { - /** - * @param prices: Given an integer array - * @return: Maximum profit - */ public int maxProfit(int[] prices) { - if (prices == null || prices.length == 0) { + if (prices == null || prices.length <= 1) { return 0; } + int curr = 0; int profit = 0; - int start = 0; - int peek = 0; - while (start < prices.length - 1) { - peek = start + 1; + int peek = 1; + while(curr < prices.length) { while (peek < prices.length && prices[peek - 1] <= prices[peek]) { peek++; } - profit += prices[peek - 1] - prices[start]; - start = peek; + profit += prices[peek - 1] - prices[curr]; + curr = peek; + peek = curr + 1; } return profit; } -}; +} + +/* +Thoughts: +Optimize the DFS (since it times out) +buyOn[i]: [i ~ n], if buying on day i, what's the best profit +sellOn[i]: [i ~ n], if selling on day i, what's the best profit. +equation: +buyOn[i]: on day i, we can buy && sell on day [i + 1], or do nothing. +sellOn[i]: on day i, we can sell && buy on day [i + 1], or do nothing. +buyOn[0]: max value will be calculated and saved up here. +O(n) + +However, still very slow, only beat 2% +*/ +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length <= 1) { + return 0; + } + int[] buyOn = new int[prices.length]; + int[] sellOn = new int[prices.length]; + int length = prices.length; + buyOn[length - 1] = 0; + sellOn[length - 1] = prices[length - 1]; + for (int i = length - 2; i >= 0; i--) { + buyOn[i] = Math.max(buyOn[i + 1], sellOn[i + 1] - prices[i]); // (not buying on day i; buying on day i, so - prices[i]) + sellOn[i] = Math.max(sellOn[i + 1], buyOn[i + 1] + prices[i]);// (not selling on day i; selling on day i, so + prices[i]) + } + return Math.max(0, buyOn[0]); + } +} + + + + +/* +Thoughts: +On given day: we can choose to buy or sell or move with no action, which generates different paths -> DFS +However: 196 / 198 test cases passed but times out. +*/ +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length <= 1) { + return 0; + } + return Math.max(dfs(prices, 1, true) - prices[0], dfs(prices, 1, false)); + } + private int dfs(int[] prices, int index, boolean sellStatus) { + if (index == prices.length - 1) { + return sellStatus ? prices[index] : 0; + } + int profit = 0; + for (int i = index; i < prices.length; i++) { + //No action + profit = Math.max(profit, dfs(prices, i + 1, sellStatus)); + //Sell or buy: + int levelDelta = sellStatus ? prices[i] : - prices[i]; + profit = Math.max(profit, dfs(prices, i + 1, !sellStatus) + levelDelta); + } + return profit; + } +} ``` \ No newline at end of file diff --git a/Java/Best Time to Buy and Sell Stock III.java b/Java/Best Time to Buy and Sell Stock III.java new file mode 100755 index 0000000..63ad48e --- /dev/null +++ b/Java/Best Time to Buy and Sell Stock III.java @@ -0,0 +1,166 @@ +H +1523511824 +tags: Array, DP, Sequence DP + +比stock II 多了一个限制:只有2次卖出机会. + +#### DP加状态 +- 只卖2次, 把买卖分割成5个状态模块. +- 在状态index 0, 2, 4: 没有持有股票. 1. 一直在此状态, max profit不变; 2. 刚卖掉, dp[i][前状态] + profit +- 在状态index 1, 3: 持有股票. 1. 一直在此状态, daily profit. 2. 刚刚买进, 状态改变, 但是没有profit yet: dp[i][前状态] + +##### Partial profit +- 把每天的partial profit (diff)加在一起, 最终的overall profit是一样的. 唯一更好的是, 不需要记录中间买入的时间点. +- 什么时候会积累profit呢? +- 1. 原本就持有股票的, 如果毫无动作, 那么状态不变, 积累profit diff. +- 2. 卖出了股票, 状态改变, 积累profit diff. +- 注意: 只有在状态index: 0, 2, 4, 也就是卖掉股票的时候, 才可以积累profit + +##### Rolling Array +- [i] 只有和 [i-1] 打交道, reduce space +- O(1) space, O(n) time + +#### 找峰头 +- 找峰头;然后往下再找一个峰头。 +- 怎么样在才能Optimize两次巅峰呢?从两边同时开始找Max!(棒棒的想法) +- leftProfit是从左往右,每个i点上的最大Profit。 +- rightProfit是从i点开始到结尾,每个点上的最大profit. +- 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 +- 三个O(n),还是O(n) + +``` +/* +Say you have an array for which the ith element is the price of a given stock on day i. + +Design an algorithm to find the maximum profit. You may complete at most two transactions. + +Example +Given an example [4,4,6,1,1,4,2,5], return 6. + +Note +You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). + +Tags Expand +Enumeration Forward-Backward Traversal Array + +*/ +/* +Thoughts: +DP: calculate the +Able to sell 2 times. Consider the last position dp[i], is it sold 2 times? sold 1 time? before buying 1st stock? before buying 2nd stock? or right in between 1st and 2nd transaction, having 0 stock? +The status of the problem should be recorded, which leads to 2nd dimension to record these status: +0 stock, before buying | having 1st stock | sold 1st stock, having 0, before buying | having 2nd stock| sold 2nd stock +dp[i][4]: max profit at index i, where both are sold. +Move the status from 0 ~ 4, and break when reached the end. + +equations are a bit complicated: +- status index 0, 2, 4: + dp[i][j]: dp[i - 1][j - 1] + price[i] - price[i - 1] (status changed from yesterday, and profiting) + OR: dp[i - 1][j] (status didn't change from yesterday) +- status index 1, 3 + dp[i][3]: dp[i - 1][j] + price[i] - prices[i - 1] (status didn't change from yesterday, keep profiting) + OR: dp[i - 1][j - 1] (status changed from yesterday, just bought) + +init: +dp[0][0] = 0; + +One note: +Consider incremental profit sum = overall profit sum. Sum of price[i] - price[i - 1] = price[0~N] +*/ +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int profit = 0; + int n = prices.length; + int[][] dp = new int[n][5]; + + dp[0][0] = 0; // No transaction on day 0 + for (int i = 1; i < n; i++) { + for (int j = 1; j < 5; j++) { + // Accumulate partial profit regardless of stock status. + int dailyDiff = prices[i] - prices[i - 1]; + if (j % 2 == 0) { // j status: no stock + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + dailyDiff); + // Find best profit when not having stock + profit = Math.max(profit, dp[i][j]); + } else { // j status: have stock + dp[i][j] = Math.max(dp[i - 1][j] + dailyDiff, dp[i - 1][j - 1]); + } + } + } + return profit; + } +} + +// Rolling array +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int profit = 0; + int n = prices.length; + int[][] dp = new int[2][5]; + + dp[0][0] = 0; // No transaction on day 0 + for (int i = 1; i < n; i++) { + for (int j = 1; j < 5; j++) { + // Accumulate partial profit regardless of stock status. + int dailyPartialProfit = prices[i] - prices[i - 1]; + if (j % 2 == 0) { // j status: no stock + dp[i % 2][j] = Math.max(dp[(i - 1) % 2][j], dp[(i - 1) % 2][j - 1] + dailyPartialProfit); + // Find best profit when not having stock + profit = Math.max(profit, dp[i % 2][j]); + } else { // j status: have stock + dp[i % 2][j] = Math.max(dp[(i - 1) % 2][j] + dailyPartialProfit, dp[(i - 1) % 2][j - 1]); + } + } + } + return profit; + } +} + +/* +Thoughts: +Idea from NineChapter: use two arrays to mark max values, however the max values are calculated from left/right sides. +Left[] marks max profit value in the range from a left-index to current index. Tracking left-min. +Right[] marks max profit value in the range from current index to a right-index. Tracking right-max. +If looking at right[i] and left[i] together at index i, they are always representing left-max-profit value and right-max-profit value. Add them together and get results. + +*/ +class Solution { + /** + * @param prices: Given an integer array + * @return: Maximum profit + */ + public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int[] leftProfit = new int[prices.length]; + int[] rightProfit = new int[prices.length]; + int min = prices[0]; //Default:leftProfit[0] = 0; + for (int i = 1; i < prices.length; i++) { + min = Math.min(min, prices[i]); + leftProfit[i] = Math.max(leftProfit[i - 1], prices[i] - min); + } + int max = prices[prices.length - 1]; //Default:rightProfit[prices.length - 1] = 0; + for (int i = prices.length - 2; i >= 0; i--) { + max = Math.max(max, prices[i]); + rightProfit[i] = Math.max(rightProfit[i + 1], max - prices[i]); + } + int profit = 0; + for (int i = 0; i < prices.length; i++) { + profit = Math.max(profit, leftProfit[i] + rightProfit[i]); + } + return profit; + } +}; + + + + + +``` \ No newline at end of file diff --git a/Java/Best Time to Buy and Sell Stock IV.java b/Java/Best Time to Buy and Sell Stock IV.java old mode 100644 new mode 100755 index 56aba94..7702b1f --- a/Java/Best Time to Buy and Sell Stock IV.java +++ b/Java/Best Time to Buy and Sell Stock IV.java @@ -1,6 +1,44 @@ -有问题。还不是非常理解: -best time to buy and sell stock: 为什么 i-1天的卖了又买,可以和第 i 天的卖合成一次交易? -因为每天交易的price是定的。所以卖了又买,等于没卖!这就是可以合并的原因。要对价格敏感啊少年。 +H +1523513634 +tags: DP, Sequence DP + +有int[] price of stock, 最多做 k transactions. 求最大profit. + +#### DP +- 根据StockIII, 不难发现StockIV就是把状态划分为2k+1份. 那么同样的代码, 移植. + +##### 注意1: +- 如果k很大, k>n/2, 那么长度为n的数组里面, 最多也只能n/2个transaction +- 那么题目简化为stockII, 给n数组, 无限次transaction. +- 注意, status的数量是 2k+1 +- Time O(NK), Space O(2k+1) to store the status + +##### 注意2: +- 最后状态是'没有stock'的都该考虑, 做一个 for 循环比较max. +- 当然, 来一个profit variable, 不断比较, 也是可以的. + +#### 方法2 +- (previous notes, 熟练第一种方法的思考就可以) +- 记得要理解:为什么 i-1天的卖了又买,可以和第 i 天的卖合成一次交易? +- 因为每天交易的price是定的。所以卖了又买,等于没卖!这就是可以合并的原因。要对价格敏感啊少年。 +- Inspired from here: http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html + +##### 局部最优解 vs. 全局最优解: +- local[i][j] = max(global[i – 1][j – 1] + diff, local[i – 1][j] + diff) +- global[i][j] = max(global[i – 1][j], local[i][j]) +- local[i][j]: 第i天,当天一定进行第j次交易的profit +- global[i][j]: 第i天,总共进行了j次交易的profit. + +- local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生。 +- 当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff; +- 当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。 +- (Note:在我下面这个solution里面没有省去 +diff) + +- global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况: +- 如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j]; +- 如果第i天有交易(卖出),那么global[i][j]=local[i][j]。 + + ``` /* @@ -22,6 +60,144 @@ Dynamic Programming */ + +/* +Thoughts: +Similar to StockIII, but able to make k transactions. +K transactions divides leads to 2K + 1 different status: always have the two conditions 'before buying' and 'holding', plus the final sold status. + +Equation: +on j%2 == 0 days (not having stock): + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + partialProfit); +on j%2 == 1 days (holding stock) + dp[i][j] = Math.max(dp[i - 1][j] + partialProfit, dp[i - 1][j - 1]); + +O(n(2*k + 1)) = O(nk) +space O(nk) +time O(nk) +*/ +class Solution { + public int maxProfit(int k, int[] prices) { + if (prices == null || prices.length == 0 || k <= 0) { + return 0; + } + int profit = 0; + int n = prices.length; + int statusLength = 2 * k + 1; + + // A side note: if k > n/2, the problem becomes easy: any n/2 number of transactions + if (k >= n/2) { + for (int i = 1; i < n; i++) { + if (prices[i] > prices[i - 1]) { + profit += prices[i] - prices[i - 1]; + } + } + return profit; + } + + int[][] dp = new int[n][statusLength]; + dp[0][0] = 0; // on day 0, having 0 stock, and with 0 transactions. + + for (int i = 1; i < n; i++) { + for (int j = 1; j < statusLength; j++) { + //int partialProfit = prices[i] - prices[i - 1]; + if (j % 2 == 0) { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - 1] + prices[i] - prices[i - 1]); + // Find best profit when not having stock + profit = Math.max(profit, dp[i][j]); + } else { + dp[i][j] = Math.max(dp[i - 1][j] + prices[i] - prices[i - 1], dp[i - 1][j - 1]); + } + } + } + return profit; + } +} + +// Rolling array with %2 +class Solution { + public int maxProfit(int k, int[] prices) { + if (prices == null || prices.length == 0 || k <= 0) { + return 0; + } + int profit = 0; + int n = prices.length; + int statusLength = 2 * k + 1; + + // A side note: if k > n/2, the problem becomes easy: any n/2 number of transactions + if (k >= n/2) { + for (int i = 1; i < n; i++) { + if (prices[i] > prices[i - 1]) { + profit += prices[i] - prices[i - 1]; + } + } + return profit; + } + + int[][] dp = new int[2][statusLength]; + dp[0][0] = 0; // on day 0, having 0 stock, and with 0 transactions. + + for (int i = 1; i < n; i++) { + for (int j = 1; j < statusLength; j++) { + //int partialProfit = prices[i] - prices[i - 1]; + if (j % 2 == 0) { + dp[i % 2][j] = Math.max(dp[(i - 1) % 2][j], dp[(i - 1) % 2][j - 1] + prices[i] - prices[i - 1]); + // Find best profit when not having stock + profit = Math.max(profit, dp[i % 2][j]); + } else { + dp[i % 2][j] = Math.max(dp[(i - 1) % 2][j] + prices[i] - prices[i - 1], dp[(i - 1) % 2][j - 1]); + } + } + } + return profit; + } +} + +// optimization: rolling array +// space: O(k), time: O(nk) +class Solution { + public int maxProfit(int k, int[] prices) { + if (prices == null || prices.length == 0 || k <= 0) { + return 0; + } + int profit = 0; + int n = prices.length; + int statusLength = 2 * k + 1; + + // A side note: if k > n/2, the problem becomes easy: any n/2 number of transactions + if (k >= n/2) { + for (int i = 1; i < n; i++) { + if (prices[i] > prices[i - 1]) { + profit += prices[i] - prices[i - 1]; + } + } + return profit; + } + + int[][] dp = new int[2][statusLength]; + int prev, curr = 0; + dp[0][0] = 0; // on day 0, having 0 stock, and with 0 transactions. + + for (int i = 1; i < n; i++) { + // reverse rolling digit + prev = curr; + curr = 1 - prev; + for (int j = 1; j < statusLength; j++) { + //int partialProfit = prices[i] - prices[i - 1]; + if (j % 2 == 0) { + dp[curr][j] = Math.max(dp[prev][j], dp[prev][j - 1] + prices[i] - prices[i - 1]); + // Find best profit when not having stock + profit = Math.max(profit, dp[curr][j]); + } else { + dp[curr][j] = Math.max(dp[prev][j] + prices[i] - prices[i - 1], dp[prev][j - 1]); + } + } + } + return profit; + } +} + + /* Thoughts: http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html local[i][j] = max(global[i – 1][j – 1] , local[i – 1][j] + diff). WHY???? @@ -29,11 +205,6 @@ */ class Solution { - /** - * @param k: An integer - * @param prices: Given an integer array - * @return: Maximum profit - */ public int maxProfit(int k, int[] prices) { if (prices == null || prices.length < 2 || k <= 0) { return 0; diff --git a/Java/Best Time to Buy and Sell Stock with Cooldown.java b/Java/Best Time to Buy and Sell Stock with Cooldown.java new file mode 100644 index 0000000..aba4f58 --- /dev/null +++ b/Java/Best Time to Buy and Sell Stock with Cooldown.java @@ -0,0 +1,71 @@ +M +1518714005 +tags: DP + +Sequence DP +跟StockIII很像. 分析好HaveStock && NoStock的状态, 然后看最后一步. + +``` +/* +Say you have an array for which the ith element is the price of a given stock on day i. + +Design an algorithm to find the maximum profit. +You may complete as many transactions as you like +(ie, buy one and sell one share of the stock multiple times) with the following restrictions: + +You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). +After you sell your stock, you cannot buy stock on next day. (ie, cooldown 1 day) +Example: + +prices = [1, 2, 3, 0, 2] +maxProfit = 3 +transactions = [buy, sell, cooldown, buy, sell] +*/ + +/* +Thoughts: +Similar to Stock III, where there where 5 states: before1stBuy, have1stStock, soldAndBefore2ndBuy, have2ndStock, soldAll. +Here we have 3 states: +BeforeBuy, BuyStock, SoldStock, cooldown(beforeBuy) ====simplify===> HaveStock(buyStock), NoStock(soldStock, cooldown). +SoldStock and cooldown can be combined together, since there is no stock at hand. +dp[prices.lengh][2] +dp[i][j]: at i, what's the best profit under status j +If j % 2 == haveStock: + before (i), no changes, dp[i - 1][j] + before (i), bought a stock: dp[i - 1][j] - prices[i - 1]; + +If j % 2 == noStock + before (i), no changes, dp[i - 1][j] + before (i), sold stock + cooldown, dp[i - 2][j] + prices[i - 2]; + +dp[0][0] = 0; +*/ +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length <= 1) { + return 0; + } + int n = prices.length; + int[][] dp = new int[n][2]; + dp[0][0] = - prices[0]; //buy + dp[0][1] = 0; //sell + + for (int i = 1; i < n; i++) { + + // no stock (sell) + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 1][0] + prices[i]); + + // haveStock (buy) + dp[i][0] = dp[i - 1][0]; + if (i >= 2) { + dp[i][0] = Math.max(dp[i][0], dp[i - 2][1] - prices[i]); + } else { + dp[i][0] = Math.max(dp[i][0], - prices[i]); + } + //dp[i][0] = Math.max(dp[i - 1][0], (i >= 2 ? dp[i - 2][1] : 0) - prices[i]); // simplify + } + + return dp[n - 1][1]; + } +} +``` \ No newline at end of file diff --git a/Java/Best Time to Buy and Sell Stock with Transaction Fee.java b/Java/Best Time to Buy and Sell Stock with Transaction Fee.java new file mode 100644 index 0000000..9f7cd6d --- /dev/null +++ b/Java/Best Time to Buy and Sell Stock with Transaction Fee.java @@ -0,0 +1,96 @@ +M +1531728802 +tags: Array, DP, Greedy, Sequence DP, Status DP +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + +``` +/* +Your are given an array of integers prices, +for which the i-th element is the price of a given stock on day i; +and a non-negative integer fee representing a transaction fee. + +You may complete as many transactions as you like, +but you need to pay the transaction fee for each transaction. +You may not buy more than 1 share of a stock at a time (ie. you must sell the stock share before you buy again.) + +Return the maximum profit you can make. + +Example 1: +Input: prices = [1, 3, 2, 8, 4, 9], fee = 2 +Output: 8 +Explanation: The maximum profit can be achieved by: +Buying at prices[0] = 1 +Selling at prices[3] = 8 +Buying at prices[4] = 4 +Selling at prices[5] = 9 +The total profit is ((8 - 1) - 2) + ((9 - 4) - 2) = 8. +Note: + +0 < prices.length <= 50000. +0 < prices[i] < 50000. +0 <= fee < 50000. +*/ + +/* +- sequence dp. dp[i] represents max profit for first i days. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +return dp[n][1] +*/ +class Solution { + public int maxProfit(int[] prices, int fee) { + if (prices == null || prices.length <= 1) return 0; + // init dp[][] with n+1 + int n = prices.length; + int[][] dp = new int[n + 1][2]; + dp[0][0] = dp[0][1] = 0; + dp[1][1] = 0; + dp[1][0] = - prices[0]; + + // calculate dp + for (int i = 2; i <= n; i++) { + dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][1] - prices[i - 1]); + dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][0] + prices[i - 1] - fee); + } + + return dp[n][1]; + } +} + +// Rolling array +class Solution { + public int maxProfit(int[] prices, int fee) { + if (prices == null || prices.length <= 1) return 0; + // init dp[][] with n+1 + int n = prices.length; + int[][] dp = new int[2][2]; + dp[0][0] = dp[0][1] = 0; + dp[1][1] = 0; + dp[1][0] = - prices[0]; + + // calculate dp + for (int i = 2; i <= n; i++) { + dp[i%2][0] = Math.max(dp[(i - 1)%2][0], dp[(i - 1)%2][1] - prices[i - 1]); + dp[i%2][1] = Math.max(dp[(i - 1)%2][1], dp[(i - 1)%2][0] + prices[i - 1] - fee); + } + + return dp[n%2][1]; + } +} +``` \ No newline at end of file diff --git a/Java/Best Time to Buy and Sell Stock.java b/Java/Best Time to Buy and Sell Stock.java new file mode 100755 index 0000000..eb17d7d --- /dev/null +++ b/Java/Best Time to Buy and Sell Stock.java @@ -0,0 +1,165 @@ +E +1531717344 +tags: Array, DP, Sequence DP + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### 理解意思是关键 +- 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出 +- 记录每天最小值Min是多少. O(n) +- 每天都算和当下的Min买卖,profit最大多少. + +#### DP +- Find min value for first i items, new dp[n + 1]. +- dp[i]: 前i天, prices最小的price是多少: min cost of first i days +- 然后用当天的price做减法dp[i]算max profit. +- Time, Space: O(n) +- 更进一步, 用一个min来表示min[i], 因为计算中只需要当下的min. + +#### Rolling array +- index i only depend on [i - 2] +- Space O(n) + +#### Brutle Failed +- 每天都试着买进,然后之后的每一天尝试卖出. double for loop, O(n^2). timeout. +- 其中很多都是没必要的计算:[7, 1, 5, 3, 6, 4]。 if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. + +``` +/* +Say you have an array for which the ith element is the price of a given stock on day i. + +If you were only permitted to complete at most one transaction +(ie, buy one and sell one share of the stock), +design an algorithm to find the maximum profit. + +Example 1: +Input: [7, 1, 5, 3, 6, 4] +Output: 5 + +max. difference = 6-1 = 5 (not 7-1 = 6, as selling price needs to be larger than buying price) +Example 2: +Input: [7, 6, 4, 3, 1] +Output: 0 + +In this case, no transaction is done, i.e. max profit = 0. + +*/ + +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int n = prices.length; + int[] dp = new int[n + 1]; // min value up to first i items + dp[0] = Integer.MAX_VALUE; + int profit = 0; + for (int i = 1; i <= n; i++) { + dp[i] = Math.min(dp[i - 1], prices[i - 1]); + profit = Math.max(profit, prices[i - 1] - dp[i]); + } + return profit; + } +} + +// Rolling array +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int n = prices.length; + int[] dp = new int[2]; + dp[0] = Integer.MAX_VALUE; + int profit = 0; + for (int i = 1; i <= n; i++) { + dp[i % 2] = Math.min(dp[(i - 1) % 2], prices[i - 1]); + profit = Math.max(profit, prices[i - 1] - dp[i % 2]); + } + return profit; + } +} + +/* +Thoughts: +Brutle - buy in on any given day (or not buy), and try to sell on any later days; find the max profit; but it timesout. + +Lots of calculations are redundant: for example, if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. +How about storing the min[i]all the time? +min[i]: from 0 ~ i the minimum +Goal: find max of (prices[i] - min[i]) that gives best profit. +*/ +/* +Thoughts: +Brutle - buy in on any given day (or not buy), and try to sell on any later days; find the max profit. +But it timesout +*/ +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length <= 1) { + return 0; + } + int profit = 0; + int[] min = new int[prices.length]; + min[0] = prices[0]; + for (int i = 1; i < prices.length; i++) { + min[i] = Math.min(min[i - 1], prices[i]); + profit = Math.max(profit, prices[i] - min[i]); + } + return profit; + } +} + +/* +Improvement: No need to have array, and only find max when needed. + +First to understand what this question is asking: +Each element is a price of the same stock on that specific day. Of course we want to buy in with min-price and sell out with max-price. +Find the min-price by looping through the array. +Find the max-profit: price[i] - min-price, which requires a loop through the loop again. +We put above 2 tasks into one-pass for loop + +*/ +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length <= 1) { + return 0; + } + int profit = 0, min = prices[0]; + for (int i = 1; i < prices.length; i++) { + if (prices[i] < min) { + min = prices[i]; + } else { + profit = Math.max(profit, prices[i] - min); + } + } + return profit; + } +} + + +/* +Thoughts: +Brutle - buy in on any given day (or not buy), and try to sell on any later days; find the max profit. +But it timesout. +*/ +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int max = 0; + for (int i = 0; i < prices.length; i++) { + for (int j = i + 1; j < prices.length; j++) { + if (prices[j] > prices[i]) { + max = Math.max(max, prices[j] - prices[i]); + } + } + } + return max; + } +} + + + +``` \ No newline at end of file diff --git a/Java/Binary Gap.java b/Java/Binary Gap.java new file mode 100644 index 0000000..307dc9c --- /dev/null +++ b/Java/Binary Gap.java @@ -0,0 +1,77 @@ +E +1531623482 +tags: Bit Manipulation +time: O(n), n = # of bits +space: O(1) + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + +``` +/* +Given a positive integer N, find and return the longest distance +between two consecutive 1's in the binary representation of N. + +If there aren't two consecutive 1's, return 0. + + + +Example 1: + +Input: 22 +Output: 2 +Explanation: +22 in binary is 0b10110. +In the binary representation of 22, there are three ones, and two consecutive pairs of 1's. +The first consecutive pair of 1's have distance 2. +The second consecutive pair of 1's have distance 1. +The answer is the largest of these two distances, which is 2. +Example 2: + +Input: 5 +Output: 2 +Explanation: +5 in binary is 0b101. +Example 3: + +Input: 6 +Output: 1 +Explanation: +6 in binary is 0b110. +Example 4: + +Input: 8 +Output: 0 +Explanation: +8 in binary is 0b1000. +There aren't any consecutive pairs of 1's in the binary representation of 8, so we return 0. + +Note: + +1 <= N <= 10^9 +*/ + + + +class Solution { + public int binaryGap(int N) { + if (N <= 0) return 0; + // mark start, end, max + int max = 0, start = -1, end = 0; + // iterate until N is over + while (N > 0) { + if ((N & 1) == 1) { + if (start == -1) start = end; + else { + max = Math.max(max, end - start); + start = end; + } + } + end++; + N = N >> 1; + } + return max; + } +} +``` \ No newline at end of file diff --git a/Java/Binary Representation.java b/Java/Binary Representation.java old mode 100644 new mode 100755 index 3db457d..92112cc --- a/Java/Binary Representation.java +++ b/Java/Binary Representation.java @@ -1,19 +1,26 @@ -主要就是要分两半。 +H +1529478003 +tags: String, Bit Manipulation -Integer那一半好弄,Loop里面 num%2, num/2就好。 -Decimal那边复杂点,bit == 1的数学条件是: -当下num * 2 - 1 >= 0... -然后循环时候还要 num * 2 - 1, 或者 num * 2 +#### String +- 首先要分两半解决,断点是'.': str.split("\\."); +- Integer那一半好弄,whie loop里: num%2, num/2. 做一个 `parseInteger()` function +- Decimal那边复杂点. 做一个 `parseDecimal()` function: +- bit == 1的数学条件: 当下num * 2 >= 1。 更新: num = num * 2 - 1; +- bit == 0的数学条件: num * 2 < 1. 更新: num = num * 2 -因为num是 double, 小于0的小数,所以其实这样做下去很可能无限循环。 - -所以题目也才有了32BIT的要求! +#### 注意 +- num是 double, 小数在 `num = num * 2 - 1` 的公式下可能无限循环 +- 因此check: num重复性,以及binary code < 32 bit. +- 所以题目也才有了32BIT的要求! ``` /* -Given a (decimal - e.g. 3.72) number that is passed in as a string, return the binary representation that is passed in as a string. If the fractional part of the number can not be represented accurately in binary with at most 32 characters, return ERROR. +Given a (decimal - e.g. 3.72) number that is passed in as a string, +return the binary representation that is passed in as a string. +If the fractional part of the number can not be represented +accurately in binary with at most 32 characters, return ERROR. -Have you met this question in a real interview? Yes Example For n = "3.72", return "ERROR". @@ -43,11 +50,8 @@ Given a (decimal - e.g. 3.72) number that is passed in as a string, return the b for example: 2x - 1 = x -> x = 1. that will cause infinite loop. */ + public class Solution { - /** - *@param n: Given a decimal number that is passed in as a string - *@return: A string - */ public String binaryRepresentation(String n) { if (n.length() == 0 || n.equals("0")) { return "0"; @@ -77,12 +81,12 @@ public String parseInteger(String n) { return n; } int num = Integer.parseInt(n); - String rst = ""; + StringBuffer sb = new StringBuffer(); while (num != 0) { - rst = num % 2 + rst;//mod(2) -> binary representation + sb.insert(0, num % 2);//mod(2) -> binary representation num = num / 2;//小时候转换二进制也是这样。 } - return rst; + return sb.toString(); } // A little bit math, but implemtable. public String parseDecimal(String n) { @@ -92,23 +96,23 @@ public String parseDecimal(String n) { //A doublem must be able to catch it. If not, that is way bigger than 32 bit. double num = Double.parseDouble("0." + n); //Check existance - HashSet set = new HashSet(); - String rst = ""; + HashSet set = new HashSet<>(); + StringBuffer sb = new StringBuffer(); while (num > 0) { - if (rst.length() > 32 || set.contains(num)) { + if (sb.length() > 32 || set.contains(num)) { return "ERROR"; } set.add(num); //For decimal: binary code on one spot == 1, means: num * 2 - 1 > 0 if (num * 2 >= 1) { - rst = rst + "1"; + sb.append("1"); num = num * 2 - 1; } else { - rst = rst + "0"; + sb.append("0"); num = num * 2; } } - return rst; + return sb.toString(); } } ``` \ No newline at end of file diff --git a/Java/Binary Search Tree Iterator.java b/Java/Binary Search Tree Iterator.java old mode 100644 new mode 100755 index 0333f5a..6c2bd31 --- a/Java/Binary Search Tree Iterator.java +++ b/Java/Binary Search Tree Iterator.java @@ -1,17 +1,47 @@ -理解binary search tree inorder traversal的规律。 -都是先找left.left.left ....left. 为top。 -然后再找parent,然后再right. +M +1518626557 +tags: Stack, Tree, Design, BST + +画一下, BST in order traversal. 用stack记录最小值, 放在top. O(h) space. +每次消耗TreeNode, 都看看rightNode(其实就是下一个最小的candidate), 并且一条龙stack叠上rightNode所有的left子孙. + +Previous Notes: +用O(h)空间的做法: + +理解binary search tree inorder traversal的规律: + 先找left.left.left ....left 到底,这里是加进stack. + 然后考虑parent,然后再right. + +例如这题: + stack里面top,也就是tree最左下角的node先考虑,取名rst. + 其实这个rst拿出来以后, 它也同时是最底层left null的parent,算考虑过了最底层的parent。 + 最后就考虑最底层的parent.right, 也就是rst.right. + +注意: + next()其实有个while loop, 很可能是O(h).题目要求average O(1),所以也是okay的. + + +用O(1)空间的做法:不存stack, 时刻update current为最小值。 + +找下一个最小值,如果current有right child: + 和用stack时的iteration类似,那么再找一遍current.right的left-most child,就是最小值了。 + +如果current没有right child: + 那么就要找current node的右上parent, search in BinarySearchTree from root. + +注意: + 一定要确保找到的parent满足parent.left == current. + 反而言之,如果current是parent的 right child, 那么下一轮就会重新process parent。 + 但是有错:binary search tree里面parent是小于right child的,也就是在之前一步肯定visit过,如此便会死循环。 + -这个题目里面找到rst之后,首先考虑这个rst.right - 其实rst在这里虽然是most left node, 但对于rst.right来说,其实它也是parent. -所以每次把left全部弄一边的时候,parent node其实也都是顾及到了的。 ``` /* Design an iterator over a binary search tree with the following rules: Elements are visited in ascending order (i.e. an in-order traversal) next() and hasNext() queries run in O(1) time in average. -Have you met this question in a real interview? Yes + Example For the following binary search tree, in-order traversal by using iterator is [1, 6, 10, 11, 12] @@ -29,14 +59,6 @@ Extra memory usage O(h), h is the height of the tree. Binary Tree LintCode Copyright Non Recursion Binary Search Tree Google LinkedIn Facebook */ -/* - Thoughts://http://blog.csdn.net/u014748614/article/details/46800891 - Put all left nodes into stack. Then top of stack must be the first element in in-order-traversal. - We never add right node into stack directly, but ever time before returnning the rst node, we take care of rst.right right away. - That is, find next() when rst.right as root. - very smart use of a 'currnt' node. - It's like a pointer on the tree, but only operates when that current node is not null, and under condition of having left child. -*/ /** * Definition of TreeNode: @@ -55,6 +77,95 @@ That is, find next() when rst.right as root. * do something for node * } */ + + /* +Thoughts: +Inorder traversal, always having the smallest on top. +Use stack to keep smallest item on top of stack; when consuming an item, always find right, and dive in and stack most left children +*/ +public class BSTIterator { + final Stack stack = new Stack<>(); + public BSTIterator(TreeNode root) { + if (root == null) { + return; + } + TreeNode node = root; + while (node != null) { + stack.push(node); + node = node.left; + } + } + + /** @return whether we have a next smallest number */ + public boolean hasNext() { + return !stack.isEmpty(); + } + + /** @return the next smallest number */ + public int next() { + TreeNode rst = stack.pop(); + if (rst.right != null) { + TreeNode node = rst.right; + while(node != null) { + stack.push(node); + node = node.left; + } + } + return rst.val; + } +} + +//Recap 02.24.2016: Similar to solution below. O(h) space. +//Stack, inorder traversal; first add left node till end. Each next() trigger a iteration. +public class BSTIterator { + public Stack stack = new Stack(); + + //@param root: The root of binary tree. + //Add till end of left + public BSTIterator(TreeNode root) { + if (root == null) { + return; + } + stack.push(root); + while (root.left != null) { + stack.push(root.left); + root = root.left; + } + } + + //@return: True if there has next node, or false + public boolean hasNext() { + return stack.size() > 0; + } + + //@return: return next node + public TreeNode next() { + TreeNode node = stack.pop(); + if (node.right != null) { + TreeNode temp = node.right; + stack.push(temp); + while (temp.left != null) { + stack.push(temp.left); + temp = temp.left; + } + } + return node; + } +} + + + +/* + Previous correct implementation, O(h) space. + Thoughts:http://blog.csdn.net/u014748614/article/details/46800891 + Put all left nodes into stack. Then top of stack must be the first element in in-order-traversal. + We never add right node into stack directly, but ever time before returnning the rst node, we take care of rst.right right away. + That is, find next() when rst.right as root. + very smart use of a 'currnt' node. + It's like a pointer on the tree, but only operates when that current node is not null, and under condition of having left child. + +*/ + public class BSTIterator { public Stack stack = new Stack(); public TreeNode current; @@ -80,13 +191,95 @@ public TreeNode next() { } } +/* + Use O(1) space, which means we will not use O(h) stack. + To begin: + 1. hasNext()? current.val <= endNode.val to check if the tree is fully traversed. + 2. Find min via left-most: We can alwasy look for left-most to find next minimum value. + + 3. Once left-most min is checked (name it `current`). Next min will be 2 cases: + If current.right != null, we can keep looking for current.right's left-most child, as next min. + Or, we need to look backwards for parent. Use binary search tree to find current's parent node. + + Note: when doing binary search for parent, make sure it satisfies parent.left = current. + + Because:If parent.right == current, that parent must has been visited before. In binary search tree, + we know that parent.val < parent.right.val. We need to skip this special case, since it leads + to ifinite loop. +*/ + + +public class BSTIterator { + public TreeNode root; + public TreeNode current; + public TreeNode endNode; + //@param root: The root of binary tree. + public BSTIterator(TreeNode root) { + if (root == null) { + return; + } + this.root = root; + this.current = root; + this.endNode = root; + + while (endNode != null && endNode.right != null) { + endNode = endNode.right; + } + while (current != null && current.left != null) { + current = current.left; + } + } + + //@return: True if there has next node, or false + public boolean hasNext() { + return current != null && current.val <= endNode.val; + } + + //@return: return next node + public TreeNode next() { + TreeNode rst = current; + //current node has right child + if (current.right != null) { + current = current.right; + while (current.left != null) { + current = current.left; + } + } else {//Current node does not have right child. + current = findParent(); + } + return rst; + } + + //Find current's parent, where parent.left == current. + public TreeNode findParent(){ + TreeNode node = root; + TreeNode parent = null; + int val = current.val; + if (val == endNode.val) { + return null; + } + while (node != null) { + if (val < node.val) { + parent = node; + node = node.left; + } else if (val > node.val) { + node = node.right; + } else {//node.val == current.val + break; + } + } + return parent; + } +} + + +``` -``` \ No newline at end of file diff --git a/Java/Binary Search.java b/Java/Binary Search.java deleted file mode 100644 index 5931e4a..0000000 --- a/Java/Binary Search.java +++ /dev/null @@ -1,61 +0,0 @@ -/* -Easy Binary Search Show Result My Submissions - -26% Accepted -Binary search is a famous question in algorithm. - -For a given sorted array (ascending order) and a target number, find the first index of this number in O(log n) time complexity. - -If the target number does not exist in the array, return -1. - -Example -If the array is [1, 2, 3, 3, 4, 5, 10], for given target 3, return 2. - -Challenge -If the count of numbers is bigger than MAXINT, can your code work properly? - -Tags Expand -Binary Search - -- -*/ - -class Solution { - /** - * @param nums: The integer array. - * @param target: Target to find. - * @return: The first position of target. Position starts from 0. - */ - public int binarySearch(int[] nums, int target) { - //write your code here - if (nums.length == 0) { - return -1; - } - - int start = 0; - int end = nums.length - 1; - int mid; - - while (start + 1 < end) { - mid = start + (end - start) / 2; - if (nums[mid] < target) { - start = mid; - } else if (nums[mid] > target) { - end = mid; - } else { - end = mid; - } - } - - if (nums[start] == target) { - return start; - } else if (nums[end] == target) { - return end; - } else { - return -1; - } - } - - -} - diff --git a/Java/Binary Tree Inorder Traversal.java b/Java/Binary Tree Inorder Traversal.java old mode 100644 new mode 100755 index c53c3e8..adb7668 --- a/Java/Binary Tree Inorder Traversal.java +++ b/Java/Binary Tree Inorder Traversal.java @@ -1,19 +1,30 @@ -1. Recursive: Divide and Conquer -2. Stack: add left nodes all the way; then print curr; move to right, add right if possible. -3. Recursive with helper method +E +1521646879 +tags: Hash Table, Stack, Tree +Inorder traverse Binary Tree -注意inorder traversal在check right node的事后, -不论right == null or != null, 每次都要强行move to right. +#### Recursive +- 在自己的基础上recursive, 不用helper function +- Divide and Conquer, with helper(dfs) method +- O(n) time, no extra space -如果不node = node.right, -很可能发生窘境: -node alays = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 +#### Iterative: Stack +- Add left nodes all the way +- Print curr +- Move to right, add right if possible +- O(n) time, O(h) space + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. +若不右移, 很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + +#### HashMap +? How? ``` /* -Binary Tree Inorder Traversal Given a binary tree, return the inorder traversal of its nodes' values. Example @@ -32,12 +43,104 @@ Can you do it without recursion? Tags Expand -Binary Tree +Recursion Binary Tree Binary Tree Traversal + +*/ + +/* +Thoughts: +Recursive, append left, itself, then right +*/ +class Solution { + public List inorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) { + return rst; + } + if (root.left == null && root.right == null) { + rst.add(root.val); + return rst; + } + List left = inorderTraversal(root.left); + List right = inorderTraversal(root.right); + + rst.addAll(left); + rst.add(root.val); + rst.addAll(right); + return rst; + } +} + +/* + recap 3.15.2016 + Recursive +*/ +public class Solution { + public List inorderTraversal(TreeNode root) { + List rst = new ArrayList(); + if (root == null) { + return rst; + } + dfs(rst, root); + return rst; + } + + public void dfs(List rst, TreeNode node) { + if (node.left != null) { + dfs(rst, node.left); + } + rst.add(node.val); + if (node.right != null) { + dfs(rst, node.right); + } + } +} + + +/* +Thoughts: +Iterative +Stack, always treat left-most-leaf with priority +- add node.left till end. +- consume stack.pop() +- if has right, add node.right and push all left children to stack */ +class Solution { + public List inorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) { + return rst; + } + Stack stack = new Stack<>(); + TreeNode node = root; + // Dive deep to left-most leaf + while (node != null) { + stack.push(node); + node = node.left; + } + + while (!stack.isEmpty()) { + // Add to rst + node = stack.pop(); + rst.add(node.val); + // Add node.right and all left children + if (node.right != null) { + node = node.right; + while (node != null) { + stack.push(node); + node = node.left; + } + } + } + return rst; + } +} + +// Previous notes /* - 3. Use a helper method, recursively add to rst + 1. Use a helper method, recursively add to rst */ public class Solution { @@ -78,10 +181,6 @@ public void helper(ArrayList rst, TreeNode node) { */ public class Solution { - /** - * @param root: The root of binary tree. - * @return: Inorder in ArrayList which contains node values. - */ public ArrayList inorderTraversal(TreeNode root) { ArrayList rst = new ArrayList(); if (root == null) { @@ -99,7 +198,7 @@ public ArrayList inorderTraversal(TreeNode root) { //Pop the top node: the curr node curr = stack.pop(); rst.add(curr.val); - //Move to right node, and push to satck if needed + //Move to right node, and push to stack if needed curr = curr.right; if (curr!= null) { stack.push(curr); @@ -110,66 +209,7 @@ public ArrayList inorderTraversal(TreeNode root) { } -/* -Thinking process: -1. Use recursive function: divide and conquer - recursive on left - result.add( current.val) - recursive on right -2. Use Stack to do traversal - while stack not empty { - stack all left childs - result.(current.val) - stack 1 right child - } -*/ -public class Solution { - - //Recursive - Divide and Conquer - public ArrayList inorderTraversal(TreeNode root) { - ArrayList rst = new ArrayList(); - if (root == null) { - return rst; - } - inorderRec(rst, root); - return rst; - } - public void inorderRec(ArrayList rst, TreeNode root) { - if (root == null) { - return; - } - inorderRec(rst, root.left); - rst.add(root.val); - inorderRec(rst, root.right); - } - //Traversal using Stack - public ArrayList inorderTraversal(TreeNode root) { - ArrayList rst = new ArrayList(); - if (root == null) { - return rst; - } - Stack stack = new Stack(); - TreeNode curr = root; - stack.push(curr); - while (!stack.empty()) { - while (curr != null && curr.left != null) { - curr = curr.left; - stack.push(curr); - } - curr = stack.pop(); - rst.add(curr.val); - curr = curr.right; - if (curr != null) { - stack.push(curr); - } - } - return rst; - } - - - -} ``` \ No newline at end of file diff --git a/Java/Binary Tree Level Order Traversal II.java b/Java/Binary Tree Level Order Traversal II.java old mode 100644 new mode 100755 index ae360e1..bd496ba --- a/Java/Binary Tree Level Order Traversal II.java +++ b/Java/Binary Tree Level Order Traversal II.java @@ -1,5 +1,22 @@ +M +1526453488 +tags: Tree, BFS + +如题, 但是output要倒序. + +#### BFS +- 跟Binary Tree Level Order Traversal一样,只不过存result一直存在存在0位. + + +#### DFS +- 根据level来append每个list +- rst里面add(0,...)每次都add在list开头 + +``` + /* -Given a binary tree, return the bottom-up level order traversal of its nodes' values. (ie, from left to right, level by level from leaf to root). +Given a binary tree, return the bottom-up level order traversal of its nodes' values. +(ie, from left to right, level by level from leaf to root). Example Given binary tree {3,9,20,#,#,15,7}, @@ -19,17 +36,38 @@ [3] ] Tags Expand -Tree Search Breadth First Search Queue Binary Tree - - -Thinking Process: -1. Non-recursive -similar to Binary Tree Level Order Traversal I, just when adding into the final result, add to the top all the time. Then the first added will be at the bottom: result.add(0, list) -2. Recursive: - Similar to Level Traversal I, do a dfs. The difference is: everytime, we use ArrayList> like a stack by doing add(0, newList); - when populating the levelArrayList, make sure to address the correct corresponding level. +Queue Binary Tree Binary Tree Traversal Breadth First Search */ +class Solution { + public List> levelOrderBottom(TreeNode root) { + ArrayList> result = new ArrayList<>(); + if (root == null) { + return result; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + final ArrayList list = new ArrayList<>(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + final TreeNode node = queue.poll(); + list.add(node.val); + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + result.add(0, list); + }// end while + + return result; + } +} + /** * Definition of TreeNode: * public class TreeNode { @@ -41,8 +79,93 @@ * } * } */ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List> levelOrderBottom(TreeNode root) { + ArrayList> result = new ArrayList<>(); + if (root == null) { + return result; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + final ArrayList list = new ArrayList<>(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + final TreeNode node = queue.poll(); + list.add(node.val); + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + result.add(0, list); + }// end while + + return result; + } +} + +/* +Thoughts: +Breadth first traversal. Add to 0 position every time. +BFS uses a queue for level -> traversal completes when the queue is drained. +Use another queue to store next level, and switch with current queue when need to be. +*/ +class Solution { + public List> levelOrderBottom(TreeNode root) { + final List> result = new ArrayList>(); + if (root == null) { + return result; + } + Queue queue = new LinkedList<>(); + Queue queueLevel = new LinkedList<>(); + List level = new ArrayList<>(); + queue.add(root); + while(!queue.isEmpty()) { + final TreeNode node = queue.poll(); + level.add(node.val); + if (node.left != null) { + queueLevel.add(node.left); + } + if (node.right != null) { + queueLevel.add(node.right); + } + if (queue.isEmpty()) { + queue = queueLevel; + result.add(0, level); + queueLevel = new LinkedList<>(); + level = new ArrayList<>(); + } + } + return result; + } +} - + /* + +Thoughts: +1. Non-recursive +similar to Binary Tree Level Order Traversal I, just when adding into the final result, +add to the top all the time. Then the first added will be at the bottom: result.add(0, list) +2. Recursive: + Similar to Level Traversal I, do a dfs. The difference is: everytime, we use ArrayList> like a stack by doing add(0, newList); + when populating the levelArrayList, make sure to address the correct corresponding level. + + */ + public class Solution { /** * @param root: The root of binary tree. @@ -89,3 +212,4 @@ public void dfs(TreeNode root, int level, ArrayList> rst) { } } +``` diff --git a/Java/Binary Tree Level Order Traversal.java b/Java/Binary Tree Level Order Traversal.java old mode 100644 new mode 100755 index 8cdb741..94ebd78 --- a/Java/Binary Tree Level Order Traversal.java +++ b/Java/Binary Tree Level Order Traversal.java @@ -1,10 +1,23 @@ -1. 最普通,Non-recursive: bfs, queue -2. Recursive with dfs: use a level to track. Add curr into corresponding level; each level > rst.size(), add a new []. - Note: rst is a ArrayList>, where each level is a arraylist; that is why we can add [] into rst to represent a level. +M +1526453393 +tags: Tree, BFS, DFS + +如题. + +#### BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + ``` /* -34% Accepted -Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). +Given a binary tree, return the level order traversal of its nodes' values. +(ie, from left to right, level by level). Example Given binary tree {3,9,20,#,#,15,7}, @@ -24,21 +37,13 @@ [15,7] ] Challenge -Using only 1 queue to implement it. +Challenge 1: Using only 1 queue to implement it. + +Challenge 2: Use DFS algorithm to do it. Tags Expand -Tree Search Breadth First Search Queue Binary Tree +Queue Binary Tree Breadth First Search Binary Tree Traversal Uber LinkedIn Facebook -Thinking process: -1. Non-recursive -Use queue to withhold the parent. -Poll one parent, add this parent’s value to arrayList -Add the children into Arraylist -jump to next level -2. Recursive -use a integer to track levels. -If at a new level, then create a new ArrayList. -At each node, add the node to corresponding level-ArrayList */ /** @@ -52,7 +57,46 @@ * } * } */ - + +public class Solution { + public List> levelOrder(TreeNode root) { + List> rst = new ArrayList<>(); + if (root == null) { + return rst; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + int size = queue.size(); + List list = new ArrayList<>(); + while (size > 0) { + size--; + TreeNode node = queue.poll(); + list.add(node.val); + if(node.left != null) queue.offer(node.left); + if(node.right != null) queue.offer(node.right); + } + rst.add(list); + } + return rst; + } +} + +/* +Thoughts: +1. Non-recursive +Use queue to withhold the parent. +Poll one parent, add this parent’s value to arrayList +Add the children into Arraylist +jump to next level +2. Recursive +use a integer to track levels. +If at a new level, then create a new ArrayList. +At each node, add the node to corresponding level-ArrayList +*/ + +//Non-recurive Iterative way: +//Even with while + for nested loop, it's just O(n) public class Solution { /** * @param root: The root of binary tree. @@ -63,7 +107,7 @@ public ArrayList> levelOrder(TreeNode root) { if (root == null) { return result; } - /* //Non-recurive Iterative way: + //Use a queue to list elements: each row Queue queue = new LinkedList(); queue.offer(root); @@ -83,9 +127,58 @@ public ArrayList> levelOrder(TreeNode root) { } result.add(list); }//while - */ + + return result; + } +} + +//Another Iterative way: using 2 Queues +public class Solution { + public List> levelOrder(TreeNode root) { + List> rst = new ArrayList>(); + if (root == null) { + return rst; + } + Queue queue = new LinkedList(); + Queue backQueue = new LinkedList(); + queue.offer(root); + ArrayList list = new ArrayList(); + + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + if (node.left != null) { + backQueue.offer(node.left); + } + if (node.right != null) { + backQueue.offer(node.right); + } + list.add(node.val); + + if (queue.isEmpty()) { + rst.add(new ArrayList(list)); + list = new ArrayList(); + queue = backQueue; + backQueue = new LinkedList(); + } + + } + return rst; + } +} + + + +//Recursive: +//Recursive with dfs: use a level to track. Add curr into corresponding level; each level > rst.size(), add a new []. +//Note: rst is a ArrayList>, where each level is a arraylist; that is why we can add [] into rst to represent a level. + +public class Solution { + public ArrayList> levelOrder(TreeNode root) { + ArrayList> result = new ArrayList>(); + if (root == null) { + return result; + } - //Recursive: dfs(root, 0, result); return result; } diff --git a/Java/Binary Tree Longest Consecutive Sequence II.java b/Java/Binary Tree Longest Consecutive Sequence II.java new file mode 100644 index 0000000..b37899c --- /dev/null +++ b/Java/Binary Tree Longest Consecutive Sequence II.java @@ -0,0 +1,86 @@ +M +1526453838 +tags: Tree, DFS, Divide and Conquer, Double Recursive + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + +``` +/* +Given a binary tree, you need to find the length of Longest Consecutive Path in Binary Tree. + +Especially, this path can be either increasing or decreasing. For example, +[1,2,3,4] and [4,3,2,1] are both considered valid, but the path [1,2,4,3] is not valid. +On the other hand, the path can be in the child-Parent-child order, +where not necessarily be parent-child order. + +Example 1: +Input: + 1 + / \ + 2 3 +Output: 2 +Explanation: The longest consecutive path is [1, 2] or [2, 1]. +Example 2: +Input: + 2 + / \ + 1 3 +Output: 3 +Explanation: The longest consecutive path is [1, 2, 3] or [3, 2, 1]. +Note: All the values of tree nodes are in the range of [-1e7, 1e7]. +*/ +class Solution { + public int longestConsecutive (TreeNode root) { + if (root == null) { + return 0; + } + int result = dfs(root, 1) + dfs(root, -1) + 1; + return Math.max(result, Math.max(longestConsecutive(root.left), longestConsecutive(root.right))); + } + + // diff can be 1,-1, indicating increasing or decreasing by 1 + private int dfs(TreeNode node, int diff) { + if (node == null) { + return 0; + } + int leftDepth = 0, rightDepth = 0; + // check node + if (node.left != null && node.val - node.left.val == diff) { + leftDepth = dfs(node.left, diff) + 1; + } + if (node.right != null && node.val - node.right.val == diff) { + rightDepth = dfs(node.right, diff) + 1; + } + return Math.max(leftDepth, rightDepth); + } +} + + +``` \ No newline at end of file diff --git a/Java/Binary Tree Longest Consecutive Sequence.java b/Java/Binary Tree Longest Consecutive Sequence.java new file mode 100755 index 0000000..df86818 --- /dev/null +++ b/Java/Binary Tree Longest Consecutive Sequence.java @@ -0,0 +1,150 @@ +M +1527054989 +tags: Tree, DFS, Divide and Conquer + +找到binary tree 里的最长 consecutive sequence. + +#### DFS +- Divide and Conquer. dfs +- 分开 看左边/右边 +- 如果左边满足连续递增的规则, dfs (depth + 1), 如果不满足规则, dfs(depth = 1) +- 右边也是一样 +- 对结果跟max作比较, return + +``` +/* +Given a binary tree, find the length of the longest consecutive sequence path. + +The path refers to any sequence of nodes from some starting node to any node in the tree +along the parent-child connections. The longest consecutive path need to be from parent to child +(cannot be the reverse). + +For example, + 1 + \\ + 3 + / \\ + 2 4 + \\ + 5 +Longest consecutive sequence path is 3-4-5, so return 3. + 2 + \\ + 3 + / + 2 + / + 1 +Longest consecutive sequence path is 2-3,not3-2-1, so return 2. + +Tags:Tree +Similar Problems: (H) Longest Consecutive Sequence + +*/ +/* +Thoughts: +1. Mark depth at each level +2. discuss wether extend or start new depth (per problem requirement) +*/ +class Solution { + public int longestConsecutive (TreeNode root) { + if (root == null) { + return 0; + } + return dfs(root, 1); + } + + private int dfs(TreeNode node, int depth) { + if (node == null) { + return depth; + } + + // check node + int leftDepth = (node.left != null && node.val + 1 == node.left.val) ? + dfs(node.left, depth + 1) : dfs(node.left, 1); + int rightDepth = (node.right != null && node.val + 1 == node.right.val) ? + dfs(node.right, depth + 1) : dfs(node.right, 1); + return Math.max(depth, Math.max(leftDepth, rightDepth)); + } +} + +/* +// DFS, actually find all the items in rst: List>. It's not requried in this problem. +public class Solution { + public int longestConsecutive(TreeNode root) { + if (root == null) { + return 0; + } + List> rst = new ArrayList<>(); + dfs(rst, new ArrayList<>(), root); + List list = new ArrayList<>(); + for (List candidate: rst) { + list = candidate.size() > list.size() ? candidate : list; + } + return list.size(); + } + + private void dfs(List> rst, + List list, + TreeNode node) { + if (node == null) { + if (list != null && list.size() > 0) { + rst.add(new ArrayList<>(list)); + } + return; + } + + // check node + if (list.size() > 0 && list.get(list.size() - 1) != node.val - 1) { + rst.add(new ArrayList<>(list)); + dfs(rst, new ArrayList<>(), node); + } else { + list.add(node.val); + dfs(rst, list, node.left); + dfs(rst, list, node.right); + list.remove(list.size() - 1); + } + } +} +*/ + +/* +Attemp2: http://www.cnblogs.com/jcliBlogger/p/4923745.html. +The original solution has just 4 lines of C++ code. That hurts. +The concept is very much similar as my attempt1, though the code is more clear with recursive call +1. pass alone a depth. +2. if consecutive, depth++; else, start from depth 1 +3. Go deeper on both left, and right; both with new depth: currDepth; +4. Compare the Max of currDept, left's return, right's return. +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public int longestConsecutive(TreeNode root) { + return recursiveHelper(root, null, 0); + } + + public int recursiveHelper(TreeNode curr, TreeNode parent, int depth) { + if (curr == null) { + return 0; + } + int currDepth = 0; + if (parent != null && parent.val + 1 == curr.val) { + currDepth = depth + 1; + } else { + currDepth = 1; + } + return Math.max(currDepth, Math.max(recursiveHelper(curr.left, curr, currDepth), recursiveHelper(curr.right, curr, currDepth))); + } +} + + +``` \ No newline at end of file diff --git a/Java/Binary Tree Maximum Path Sum II.java b/Java/Binary Tree Maximum Path Sum II.java old mode 100644 new mode 100755 index 0dbb7b8..89c790b --- a/Java/Binary Tree Maximum Path Sum II.java +++ b/Java/Binary Tree Maximum Path Sum II.java @@ -1,7 +1,15 @@ -比Binary Tree Maximum Path Sum I 简单许多. -因为条件给的更多:at least 1 node + have to start from root => have to have root. -Single path: either left or right. -If the path sum < 0, just skip it. +M +1526771382 +tags: Tree, DFS + +找到从max path sum from root. 条件: 至少有一个node. + +#### DFS +- 比Binary Tree Maximum Path Sum I 简单许多. 因为条件给的更多:at least 1 node + have to start from root +- root一定用到 +- 3种情况: curr node, curr+left, curr+right +- 因为一定包括root, 说以从 `dfs(root, sum=0)` 开始, 每个level先加root, sum += root.val + ``` /* Binary Tree Maximum Path Sum II @@ -22,6 +30,29 @@ Binary Tree */ +/* + 02.20.2016 recap + just return integer sum, so just traversal the entier binary tree via dfs + dfs: node, sum, return sum +*/ +public class Solution { + public int maxPathSum2(TreeNode root) { + if (root == null) { + return 0; + } + return dfs(root, 0); + } + public int dfs (TreeNode node, int sum) { + if (node == null) { + return sum; + } + sum += node.val; + return Math.max(sum, Math.max(dfs(node.left, sum), + dfs(node.right, sum))); + } +} + + /* Thoughts: maximum path sum from root, so it must include root, and it will be a single path from root to some point in the tree. @@ -29,17 +60,6 @@ 'contains at least 1 node' -> at least have root. However, depending on child is positive or negative, we choose add or no add child */ -/** - * Definition of TreeNode: - * public class TreeNode { - * public int val; - * public TreeNode left, right; - * public TreeNode(int val) { - * this.val = val; - * this.left = this.right = null; - * } - * } - */ public class Solution { /** * @param root the root of binary tree. diff --git a/Java/Binary Tree Maximum Path Sum.java b/Java/Binary Tree Maximum Path Sum.java old mode 100644 new mode 100755 index f3ad82e..2c0c798 --- a/Java/Binary Tree Maximum Path Sum.java +++ b/Java/Binary Tree Maximum Path Sum.java @@ -1,37 +1,71 @@ -有点难理解. -复杂原因是:因为可能有负值啊。不能乱assume正数。 -single path max 的计算是为了给后面的comboMax用的。 -如果single path max小于0,那没有什么加到parent上面的意义,所以就被再次刷为0. -combo的三种情况:(root可能小于0)1. 只有left, 2。 只有右边。 3. root大于0,那么就left,right,curr全部加起来。 - 情况1和情况2去一个最大值, - 然后和情况三比较。 - 做了两个Math.max(). 然后就有了这一层的comboMax - - -12.11.2015 recap: - So totally, 5 conditions: - (save in single:) - left + curr.val - right + curr.val - (save in combo:) - left, - right, - left + curr.val + right +H +1526524522 +tags: Tree, DFS, DP, Tree DP + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything +/* +private class PathSum { + int singlePathMax; + int combinedPathMax; + PathSum(int singlePathMax, int combinedPathMax) { + this.singlePathMax = singlePathMax; + this.combinedPathMax = combinedPathMax; + } +} +*/ + +#### Previous Notes +##### Note1 +- 用 PathSum 比较特别. 没有 data structure的时候, 写起来比较繁琐. +- 第一次做有点难理解,复杂原因是:因为可能有负值啊。不能乱assume正数。 +- single path max 的计算是为了给后面的comboMax用的。 +- 如果single path max小于0,那没有什么加到parent上面的意义,所以就被再次刷为0. +- combo的三种情况:(root可能小于0) +- 1. 只有left +- 2. 只有right +- 3. root大于0,那么就left,right,curr全部加起来。 +- 情况1和情况2取一个最大值,然后和情况三比较。做了两个Math.max(). 然后就有了这一层的comboMax + +##### Note2 +- 12.11.2015 recap +- totally 5 conditions: +- (save in single): left + curr.val OR right + curr.val +- (save in combo):left, right, OR left + curr.val + right + ``` /* -23% Accepted Given a binary tree, find the maximum path sum. + The path may start and end at any node in the tree. + + Example -Given the below binary tree, - 1 - / \ - 2 3 -Return 6. +Given the below binary tree: + + 1 + / \ +2 3 +return 6. + Tags Expand -Dynamic Programming Tree Depth First Search +Divide and Conquer Dynamic Programming Recursion */ @@ -46,7 +80,68 @@ * } * } */ +// DP, DFS, 99.73% +public class Solution { + int max = Integer.MIN_VALUE; + + public int maxPathSum(TreeNode root) { + maxPathDown(root); + return max; + } + + private int maxPathDown(TreeNode node) { + if (node == null) return 0; + int left = Math.max(0, maxPathDown(node.left)); // left single path + int right = Math.max(0, maxPathDown(node.right)); // right single path + max = Math.max(max, left + right + node.val); // compare combo max with global max + return Math.max(left, right) + node.val; // always return the max single continuous path + } +} +// Use a customized object to track single path, v.s. combo path +public class Solution { + // Class used to carry sum in recursive dfs + private class PathSum { + int singlePathMax; + int combinedPathMax; + PathSum(int singlePathMax, int combinedPathMax) { + this.singlePathMax = singlePathMax; + this.combinedPathMax = combinedPathMax; + } + } + + // Goal: find the combined path value, if applicable + public int maxPathSum(TreeNode root) { + PathSum result = dfs(root); + return result.combinedPathMax; + } + + public PathSum dfs(TreeNode root) { + if (root == null) { + return new PathSum(0, Integer.MIN_VALUE); + } + //Divide + PathSum left = dfs(root.left); + PathSum right = dfs(root.right); + + //Conquer + + //Step 1: build single path max (continuous path sum including curr root) + int singlePathMax = Math.max(left.singlePathMax, right.singlePathMax) + root.val; + //If less than 0, set to 0. It'll be dropped for combinedPathMax, so leave it to 0. + singlePathMax = Math.max(singlePathMax, 0); + + //Step2: build combined path max. + // Compare leftChild.combo vs. rightChild.combo + int combinedPathMax = Math.max(left.combinedPathMax, right.combinedPathMax); + // Compare with left.single + right.single + root.val + combinedPathMax = Math.max(combinedPathMax, left.singlePathMax + right.singlePathMax + root.val); + + // Return the summary for the current node. + return new PathSum(singlePathMax, combinedPathMax); + } + +} /* @@ -95,8 +190,9 @@ public PathSumType helper(TreeNode root) { int singlePathMax = Math.max(left.singlePathMax, right.singlePathMax) + root.val; singlePathMax = Math.max(singlePathMax, 0);//If less than 0, no need to keep, because it only decrease parent-level max. - //first comparison: does not include curr node at all(this would be applicable when curr.val < 0, so we take this condition into account) + //first comparison: does not include root node at all(this would be applicable when curr.val < 0, so we take this condition into account) int combinedPathMax = Math.max(left.combinedPathMax, right.combinedPathMax); + //second comparison: combinedPathMax = Math.max(combinedPathMax, left.singlePathMax + right.singlePathMax + root.val); return new PathSumType(singlePathMax, combinedPathMax); @@ -104,6 +200,4 @@ public PathSumType helper(TreeNode root) { } - - ``` \ No newline at end of file diff --git a/Java/Binary Tree Paths.java b/Java/Binary Tree Paths.java old mode 100644 new mode 100755 index f2e8730..85e4d17 --- a/Java/Binary Tree Paths.java +++ b/Java/Binary Tree Paths.java @@ -1,7 +1,26 @@ -Recursive:分叉。Helper。 +E +1519797926 +tags: Binary Tree, DFS, Backtracking + +给一个binary tree, 返回所有root-to-leaf path + +#### DFS, backtracking +- Find all paths, bfs/dfs all works. dfs will be simplier to write +- Recursive:分叉. dfs. +- top->bottom: enumerate current node into the list, carry to next level, and backtrack +- top->bottom is trivial to consider: path flows from top->bottom + +#### DFS, bottom->up +- We can also take current node.left or node.right to generate list of results from the subproblem +- let dfs return list of string candidates, and we can run pair the list with currenet node, once they come back. +- TODO: can write code to practice + +#### Iterative +- Iterative, 非递归练习了一下 +- 因为要每次切短list, 所以再加了一个Stack 来存level +- 单这道题用dfs更简单, 因为找的就是从头到尾的path, 是dfs的pattern + -非递归练习了一下 -因为要每次切短list, 所以再加了一个Stack 来存level ``` /* Binary Tree Paths @@ -26,7 +45,82 @@ Binary Tree Binary Tree Traversal Facebook Google */ +// cleaner: +class Solution { + public List binaryTreePaths(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) { + return rst; + } + dfs(rst, new ArrayList<>(), root); + return rst; + } + + public void dfs(List rst, List list, TreeNode node) { + if (node == null) return; + + list.add(node.val); + if (node.left == null && node.right == null) { + rst.add(convert(list)); + list.remove(list.size() - 1); + return; + } + + dfs(rst, list, node.left); + dfs(rst, list, node.right); + list.remove(list.size() - 1); + } + + private String convert(List list) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < list.size() - 1; i++) { + sb.append(list.get(i) + "->"); + } + sb.append(list.get(list.size() - 1)); + return sb.toString(); + } +} + /* +Basic dfs, pass along sb, List. +Save when root == null. +*/ +class Solution { + public List binaryTreePaths(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) { + return rst; + } + dfs(rst, new ArrayList<>(), root); + return rst; + } + + public void dfs(List rst, List list, TreeNode node) { + list.add(node.val); + if (node.left == null && node.right == null) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < list.size() - 1; i++) { + sb.append(list.get(i) + "->"); + } + sb.append(list.get(list.size() - 1)); + rst.add(sb.toString()); + return; + } + + if (node.left != null) { + dfs(rst, list, node.left); + list.remove(list.size() - 1); + } + if (node.right != null) { + dfs(rst, list, node.right); + list.remove(list.size() - 1); + } + } +} + + +/* +Previous notes Thoughts: Recursive, need a helper (root, arraylist list, List) Add curr. @@ -39,42 +133,6 @@ Recursive, need a helper (root, arraylist list, List) time: 2715m */ -public class Solution { - /** - * @param root the root of the binary tree - * @return all root-to-leaf paths - */ - public List binaryTreePaths(TreeNode root) { - List rst = new ArrayList(); - if (root == null) { - return rst; - } - helper(root, rst, new ArrayList()); - return rst; - } - - public void helper(TreeNode root, List rst, ArrayList list){ - list.add(root.val); - if (root.left == null && root.right == null) { - StringBuffer sb = new StringBuffer(); - for (int i = 0; i < list.size() - 1; i++) { - sb.append(list.get(i) + "->"); - } - sb.append(list.get(list.size() - 1)); - rst.add(sb.toString()); - } - if (root.left != null) { - helper(root.left, rst, list); - list.remove(list.size() - 1); - } - if (root.right != null) { - helper(root.right, rst, list); - list.remove(list.size() - 1); - } - } -} - - /* Iterative: Use a stack. Note. Process push curr, right, left @@ -127,10 +185,6 @@ public List binaryTreePaths(TreeNode root) { return rst; } } - - - - /** * Definition of TreeNode: * public class TreeNode { @@ -143,13 +197,4 @@ public List binaryTreePaths(TreeNode root) { * } */ - - - - - - - - - -``` \ No newline at end of file +``` diff --git a/Java/Binary Tree Postorder Traversal.java b/Java/Binary Tree Postorder Traversal.java old mode 100644 new mode 100755 index 795367c..0c706c2 --- a/Java/Binary Tree Postorder Traversal.java +++ b/Java/Binary Tree Postorder Traversal.java @@ -1,9 +1,28 @@ -最prefer 2 stack的做法。特别清楚。 -stack1和stack2合作。 -记得这个做法。。。挺神奇的。 +M +1521692416 +tags: Stack, Two Stacks, Tree -Divide and Conquer 的方法也非常明了! +如题, POST-ORDER traversal. +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Recursive +trivial, 先加left recursively, 再加right recursively, 然后组成头部. + +#### Stack +- 双stack的思想, 需要在图纸上画一画 +- 原本需要的顺序是: 先leftChild, rightChild, currNode. +- 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild +- 这样出来的结果是reverse的, 那么翻转一下就可以了. +- v1做的时候用了stack1, stack2, 因为根据这个双stack的思想而来 +- v2简化, 可以放在一个stack里面, 每次record result 的时候: rst.add(0, item); + +##### 利用stack的特点 +- 每次加element进stack的时候, 想要在 bottom/后process的, 先加 +- 想要下一轮立刻process的, 最后push进stack. + +##### 注意 +这些binary tree traversal的题目.常常有多个做法:recursive or iterative ``` /* @@ -30,70 +49,79 @@ */ -/* - -Thinking process: -1. Resursive: (divide and conquer) - rec on left node - rec on right node - rst.addAll(left) - rst.addAll(right) - rst.add(curr) - -*/ -public class Solution { - /** - * @param root: The root of binary tree. - * @return: Postorder in ArrayList which contains node values. - */ - public ArrayList postorderTraversal(TreeNode root) { - ArrayList rst = new ArrayList(); +// Recursive: always add left, add right, then add middle +class Solution { + public List postorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); if (root == null) { return rst; } - //Recursive: - ArrayList right = postorderTraversal(root.right); - ArrayList left = postorderTraversal(root.left); - rst.addAll(left); - rst.addAll(right); - rst.add(root.val); + rst.addAll(postorderTraversal(root.left)); + rst.addAll(postorderTraversal(root.right)); + rst.add(root.val); + return rst; } } - /* - 2. Non-recursive, interative - use 2 stacks: pull water from s1 into s2 - in s2, we want: at each level, parentNode at bottom, then rightNode, then leftNode - loop through s2, then we print out leftNode, rightNode, parentNode ... in postOrder. +Iteratively: stack +V1 +it's cleaner to draw two stacks and simulate the add proces on paper +- stack2 is a placeholder with reversed post-order. It'll be used to generate final results. +- Item in stack1 will be added with reversed order: left, right +- Always save the parent node to stack2: it enters stack2 first than its 2 children, so it'll be at bottom +- Next round: since right child is on top, it'll be processed first and added to stack2 */ -public class Solution { - /** - * @param root: The root of binary tree. - * @return: Postorder in ArrayList which contains node values. - */ - public ArrayList postorderTraversal(TreeNode root) { - ArrayList rst = new ArrayList(); +class Solution { + public List postorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); if (root == null) { return rst; } - //Non-recursive: - Stack s1 = new Stack(); - Stack s2 = new Stack(); - s1.push(root); - while (!s1.empty()) { - TreeNode curr = s1.pop(); - s2.push(curr); - if (curr.left != null) { - s1.push(curr.left); + Stack stack1 = new Stack<>(); + Stack stack2 = new Stack<>(); + stack1.push(root); + + while (!stack1.isEmpty()) { + TreeNode node = stack1.pop(); + stack2.push(node); + if (node.left != null) { + stack1.push(node.left); } - if (curr.right != null) { - s1.push(curr.right); + if (node.right != null) { + stack1.push(node.right); } } - while (!s2.empty()) { - rst.add(s2.pop().val); + + while (!stack2.isEmpty()) { + rst.add(stack2.pop().val); + } + + return rst; + } +} + +// Simpler version, add to begining of list. +// V2 +class Solution { + public List postorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) { + return rst; + } + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + rst.add(0, node.val); + if (node.left != null) { + stack.push(node.left); + } + if (node.right != null) { + stack.push(node.right); + } } return rst; } @@ -132,11 +160,4 @@ public void helper(ArrayListrst, TreeNode node) { } - - - - - - - ``` \ No newline at end of file diff --git a/Java/Binary Tree Preorder Traversal.java b/Java/Binary Tree Preorder Traversal.java old mode 100644 new mode 100755 index f2cc535..94d1479 --- a/Java/Binary Tree Preorder Traversal.java +++ b/Java/Binary Tree Preorder Traversal.java @@ -1,7 +1,16 @@ -Preorder 写写, stack -1. Divide and conquer -2. Stack(NON-recursive) push curr, push right, push left. -3. recursive with helper method +E +1522009726 +tags: Stack, Tree, DFS, BFS + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + ``` /* Given a binary tree, return the preorder traversal of its nodes' values. @@ -30,7 +39,55 @@ */ /* +Thoughts: +DFS, add root, then left, then right +*/ +class Solution { + public List preorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) { + return rst; + } + rst.add(root.val); + rst.addAll(preorderTraversal(root.left)); + rst.addAll(preorderTraversal(root.right)); + return rst; + } +} + +/* +Thoughts: +BFS. Want the sequence in root, left, and right. +Queue? NO. After root.left is process, it should go to root.left.left. rather than root.right. +We need to proces root, then put root.right at bottom, stack root.left on top, then work on root.left's children first. +*/ +class Solution { + public List preorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) { + return rst; + } + Stack stack = new Stack<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + rst.add(node.val); + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + } + + return rst; + } +} + +/* + Previous notes Recap: 12.08.2015 Draw a few nodes and will realize to use stack Cannot use queue, because whatever added on it first, will first process. @@ -38,7 +95,7 @@ IN FACT: binary tree traversal are all using stack... */ -//Itereative +//Iterative public class Solution { public ArrayList preorderTraversal(TreeNode root) { @@ -80,90 +137,4 @@ public void helper(ArrayListrst, TreeNode node){ } } - - - -/* -Thinking process: -Check if root is null -use a container to save results - -use current node -put right on stack -put left on stack -4. In next run, the ‘left’ will be on top of stack, and will be taken first. So the order becomes: parent -> left -> right - -*/ -/** - * Definition of TreeNode: - * public class TreeNode { - * public int val; - * public TreeNode left, right; - * public TreeNode(int val) { - * this.val = val; - * this.left = this.right = null; - * } - * } - */ -public class Solution { - /** - * @param root: The root of binary tree. - * @return: Preorder in ArrayList which contains node values. - */ - public ArrayList preorderTraversal(TreeNode root) { - Stack stack = new Stack(); - ArrayList result = new ArrayList(); - //Check top - if (root == null) { - return result; - } - //save root - stack.push(root); - //add to result, and load on stack. Right-side at the bottom - while (!stack.empty()) { - TreeNode node = stack.pop(); - result.add(node.val); - if (node.right != null) { - stack.push(node.right); - } - if (node.left != null) { - stack.push(node.left); - } - }//while - - return result; - } -} - - - -//Divide and Conquer - recursive -/* -Check root == null? -Dive them into 2 recursive calls: get result from left, get result from right -Conquer - add all of the results together and return. As the pre-order defines: -add current parent -add left nodes -add right nodes. -*/ - - //Divide and conquer - public ArrayList preorderTraversal(TreeNode root) { - // write your code here - ArrayList result = new ArrayList(); - - if (root == null) { - return result; - } - ArrayList left = preorderTraversal(root.left); - ArrayList right = preorderTraversal(root.right); - - result.add(root.val); - result.addAll(left); - result.addAll(right); - - return result; - } - - ``` \ No newline at end of file diff --git a/Java/Binary Tree Right Side View.java b/Java/Binary Tree Right Side View.java new file mode 100755 index 0000000..b0d65e9 --- /dev/null +++ b/Java/Binary Tree Right Side View.java @@ -0,0 +1,217 @@ +M +1526769889 +tags: Tree, DFS, BFS + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右:即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list + +#### DFS +- Use Map 来存每一个level的结果 +- dfs function 里, 如果 input depth 不存在, 就add to map. +- dfs function 里面先: dfs(node.right), 然后 dfs(node.left) +- 由于always depth search on right side, 所以map会被right branch populate; 然后才是 leftChild.right + + +``` +/* +Given a binary tree, imagine yourself standing on the right side of it, +return the values of the nodes you can see ordered from top to bottom. + +Example: + +Input: [1,2,3,null,5,null,4] +Output: [1, 3, 4] +Explanation: + + 1 <--- + / \ +2 3 <--- + \ \ + 5 4 <--- +*/ + +/* +right side view: +- the tree may not be complete +- always find right-most. if right child not available, dfs into left child +- tracking back is hard for dfs +- bfs: on each level, record the last item of the queue +*/ + +class Solution { + public List rightSideView(TreeNode root) { + List result = new ArrayList<>(); + // check edge case + if (root == null) { + return result; + } + + // init queue, result list + Queue queue = new LinkedList<>(); + queue.offer(root); + + // loop over queue with while loop; inner while loop to complete level + while (!queue.isEmpty()) { + int size = queue.size(); + while (size > 0) { + size--; + TreeNode node = queue.poll(); + if (size == 0) { + result.add(node.val); + } + if(node.left != null) queue.offer(node.left); + if(node.right != null) queue.offer(node.right); + } + } + return result; + } +} + + +/* +DFS: mark each level with map +1. dfs right side first, then left side at each level +2. if candidate not exist, add to map, if exist, skip. +*/ + +class Solution { + public List rightSideView(TreeNode root) { + List result = new ArrayList<>(); + // check edge case + if (root == null) { + return result; + } + // init map, dfs + Map map = new HashMap<>(); + int depth = dfs(map, root, 0); + // output result + for (int i = 0; i <= depth; i++) { + if (map.containsKey(i)) + result.add(map.get(i)); + } + return result; + } + + private int dfs(Map map, TreeNode node, int depth) { + if(node == null) { + return 0; + } + if (!map.containsKey(depth)) { + map.put(depth, node.val); + } + int rightDepth = dfs(map, node.right, depth + 1); + int leftDepth = dfs(map, node.left, depth + 1); + return Math.max(leftDepth, rightDepth) + depth; + } +} + + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +/* +02.09.2016 Revist +Thoughts: +BFS: traverse all levels, save to ArrayList, get all nodes at end of level list. +*/ + +public class Solution { + public List rightSideView(TreeNode root) { + List rst = new ArrayList(); + if (root == null) { + return rst; + } + Queue queue = new LinkedList(); + queue.offer(root); + int size = queue.size(); + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + size--; + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + if (size == 0) { + rst.add(node.val); + size = queue.size(); + } + } + + return rst; + } +} + + +/* + +自己想了这个方法,有可能不是特别efficient. +一个queue放普通的BFS。 +一个queue放level。 +同时维护一个parent value;维护一个跟着BFS跑的level。 +每个node都有一个lv。一旦lv和正在跑的level不一样,证明lv>level,那么也就是说,刚刚换行拉。parent的值,就是上一行最右边的值。DONE. + + +Thoughts: +Use 2 queue: one for BFS, one for level. Each node in queue has a corresponding level +Track level. +WHen level != levelQ.poll(), that means we are moving to next level, and we should record the previous(parent) node's value. +*/ + +public class Solution { + public List rightSideView(TreeNode root) { + List rst = new ArrayList(); + if (root == null) { + return rst; + } + Queue q = new LinkedList(); + Queue levelQ = new LinkedList(); + q.offer(root); + levelQ.offer(1); + int level = 1; + int parent = root.val; + TreeNode node = null; + + while (!q.isEmpty()) { + node = q.poll(); + int lv = levelQ.poll(); + if (level != lv) { + level++; + rst.add(parent); + } + parent = node.val; + if (node.left != null) { + q.offer(node.left); + levelQ.offer(lv + 1); + } + if (node.right != null) { + q.offer(node.right); + levelQ.offer(lv + 1); + } + }//END while + rst.add(parent); + return rst; + } +} + + + + + + + + + + +``` \ No newline at end of file diff --git a/Java/Binary Tree Vertical Order Traversal.java b/Java/Binary Tree Vertical Order Traversal.java new file mode 100644 index 0000000..a7a68bf --- /dev/null +++ b/Java/Binary Tree Vertical Order Traversal.java @@ -0,0 +1,213 @@ +M +1532015904 +tags: BFS, DFS, Hash Table, Tree +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + +``` +/* +Given a binary tree, return the vertical order traversal of its nodes' values. +(ie, from top to bottom, column by column). + +If two nodes are in the same row and column, the order should be from left to right. + +Examples 1: + +Input: [3,9,20,null,null,15,7] + + 3 + /\ + / \ + 9 20 + /\ + / \ + 15 7 + +Output: + +[ + [9], + [3,15], + [20], + [7] +] +Examples 2: + +Input: [3,9,8,4,0,1,7] + + 3 + /\ + / \ + 9 8 + /\ /\ + / \/ \ + 4 01 7 + +Output: + +[ + [4], + [9], + [3,0,1], + [8], + [7] +] +Examples 3: + +Input: [3,9,8,4,0,1,7,null,null,null,2,5] (0's right child is 2 and 1's left child is 5) + + 3 + /\ + / \ + 9 8 + /\ /\ + / \/ \ + 4 01 7 + /\ + / \ + 5 2 + +Output: + +[ + [4], + [9,5], + [3,0,1], + [8,2], + [7] +] +*/ + + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +/* +BFS: naturally level-traverse all nodes, add node to appropriate col list +Use min/max to track map keys, since it's continous +*/ +class Solution { + class Node { + int col; + TreeNode treeNode; + public Node (int col, TreeNode treeNode) { + this.col = col; + this.treeNode = treeNode; + } + } + public List> verticalOrder(TreeNode root) { + List> rst = new ArrayList<>(); + if (root == null) return rst; + + int min = 0, max = 0; + Map> map = new HashMap<>(); + Queue queue = new LinkedList<>(); + queue.offer(new Node(0, root)); + + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + Node node = queue.poll(); + map.putIfAbsent(node.col, new ArrayList<>()); + map.get(node.col).add(node.treeNode.val); + + if (node.treeNode.left != null) queue.offer(new Node(node.col - 1, node.treeNode.left)); + if (node.treeNode.right != null) queue.offer(new Node(node.col + 1, node.treeNode.right)); + + min = Math.min(min, node.col); + max = Math.max(max, node.col); + } + } + + for (int col = min; col <= max; col++) { + rst.add(map.get(col)); + } + + return rst; + } + +} + +/* +Use Map> to track cols +dfs: add curr at col, dfs(col-1, node.left), dfs(col+1, node.right); +at end: sort map.keySet, and out + +DFS can fail, if we assume `node.left subtree` always appears on top of `node.right subtree` +Therefore, between each column, node should have order as well: level +*/ +class Solution { + class Node { + int val, level; + public Node (int val, int level) { + this.val = val; + this.level = level; + } + } + public List> verticalOrder(TreeNode root) { + List> rst = new ArrayList<>(); + if (root == null) return rst; + + Map> map = new HashMap<>(); + map.putIfAbsent(0, new ArrayList<>()); + map.get(0).add(new Node(root.val, 0)); + dfs(map, root, 0, 0); + + List keys = new ArrayList<>(map.keySet()); + Collections.sort(keys); + + for (int key : keys) { + List nodes = map.get(key); + nodes.sort(Comparator.comparing(a -> a.level)); + List col = new ArrayList<>(); + for (Node node : nodes) { + col.add(node.val); + } + rst.add(col); + } + + return rst; + } + + public void dfs(Map> map, TreeNode node, int col, int level) { + if (node == null) return; + if (node.left != null) { + map.putIfAbsent(col - 1, new ArrayList<>()); + map.get(col - 1).add(new Node(node.left.val, level + 1)); + } + if (node.right != null) { + map.putIfAbsent(col + 1, new ArrayList<>()); + map.get(col + 1).add(new Node(node.right.val, level + 1)); + } + + dfs(map, node.left, col - 1, level + 1); + dfs(map, node.right, col + 1, level + 1); + } +} +``` \ No newline at end of file diff --git a/Java/Binary Tree Zigzag Level Order Traversal.java b/Java/Binary Tree Zigzag Level Order Traversal.java old mode 100644 new mode 100755 index fda05d8..fce6957 --- a/Java/Binary Tree Zigzag Level Order Traversal.java +++ b/Java/Binary Tree Zigzag Level Order Traversal.java @@ -1,5 +1,18 @@ -/* 24% Accepted -Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between). +M +1531703331 +tags: Stack, Tree, BFS +time: O(n) +space: O(n) + +#### Queue +- 简单的level traversal.根据level奇数偶数而add到不同位子. +- Option1: based on level % 2, insert to front/end of list +- Option2: based on level, insert right/left of node into queue + +``` +/* +Given a binary tree, return the zigzag level order traversal of its nodes' values. +(ie, from left to right, then right to left for the next level and alternate between). Example Given binary tree {3,9,20,#,#,15,7}, @@ -21,11 +34,6 @@ Tags Expand Tree Search Breadth First Search Queue Binary Tree -Thinking Process: -1. realize: queue is no longer can be used. draw a example map to see why. -Instead, use 2 stacks. -Because we can only take the top of stack, and we are constantly adding to the top of the stac, so we need 2 stacks. One is the current one, will be empty every time when we finish the level. The other one is nextLevel, which holds next level’s nodes temporarily. -2. Use a boolean to track if which level it’s running at. */ /** @@ -39,8 +47,47 @@ * } * } */ + +//BFS. first level = 0; level % 2 = 1, list.add(0, ...) +public class Solution { + public List> zigzagLevelOrder(TreeNode root) { + List> rst = new ArrayList<>(); + if (root == null) return rst; + + int level = 0, size = 0; + Queue queue = new LinkedList<>(); + queue.offer(root); + + while (!queue.isEmpty()) { + size = queue.size(); + ArrayList list = new ArrayList<>(); + for (int i = 0 ; i < size; i++) { + TreeNode node = queue.poll(); + list.add(node.val); + if (level % 2 == 1) addChild(queue, node.left, node.right); + else addChild(queue, node.right, node.left); + } + level++; + rst.add(list); + } + + return rst; + } + + private void addChild(Queue queue, TreeNode nodeA, TreeNode nodeB) { + if (nodeA != null) queue.offer(nodeA); + if (nodeB != null) queue.offer(nodeB); + } +} + /* +Thought: +1. realize: queue is no longer can be used. draw a example map to see why. +Instead, use 2 stacks. +Because we can only take the top of stack, and we are constantly adding to the top of the stac, so we need 2 stacks. One is the current one, will be empty every time when we finish the level. The other one is nextLevel, which holds next level’s nodes temporarily. +2. Use a boolean to track if which level it’s running at. + */ public class Solution { /** * @param root: The root of binary tree. @@ -89,3 +136,5 @@ public void addLevel(Stack level, TreeNode node) { } } + +``` diff --git a/Java/Bomb Enemy.java b/Java/Bomb Enemy.java new file mode 100644 index 0000000..0b092a9 --- /dev/null +++ b/Java/Bomb Enemy.java @@ -0,0 +1,122 @@ +M +1517296407 +tags: DP, Coordinate DP + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Corrdinate DP +- Space, Time: O(MN) +- dp[i][j] 就是(i, j)上最多能炸掉的enemy数量 +- dp[i][j] 需要从4个方向加起来, 也就是4个方向都要走一遍, 所以分割成 UP/Down/Left/Right 4个 int[][] +- 最后一步的时候求max +- 分方向考虑的方法很容易想到,但是四个方向移动的代码比较繁琐. +- 往上炸: 要从顶向下考虑 +- 往下炸: 要从下向上考虑 +- 熟练写2D array index 的变换. + +似乎还有一个更简洁的方法, 用col count array: http://www.cnblogs.com/grandyang/p/5599289.html + +``` +/* +Given a 2D grid, each cell is either a wall 'W', an enemy 'E' or empty '0' (the number zero), +return the maximum enemies you can kill using one bomb. +The bomb kills all the enemies in the same row and column from the planted point +until it hits the wall since the wall is too strong to be destroyed. +Note that you can only put the bomb at an empty cell. + +Example: +For the given grid + +0 E 0 0 +E 0 W E +0 E 0 0 + +return 3. (Placing a bomb at (1,1) kills 3 enemies) +*/ + +/* +Thoughts: +It goes off towards 4 difference directions: UP/DOWN/LEFT/RIGHT +Normally: we could traverse the 2D map, use each point to go 4 directions: O(MN*(M+N)) ~ O(MN^2) or O(NM^2) +Need to optimize: standing on any point, the 4 directions are likely to be calculated in earlier time. +Consider UP case: +up[i][j]: the # of bombed enemy at (i,j) can be: +1. up[i-1][j], if grid[i-1][j]== '0' +2. up[i-1][j] + 1 if grid[i-1][j]== 'E' +3. 0, if grid[i-1][j]== 'W' + +We'll sum up UP/DOWN/LEFT/RIGHT at the end. During initialize of the 4 directions, ignore 'W'. + +dp[i][j] = UP[i][j] + DOWN[i][j] + LEFT[i][j] + RIGHT[i][j]. + +*/ +class Solution { + public int maxKilledEnemies(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int m = grid.length; + int n = grid[0].length; + int[][] up = new int[m][n]; + int[][] down = new int[m][n]; + int[][] left = new int[m][n]; + int[][] right = new int[m][n]; + + // UP + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] != 'W') { + up[i][j] = grid[i][j] == 'E' ? 1 : 0; + up[i][j] += i - 1 >= 0 ? up[i - 1][j] : 0; + } + } + } + + // DOWN + for (int i = m - 1; i >= 0; i--) { + for (int j = 0; j < n; j++) { + if (grid[i][j] != 'W') { + down[i][j] = grid[i][j] == 'E' ? 1 : 0; + down[i][j] += i + 1 < m ? down[i + 1][j] : 0; + } + } + } + + // LEFT + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] != 'W') { + left[i][j] = grid[i][j] == 'E' ? 1 : 0; + left[i][j] += j - 1 >= 0 ? left[i][j - 1] : 0; + } + } + } + + // RIGHT + for (int i = 0; i < m; i++) { + for (int j = n - 1; j >= 0; j--) { + if (grid[i][j] != 'W') { + right[i][j] = grid[i][j] == 'E' ? 1 : 0; + right[i][j] += j + 1 < n ? right[i][j + 1] : 0; + } + } + } + + + // DP + int max = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == '0') { + max = Math.max(max, up[i][j] + down[i][j] + left[i][j] + right[i][j]); + } + } + } + return max; + } +} + + +``` \ No newline at end of file diff --git a/Java/Brick Wall.java b/Java/Brick Wall.java new file mode 100644 index 0000000..0eb130d --- /dev/null +++ b/Java/Brick Wall.java @@ -0,0 +1,81 @@ +M +1531811136 +tags: Hash Table +time: O(mn) +space: O(X), X = max wall width + +给一面墙, 每一行是一行bricks. 用一条vertical line 扫描, 会vertically割开brink. 找到割开最少brick的那条线的x index. + +#### Hash Table +- Find the vertical line (x-coordinate of the grid), where most gaps are found. +- Each gap has (x,y) coordinate +- Create `map`, and maintain a max occurance. +- 计算: x-coordinate: `x = 0; x += brick[i] width` +- Eventually: min-crossed bricks = wall.lenght - maxOccurrance + +##### 思想 +- 分析题意, 找到题目的目标 +- 虽然Map自己不能有sort的规律, 但是可以maintain global variable + +``` +/* +There is a brick wall in front of you. The wall is rectangular and has several rows of bricks. +The bricks have the same height but different width. +You want to draw a vertical line from the top to the bottom and cross the least bricks. + +The brick wall is represented by a list of rows. Each row is a list of integers +representing the width of each brick in this row from left to right. + +If your line go through the edge of a brick, then the brick is not considered as crossed. +You need to find out how to draw the line to cross the least bricks and return the number of crossed bricks. + +You cannot draw a line just along one of the two vertical edges of the wall, +in which case the line will obviously cross no bricks. + +Example: +Input: +[[1,2,2,1], + [3,1,2], + [1,3,2], + [2,4], + [3,1,2], + [1,3,1,1]] +Output: 2 +Explanation: +// image missing, https://leetcode.com/problems/brick-wall/description/ + +Note: +The width sum of bricks in different rows are the same and won't exceed INT_MAX. +The number of bricks in each row is in range [1,10,000]. +The height of wall is in range [1,10,000]. +Total number of bricks of the wall won't exceed 20,000. + +*/ + +// Hasp map of frequency, maintain global variable +class Solution { + public int leastBricks(List> wall) { + if (validate(wall)) { + return 0; + } + + int max = 0; + Map map = new HashMap<>(); + // build map, and maintain max + for (List row : wall) { + int x = 0; + for (int i = 0; i < row.size() - 1; i++) { + x += row.get(i); + map.putIfAbsent(x, 0); + map.put(x, map.get(x) + 1); + max = Math.max(max, map.get(x)); + } + } + return wall.size() - max; + } + + private boolean validate(List> wall) { + return wall == null || wall.size() == 0 || wall.get(0) == null || wall.get(0).size() == 0; + } +} +``` \ No newline at end of file diff --git a/Java/Bricks Falling When Hit.java b/Java/Bricks Falling When Hit.java new file mode 100644 index 0000000..88fcc41 --- /dev/null +++ b/Java/Bricks Falling When Hit.java @@ -0,0 +1,222 @@ +H +1532820928 +tags: Union Find + +给一个matrix of 1 and 0, `1` 代表brick. 连着ceiling的brick就不会drop. 给一串coordinate hits[][], 记录每次take down 1 brick 后, 会drop多少个. + +#### UnionFind +- 1. 我们知道大部分的brick可能都是连着ceiling, 所以每次正向检查都traverse all and timeout +- 2. 能否用union, 把connect都装在一起, 然后drop brick的时候把连着的都drop掉? 难: 因为还是要check所有brick当下的status. +- 受其他人的解答启发, 由于是计算count,我们可以`反向考虑`: +- 把hit-brick全部mark=2 (就当舍弃不算), 观察整个局面的最后一步, 先把所有还连着ceiling的brick算一下总数, 统计在unionFind的 全部统计在count[0] 里面. +- 剩下的不连着ceiling的也就是一个个isolated island +- 做法: 把hit-brick 一个个加回去, 然后再做一次union, 看看最终连到ceiling的有多少个. 增加的count, 就是正向思考时 dropped brick 数量! + +##### Union Find 变种 +- 还是用数字index做union find, 但是把每一个index都+1, 右移一位, 而[0]留下来做特殊用途: +- 用union at 0来 统计总共的remain count of ceiling-connected bricks, where `x = 0`. +- 如果在其他其他题目种, 条件可能就不是`x=0`, 但也可以用这个 union index = 0 来做一个root的统计 +- 关键: 把最后一个hit brick加回去, 然后再重新union一下这个hit-brick周围: count增加的变化, 不就是缺少hit-brick时候掉下去的数量. + + +#### DFS (timeout) +- 考虑每个hit的四周, 全部traverse, 没有连着ceiling就全部: +- 比如是 200 x 200 的 全部是1的matrix, 任何一次traverse都要到顶; 重复计算, 所以timeout +- 算法是没错, 但是不efficient. +- 想要减少重复计算, 但是又不能提前计算: grid在不断变化. 所以看能不能把连着ceiling的都group起来, 可以O(1)快速check? + + +``` +/** +We have a grid of 1s and 0s; the 1s in a cell represent bricks. +A brick will not drop if and only if it is directly connected to the top of the grid, +or at least one of its (4-way) adjacent bricks will not drop. + +We will do some erasures sequentially. Each time we want to do the erasure at the location (i, j), +the brick (if it exists) on that location will disappear, +and then some other bricks may drop because of that erasure. + +Return an array representing the number of bricks that will drop after each erasure in sequence. + +Example 1: +Input: +grid = [[1,0,0,0],[1,1,1,0]] +hits = [[1,0]] +Output: [2] +Explanation: +If we erase the brick at (1, 0), the brick at (1, 1) and (1, 2) will drop. So we should return 2. +Example 2: +Input: +grid = [[1,0,0,0],[1,1,0,0]] +hits = [[1,1],[1,0]] +Output: [0,0] +Explanation: +When we erase the brick at (1, 0), the brick at (1, 1) has already disappeared due to the last move. +So each erasure will cause no bricks dropping. +Note that the erased brick (1, 0) will not be counted as a dropped brick. + + +Note: + +The number of rows and columns in the grid will be in the range [1, 200]. +The number of erasures will not exceed the area of the grid. +It is guaranteed that each erasure will be different from any other erasure, and located inside the grid. +An erasure may refer to a location with no brick - if it does, no bricks drop. + +*/ + +/** +- mark hit-brick elements +- union the rest with unionAround +- process +*/ +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + int[] count; + int[] parent; + + public int[] hitBricks(int[][] grid, int[][] hits) { + + int m = grid.length, n = grid[0].length; + buildUnionFind(m * n + 1); + + // mark hit-brick + for (int i = 0; i < hits.length; i++) { + if (grid[hits[i][0]][hits[i][1]] == 1) { + grid[hits[i][0]][hits[i][1]] = 2; + } + } + + // union all island, and accumulate count for ceiling-connected island + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + unionAround(grid, i, j); + } + } + } + + int[] rst = new int[hits.length]; + int currCount = count[find(0)]; // sum of remaining ceiling-connected brick count + for (int i = hits.length - 1; i >= 0; i--) { + int x = hits[i][0], y = hits[i][1]; + if (grid[x][y] == 2) { + unionAround(grid, x, y); + grid[x][y] = 1; + } + int newCount = count[find(0)]; + rst[i] = newCount > currCount ? newCount - currCount - 1 : 0; + currCount = newCount; + } + + return rst; + } + + private void unionAround(int[][] grid, int x, int y) { + int m = grid.length, n = grid[0].length; + + for (int i = 0; i < 4; i++) { + int nX = x + dx[i], nY = y + dy[i]; + if (!validate(grid, nX, nY)) continue; + union(x * n + y + 1, nX * n + nY + 1); // shift the save by 1 to reserve lot[0] + } + + if (x == 0) { // if ceiling, union to ceiling brick at index = 0 + union(x * n + y + 1, 0); + } + } + + /** Classic UnionFind functions */ + private void buildUnionFind(int size) { + parent = new int[size]; + count = new int[size]; + for (int i = 0; i < size; i++) { + parent[i] = i; + count[i] = 1; + } + } + + private int find(int x) { + if (x == parent[x]) { + return x; + } + return parent[x] = find(parent[x]); + } + + private void union(int x, int y) { + int parentX = find(x); + int parentY = find(y); + if (parentX != parentY) { + parent[parentX] = parentY; + count[parentY] += count[parentX]; // accumulate union size + } + } + + // validate border and equals 1 + private boolean validate(int[][] grid, int x, int y) { + return x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && grid[x][y] == 1; + } +} + + + +// correct algorithm, but not efficient +// times out: every hit, we need to traverse all the way to ceiling via dfs, which consumes too much time +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + public int[] hitBricks(int[][] grid, int[][] hits) { + // dfs towards 4 directions + // clean entire boolean[][] map, and count + int[] count = new int[hits.length]; + for (int i = 0; i < hits.length; i++) { + int x = hits[i][0], y = hits[i][1]; + grid[x][y] = 0; + for (int j = 0; j < 4; j++) { + Set visited = new HashSet<>(); + if(!dfs(grid, visited, x + dx[j], y + dy[j])) count[i] += flip(grid, visited); + } + } + + return count; + } + + private int flip(int[][] grid, Set visited) { + int count = 0; + for (String s : visited) { + String[] ss = s.split("@"); + int i = Integer.parseInt(ss[0]), j = Integer.parseInt(ss[1]); + if (grid[i][j] > 0) { // visited + grid[i][j] = 0; + count++; + } + } + return count; + } + + // generates a map of connected nodes. If any node reaches top, terminate dfs. + // return false: no ceiling reached + // each dfs starts at '1' + private boolean dfs(int[][] grid, Set visited, int x, int y) { + String key = x + "@" + y; + if (!validate(grid, x, y) || visited.contains(key)) return false; + + //TODO: check ceiling + if (x == 0 && grid[x][y] == 1) return true; + visited.add(key); + + if (dfs(grid, visited, x + 1, y) || dfs(grid, visited, x - 1, y) || + dfs(grid, visited, x, y + 1) || dfs(grid, visited, x, y - 1)) { + return true; + } + + return false; + } + + // validate border and equals 1 + private boolean validate(int[][] grid, int x, int y) { + return x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && grid[x][y] == 1; + } +} +``` \ No newline at end of file diff --git a/Java/Burst Balloons.java b/Java/Burst Balloons.java new file mode 100755 index 0000000..718a2b1 --- /dev/null +++ b/Java/Burst Balloons.java @@ -0,0 +1,203 @@ +H +1522042382 +tags: Divide and Conquer, DP, Interval DP, Memoization + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + +``` +/* +Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. +You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. +Here left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent. + +Find the maximum coins you can collect by bursting the balloons wisely. + +Note: +(1) You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them. +(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100 + +Example: + +Given [3, 1, 5, 8] + +Return 167 + + nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] + coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167 +Credits: +Special thanks to @peisi for adding this problem and creating all test cases. + +Hide Company Tags Google +Show Tags +Divide and Conquer Dynamic Programming + + +*/ + +/* +Thoughts: +Interval DP. Think about it: it's really hard to find which ballon burst first; +how about which ballon burst last? + +If it burst last, the value will be left * lastItem * right. +Now we just have to pick which one burst last? k in [i, j] + +Note that, we need the invisible wall on head and tail, so make sure creating dp at length of n+2 +dp[i][j] represent max value on range [i, j) + +Pick k in the middle: +dp[i][j] = dp[i][k] + dp[k][j] + nums[i] * nums[k] * nums[j]; +where: +dp[i][k]: range (i, k) +dp[k][j]: range (k, j) + +Time O(n^3) +Space O(n^2) +*/ +class Solution { + public int maxCoins(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] values = new int[n + 2]; + values[0] = 1; + values[n + 1] = 1; + // reassign new array + for (int i = 1; i <= n; i++) { + values[i] = nums[i - 1]; + } + + n = values.length; + int[][] dp = new int[n][n]; // dp[i][j] denotes the max value in range [i, j) + // Critical: iterate over RANGE: then come up with i and j; i <= n - len + for (int len = 3; len <= n; len++) { + for (int i = 0; i <= n - len; i++) { + int j = len + i - 1; + for (int k = i + 1; k < j; k++) { + dp[i][j] = Math.max(dp[i][j], dp[i][k] + dp[k][j] + values[i] * values[k] * values[j]); + } + } + } + + return dp[0][n - 1]; + } +} + + +/* + Thoughts: as seen in dicussion. Build DP. + State: + dp[i][j]: the number of max coins can collect between i and j. + For a position x in [i,j], where to burst it? So this goes into a divide and conquer method. + Burst at x, track the sum, and record the max into dp[i][j] + Function: + dp[i][j] = Math.max(dp[i][j], DP(i, x-1) + nums[x-1]*nums[x]*nums[x+1] + DP(x+1, j)) + Init: + create dp[n+2][n+2]. (from 0 to n+1) + dp[0][1] = 1; + dp[n][n+1] = 1; + Return: + dp[1][n] + + DP(int i, int j, int[][] dp) + + Need to redo that nums. +*/ + +/* +Thoughts: +Recursive solution with memoization. +Still define dp[i][j] to represent the max value in range (i, j) +dp[i][j] = Math.max(dp[i][j], dfs(i, k - 1) + dfs(k + 1, j) + values[i - 1] * values[k] * values[j + 1])); +*/ +class Solution { + int[][] dp; + int[] values; + public int maxCoins(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + dp = new int[n + 2][n + 2]; + + //Initialize new array + values = new int[n + 2]; + values[0] = values[n + 1] = 1; + for (int i = 1; i <= n; i++) { + values[i] = nums[i - 1]; + } + + return dfs(1, n); + } + + private int dfs(int i, int j) { + if (dp[i][j] > 0) { //momorization + return dp[i][j]; + } + for (int k = i; k <= j; k++) { + dp[i][j] = Math.max(dp[i][j], dfs(i, k - 1) + dfs(k + 1, j) + values[i - 1] * values[k] * values[j + 1]); + } + return dp[i][j]; + } +} + +/* + 用了recursive + memorization, 但是也可以用传统的DP,比如: + for (int length = 1; length < n; length++) [ + for (int = 0; i < n-1; i++) { + j = i + length; + if length == 1: + dp[i][j] = A[i] * A[j] + A[i] + else: + dp[i][j] = max {} + } + } + +*/ + + + +/* + My Thought: TOO COMPLEX. Should go with the easy DP approach. Also, using a hashMap to trach all the patterns, + this might not be applicable: because as the integer array's length goes up, there will be too many possible + combinations to store in hashamp. + Burst each balloon, and DFS into each branch, calcualte the sum + each balloon-burst's product. + Also, use a HahsMap<"Value combination", max value>. to reduce the # of re-calculation. + convert nums into string, and in DFS, we don't even need bakc-tracking + helper(list, sum) + + + Thoughts:http://www.cnblogs.com/grandyang/p/5006441.html + dp[i,j]: burst range [i~j]'s max coins. + +*/ +``` diff --git a/Java/Bus Routes.java b/Java/Bus Routes.java new file mode 100644 index 0000000..b8bff28 --- /dev/null +++ b/Java/Bus Routes.java @@ -0,0 +1,69 @@ +H +1534208810 +tags: BFS + +``` +/* +We have a list of bus routes. Each routes[i] is a bus route that the i-th bus repeats forever. +For example if routes[0] = [1, 5, 7], this means that the first bus (0-th indexed) +travels in the sequence 1->5->7->1->5->7->1->... forever. + +We start at bus stop S (initially not on a bus), and we want to go to bus stop T. +Travelling by buses only, what is the least number of buses we must take to reach our destination? +Return -1 if it is not possible. + +Example: +Input: +routes = [[1, 2, 7], [3, 6, 7]] +S = 1 +T = 6 +Output: 2 +Explanation: +The best strategy is take the first bus to the bus stop 7, then take the second bus to the bus stop 6. +Note: + +1 <= routes.length <= 500. +1 <= routes[i].length <= 500. +0 <= routes[i][j] < 10 ^ 6. +*/ + +/* +1. Map stop -> bus list +2. Add stop to queue +3. process queue, if stop match return; otherwise, try the linked bus (track visited bus) +*/ +class Solution { + public int numBusesToDestination(int[][] routes, int S, int T) { + Set visited = new HashSet<>(); + Map> stopMap = new HashMap<>(); + Queue queue = new LinkedList<>(); + // init + for (int i = 0; i < routes.length; i++) { + for (int stop : routes[i]) { + stopMap.putIfAbsent(stop, new HashSet<>()); + stopMap.get(stop).add(i); // add bus route to stop + } + } + queue.offer(S); + int count = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + int stop = queue.poll(); // 1, 2, 7 + if (stop == T) return count; + for (int bus : stopMap.get(stop)) { + if (!visited.contains(bus)) { + visited.add(bus); + for (int nextStop : routes[bus]) { + queue.offer(nextStop); // 3, 6, 7 + } + } + } + } + count++; + } + + return -1; + } +} +``` \ No newline at end of file diff --git a/Java/Change to Anagram.java b/Java/Change to Anagram.java new file mode 100755 index 0000000..300771b --- /dev/null +++ b/Java/Change to Anagram.java @@ -0,0 +1,118 @@ +E +1521692946 +tags: String + +HackerRank里面的random 题目: 给一个string, 一切成两半, 看两半要变化多少个字符, 能变成anagram. + +- 切两半成两个String A,B. 分别在int count[26]里面++, --. +- 记录 26个小写字母的频率. 如果全部抵消, 就是anagram. +- 注意: 最后count出来要除以2:字母不同,会在不同的字母位上加减count,那么就是刚好重复计算了一遍。所以除以二 + +- Note: HackerRank里要注意自己写: Scanner, import java.util, non-static method ...etc. + +``` +/* +HackerRank CodePair + +Sid loves to read short stories. Being a Computer Science student, +he decides to do some frequency analysis on his favorite reading material. +For each data point, chooses a string of length a from one book, +and a string of length b from a second book. The strings' lengths differ by no more than 1. +|a-b|≤1, where |x| represents the absolute value function. + +The frequency analysis consists of checking how far the strings are +from being anagrams of one another. Your challenge is to help him find +the minimum number of characters of the first string he needs to change to +make it an anagram of the second string. He can neither add nor delete characters +from the first string. Only replacement of the characters with new ones is allowed. + +Input Format +The first line will contain an integer T representing +the number of test cases. Each test case will contain a string having length (a+b) +which will be concatenation of both the strings described in problem. +The string will only contain small letters and without any spaces. + +Output Format +An integer corresponding to each test case is printed in a different line i.e., +the number of changes required for each test case. Print ‘-1’ if it is not possible. + +Constraints +1 ≤ T ≤ 100 + 1 ≤ a+b ≤ 10,000 + +Sample Input +5 +aaabbb +ab +abc +mnop +xyyx +Sample Output +3 +1 +-1 +2 +0 + +Explanation +In the five test cases +One string must be “aaa” and the other “bbb”. The lengths are a=3 and b=3, so the difference is less than 1. No characters are common between the strings, so all three must be changed. +One string must be “a” and the second “b”. The lengths are a=1 and b=1, so the difference is less than 1. One character must be changed to them the same. +Since the string lengths a and b must differ by no more than 1, the lengths are either a=1 and b=2 or a=2 and b=1. No sequence of substitutions will make the two anagrams of one another. +One string must be “mn" and other be “op”. The length are a=2 and b=2, so the difference is less than 1. No characters are common between the strings, so both must be changed. +One string must be “xy” and the other be “yx”. The length are a=2 and b=2, so the difference is less than 1. No changes are needed because the second string is already an anagram of the first. +*/ + + +/* +Check if they are valid string. If length == odd, then fail, -1 +Use int[26] arr to add chars from s1. If non-26-char exist, return -1 +use same int arr to remove chars from s2. +count the non-zeros + +Note: the order of letters does not matter, becase all we want to change to is anagram +*/ +import java.io.*; +import java.util.*; +public class Solution { + public static void main(String args[] ) throws Exception { + /* Enter your code here. Read input from STDIN. Print output to STDOUT */ + Scanner in = new Scanner(System.in); + int numOfCase = Integer.parseInt(in.nextLine()); + Solution sol = new Solution(); + + while (numOfCase > 0) { + int rst = sol.validate(in.nextLine()); + System.out.println(rst); + numOfCase--; + } + } + + public int validate(String str) { + if (str.length() % 2 == 1) { + return -1; + } + String sA = str.substring(0, str.length()/2); + String sB = str.substring(str.length()/2); + + int[] arr = new int[26]; + for (int i = 0; i < sA.length(); i++) { + char cA = sA.charAt(i); + char cB = sB.charAt(i); + if (cA < 'a' || cA > 'z' || cB < 'a' || cB > 'z') { + return -1; + } + arr[cA - 'a']++; + arr[cB - 'a']--; + } + + int count = 0; + for (int num : arr) { + count += Math.abs(num); + } + return count/2; + } + + +} +``` \ No newline at end of file diff --git a/Java/Classical Binary Search.java b/Java/Classical Binary Search.java old mode 100644 new mode 100755 index cae325c..bbe3f8e --- a/Java/Classical Binary Search.java +++ b/Java/Classical Binary Search.java @@ -1,5 +1,18 @@ +E +1521694570 +tags: Binary Search + +#### Binary Search Template +- while: start + 1 < end +- mid = start + (end - start) / 2; +- 根据mid作比较 +- 末尾double check start, end. + + +``` /* -Find any position of a target number in a sorted array. Return -1 if target does not exist. +Find any position of a target number in a sorted array. +Return -1 if target does not exist. Example Given [1, 2, 2, 4, 5, 5]. @@ -22,31 +35,31 @@ start,mid,end */ public class Solution { - /** - * @param A an integer array sorted in ascending order - * @param target an integer - * @return an integer - */ - public int findPosition(int[] A, int target) { - if (A == null || A.length == 0) { - return -1; - } - int start = 0; - int end = A.length - 1; - int mid; - while(start + 1 < end) { - mid = start + (end - start) / 2; - if (target == A[mid]) { - return mid; - } else if (target > A[mid]) { - start = mid; - } else { - end = mid; - } - }//end while - if (A[start] == target || A[end] == target) { - return A[start] == target ? start : end; - } - return -1; + public int findPosition(int[] nums, int target) { + if (nums == null || nums.length == 0) { + return -1; + } + int start = 0; + int end = nums.length - 1; + while (start + 1 < end) { + int mid = start + ((end - start) >> 1); + if (nums[mid] == target) { + return mid; + } else if (nums[mid] < target) { + start = mid; + } else { + end = mid; + } + } + + if (nums[start] == target) { + return start; + } + if (nums[end] == target) { + return end; + } + return -1; } } + +``` diff --git a/Java/Climbing Stairs.java b/Java/Climbing Stairs.java old mode 100644 new mode 100755 index 254d0b9..d82c594 --- a/Java/Climbing Stairs.java +++ b/Java/Climbing Stairs.java @@ -1,25 +1,189 @@ +E +1521695977 +tags: DP, Memoization, Sequence DP + +每一步可以走1步或者2步, 求总共多少种方法爬完梯子. + +#### Recursive + Memoization +- 递归很好写, 但是重复计算, timeout. time: O(2^n) +- O(2^n): each n can spawn 2 dfs child, at next level, it will keep spawn. Total 2^n nodes will spawn. +- 用全局变量int[] memo 帮助减少重复计算 +- O(n) time, space + +#### DP +- 加法原理, 最后一步被前两种走法决定: dp[i] = dp[i - 1] + dp[i - 2] +- 基础sequence DP, int[] dp = int[n + 1]; +- DP[]存的是以 1-based index的状态 +- dp[i]: count # of ways to finish 前i个 台阶 +- 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1] +- dp[0]往往是有特殊状态的 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + +``` /* -40% Accepted + You are climbing a stair case. It takes n steps to reach to the top. -Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? +Each time you can either climb 1 or 2 steps. +In how many distinct ways can you climb to the top? + +Dynamic Programming + +*/ + +/* +Thoughts: +Can be recursive, it goes like: climbStairs(n-1) + climbStairs(n - 2) +That has too much of redundant calculation. +Improve by Memoization +*/ +class Solution { + int[] memo; + public int climbStairs(int n) { + if (n <= 1) { + return 1; + } + memo = new int[n]; + return dfs(n - 1) + dfs(n - 2); + } + + public int dfs(int n) { + if (n <= 1) { + return 1; + } + if (memo[n] > 0) { + return memo[n]; + } + memo[n - 1] = dfs(n - 1); + memo[n - 2] = dfs(n - 2); + return memo[n - 1] + memo[n - 2]; + } +} +/* +Thoughts: +DP, consider the last step. It can be reached by 2 steps or 1 steps. +DP[i] represents # ways to reach index i. +DP[i] = DP[i - 1] + DP[i - 2]. + +Create DP = int [n + 1] +init: DP[0] = 1; DP[1] = 1; + +Return DP[n] +*/ +class Solution { + public int climbStairs(int n) { + if (n <= 1) { + return 1; + } + int[] dp = new int[n + 1]; + dp[0] = 1; + dp[1] = 1; + for (int i = 2; i <= n; i++) { + dp[i] = dp[i - 1] + dp[i - 2]; + } + + return dp[n]; + } +} + +/* +Rolling array +[i] only associates with i-2, i-1. +*/ +class Solution { + public int climbStairs(int n) { + if (n <= 1) { + return 1; + } + int[] dp = new int[2]; + dp[0] = 1; + dp[1] = 1; + for (int i = 2; i <= n; i++) { + dp[i % 2] = dp[(i - 1) % 2] + dp[(i - 2) % 2]; + } + + return dp[n % 2]; + } +} + +/* + Based on the DP solution, think about rolling array. + We only make use of i, i-1, and i-2. + Ideally, we can reduce them just to 3 variables -Example -Tags Expand + 1. Replace the variable + 2. Implement the rotation process: + prev2 = prev1; + prev1 = rst; +*/ +public class Solution { + public int climbStairs(int n) { + if (n == 0) { + return 0; + } + if (n == 1) { + return 1; + } + int rst = 0; + int prev2 = 1; + int prev1 = 1; + for (int i = 2; i <= n; i++) { + rst = prev1 + prev2; + prev2 = prev1; + prev1 = rst; + } + return rst; + } +} +/* + Recap 3.25.2016 + Naturally think of dfs. Any time when n drops to <= 0, count++ + Exceed time limit +*/ + +public class Solution { + public int count = 0; + public int climbStairs(int n) { + if (n <= 0) { + return 0; + } + dfs(n); + return count; + } + + public void dfs(int n) { + if (n <= 0) { + count++; + return; + } else if (n == 1) { + count++; + return; + } + dfs(n - 1); + dfs(n - 2); + } +} + + +/* Thinking process: State: at i level, f[i] is the ways to climb to i position. Function: f[i] = f[i-1] + f[i-2]. - f[i] is constructed from 2 branches: - Last step is 1 from f[i-1] - Last step is 2 from f[i-2] - This idea can be presented using a tree. However we don’t need to do recursive. We just need to use two pointers to withhold 2 level’s values. + f[i] is constructed from 2 branches: + Last step is 1 from f[i-1] + Last step is 2 from f[i-2] + This idea can be presented using a tree. However we don’t need to do recursive. We just need to use two pointers to withhold 2 level’s values. Init: The for loop starts at level2, so before level 2 there are 2 init states: - f[0] == 1. This means we jump 2 steps from level0 to level2. - f[i] == 1. This means we jump 1 steps to level1, then jump another step to level2 + f[0] == 1. This means we jump 2 steps from level0 to level2. + f[i] == 1. This means we jump 1 steps to level1, then jump another step to level2 Answer: f[n] */ - public class Solution { /** * @param n: An integer @@ -42,3 +206,5 @@ public int climbStairs(int n) { } + +``` \ No newline at end of file diff --git a/Java/Clone Graph.java b/Java/Clone Graph.java old mode 100644 new mode 100755 index eeaabe3..dec606d --- a/Java/Clone Graph.java +++ b/Java/Clone Graph.java @@ -1,7 +1,32 @@ +M +1533605472 +tags: DFS, BFS, Graph + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes. +- 先能复制多少Node复制多少. 然后把neighbor 加上 +- Use `map` to mark visited + +#### DFS +- Given graph node obj `{val, list of neighbor}`: copy the node and all neighbors +- Mark visited using map +- for loop on the each one of the neighbors: map copy, record in map, and further dfs +- once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +- 主要思想是: 一旦复制过了, 不必要重新复制 + +#### BFS +- Copy the root node, then copy all the neighbors. +- Mark copied node in map. +- Use queue to contain the newly added neighbors. Need to work on them in the future. + +``` /* Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors. - OJ's undirected graph serialization: Nodes are labeled uniquely. @@ -23,114 +48,109 @@ \_/ Hide Tags Depth-first Search Breadth-first Search Graph - + */ +/** + * Definition for undirected graph. + * class UndirectedGraphNode { + * int label; + * List neighbors; + * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList(); } + * }; + */ /* - //NEED TO RUN THIS ON LINT - Thoughts: 12.12.2015 - The original thoughs of using ArrayList, and using a index to track of which node has not been visited. - It's alright, but it uses extra space, and basically copie all nodes again. - It's similar to using a queue. - At the end, it's doing O(m * n) - Maybe can improve this. - - Need a queue and process each element. and a hashmap to track duplicates. - 1. make sure the node is no duplicate - 2. make sure to all all child - - border: case: node == nul, or node has not child, return a new instance of it'self? - +DFS +- Mark visited in map +- Add neibhors via dfs +- return node */ - public class Solution { + // old -> node node map + Map map = new HashMap<>(); public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { - if (node == null || node.neighbors.size() == 0) { + if (node == null) { return node; } + if (map.containsKey(node)) { + return map.get(node); + } - HashMap map = - new HashMap(); - Queue queue = new LinkedList(); - - queue.offer(node); - //process each node - while (!queue.isEmpty()) { - UndirectedGraphNode curr = queue.poll(); - UndirectedGraphNode newNode; - if (!map.containsKey(curr)) { - map.put(curr, new UndirectedGraphNode(curr.label)); + UndirectedGraphNode newNode = new UndirectedGraphNode(node.label); + map.put(node, newNode); + for (UndirectedGraphNode neighbor: node.neighbors) { + newNode.neighbors.add(cloneGraph(neighbor)); + } + return newNode; + } +} +/* +Thougths: +DFS with map in graph. +The serialized graph is just explaination for the test input. +1. copy the node +2. Mark 'added' using map(old, new) +3. for loop on the each one of the neighbors: map copy, record in map, and further dfs +4. once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +*/ +public class Solution { + HashMap map = new HashMap<>(); + public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { + if (node == null) { + return null; + } + + UndirectedGraphNode newNode = new UndirectedGraphNode(node.label); + map.put(node, newNode); + dfs(node); + return newNode; + } + + public void dfs(UndirectedGraphNode node) { + if (node == null) { + return; + } + for (UndirectedGraphNode neighbor: node.neighbors) { + if (!map.containsKey(neighbor)) { + UndirectedGraphNode newNeighbor = new UndirectedGraphNode(neighbor.label); + map.put(neighbor, newNeighbor); + dfs(neighbor); } - UndirectedGraphNode newNode = map.get(curr); - //Add neighbors for each node - for (UndirectedGraphNode neighbor : curr.neighbors) { - UndirectedGraphNode newNeighbor; - if (!map.containsKey(neighbor)) { - map.put(neighbor, new UndirectedGraphNode(neighbor.label)); - } - newNeighbor = map.get(neighbor); - - newNode.neighbors.add(newNeighbor); - }//end for - - }//end while - - return map.get(node); + map.get(node).neighbors.add(map.get(neighbor)); + } } } - - /* - - -Thinking process: -1. Clone all nodes available: using HashMap to go through all possible query. No duplicates added using HashMap. - HashMap map has the list of all new nodes. No neighbors added yet - = - At same time, the arrayList nodes has all original nodes(with neighbors) in Breadth-first order. -2. Add neighbor for nodes in map: - - Locate the 'newNode' from map by using the key: the original node - - loop through the original node's neighbor size - - use original neighbor as key to get the new neighbor instance from map - - add this new neighbor instance to the neighbor list of 'newNode' +Thougths: +BFS, same concept as DFS. +1. Copy the root node, then copy all the neighbors. +2. Mark copied node in map. +3. Use queue to contain the neighbors for next round: if it has neighbors */ -/** - * Definition for undirected graph. - * class UndirectedGraphNode { - * int label; - * List neighbors; - * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList(); } - * }; - */ public class Solution { public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { if (node == null) { - return node; + return null; } - ArrayList nodes = new ArrayList(); - nodes.add(node); - HashMap map = new HashMap(); - map.put(node, new UndirectedGraphNode(node.label)); - int start = 0; - //Clone nodes without neighbors: - while (start < nodes.size()) { - List neighbors = nodes.get(start++).neighbors; - for (int i = 0; i < neighbors.size(); i++) { - if (!map.containsKey(neighbors.get(i))) { - map.put(neighbors.get(i), new UndirectedGraphNode(neighbors.get(i).label)); - nodes.add(neighbors.get(i)); + HashMap map = new HashMap<>(); + map.put(node, new UndirectedGraphNode(node.label)); // copy root + + Queue queue = new LinkedList<>(); + queue.offer(node); + + while(!queue.isEmpty()) { + UndirectedGraphNode curr = queue.poll(); + for (UndirectedGraphNode neighbor: curr.neighbors) { + if (!map.containsKey(neighbor)) { // Init neighbors + queue.offer(neighbor); + map.put(neighbor, new UndirectedGraphNode(neighbor.label)); } + map.get(curr).neighbors.add(map.get(neighbor)); // link neighbor } } - // Clone neighbors: - for (int i = 0; i < nodes.size(); i++) { - UndirectedGraphNode newNode = map.get(nodes.get(i)); - for (int j = 0; j < nodes.get(i).neighbors.size(); j++) { - newNode.neighbors.add(map.get(nodes.get(i).neighbors.get(j))); - } - } - return map.get(node); + + return map.get(node); } } - +``` diff --git a/Java/Closest Binary Search Tree Value.java b/Java/Closest Binary Search Tree Value.java new file mode 100755 index 0000000..b4319f9 --- /dev/null +++ b/Java/Closest Binary Search Tree Value.java @@ -0,0 +1,85 @@ +E +1521773643 +tags: Binary Search, Tree, BST + +给一个BST, 和一个double target, 走位找到最接近的number. + +#### Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, +- 找到 node.val == target, 或者走位走完, return closest + +``` +/* +Given a non-empty binary search tree and a target value, find the value in the BST that is closest to the target. + +Note: +Given target value is a floating point. +You are guaranteed to have only one unique value in the BST that is closest to the target. + +Tags: Tree Binary Search +Similar Problems: (M) Count Complete Tree Nodes, (H) Closest Binary Search Tree Value II + +*/ + +/* +Thoughts: +dfs, before going into the left/right child, compare target with root, leftChild and rightChild see if a match +*/ +class Solution { + public int closestValue(TreeNode root, double target) { + if (root.left != null && target < root.val) { + int leftResult = closestValue(root.left, target); + return Math.abs(leftResult - target) < Math.abs(root.val - target) ? leftResult : root.val; + } + if (root.right != null && target > root.val) { + int rightResult = closestValue(root.right, target); + return Math.abs(rightResult - target) < Math.abs(root.val - target) ? rightResult : root.val; + } + return root.val; + } +} + +/* +Thoughts: +Binary search, maintain a closest value. +Note: initial closest in real case is just the root, since we start from the root +*/ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public int closestValue(TreeNode root, double target) { + if (root == null) { + return 0; + } + double closest = root.val; + while (root != null) { + if (root.val == target) { + return root.val; + } else { + if (Math.abs(target - closest) >= Math.abs(target - root.val)) { + closest = root.val; + } + if (root.val > target) { + root = root.left; + } else { + root = root.right; + } + } + }//END while + return (int)closest; + } +} +``` \ No newline at end of file diff --git a/Java/Closest Number in Sorted Array.java b/Java/Closest Number in Sorted Array.java old mode 100644 new mode 100755 index 29ab136..297f4e9 --- a/Java/Closest Number in Sorted Array.java +++ b/Java/Closest Number in Sorted Array.java @@ -1,8 +1,16 @@ -Binary search. 考虑mid-1, mid+1. -一旦没有mid = target.index。 那么target最终就narrow down在(mid-1,mid) 或者(mid,mid+1) +E +1522011280 +tags: Binary Search + +- Binary Search 的一种变型, LintCode无法再跑一边. +- 考虑mid-1, mid+1. +- 一旦没有mid = target.index。 那么target最终就narrow down在(mid-1,mid) 或者(mid,mid+1) + ``` /* -Given a target number and an integer array A sorted in ascending order, find the index i in A such that A[i] is closest to the given target. +LintCode +Given a target number and an integer array A sorted in ascending order, +find the index i in A such that A[i] is closest to the given target. Return -1 if there is no element in the array. @@ -32,11 +40,6 @@ */ public class Solution { - /** - * @param A an integer array sorted in ascending order - * @param target an integer - * @return an integer - */ public int closestNumber(int[] A, int target) { if (A == null || A.length == 0) { return -1; diff --git a/Java/Coin Change 2.java b/Java/Coin Change 2.java new file mode 100644 index 0000000..4a1b21c --- /dev/null +++ b/Java/Coin Change 2.java @@ -0,0 +1,71 @@ +M +1522735311 +tags: DP, Backpack DP + +给串数字, target amount, 求总共多少种方式可以reach the amount. + +#### DP +- O(MN): M, total target amount; N: size of coins +- 类似于: 网格dp, unique path 里面的2种走法: 从上到下, 从左到右 +- 状态: dp[i]: sum of ways that coins can add up to i. +- Function: dp[j] += dp[j - coins[i]]; +- Init: dp[0] = 1 for ease of calculation; other dp[i] = 0 by default +- note: 避免重复count, 所以 j = coins[i] as start +- 注意 coins 需要放在for loop 外面, 主导换coin的流程, 每个coin可以用无数次, 所以在每一个sum value上都尝试用一次每个coin + +#### knapsack problem: backpack problem + +``` +/* +You are given coins of different denominations and a total amount of money. +Write a function to compute the number of combinations that make up that amount. +You may assume that you have infinite number of each kind of coin. + +Note: You can assume that + +0 <= amount <= 5000 +1 <= coin <= 5000 +the number of coins is less than 500 +the answer is guaranteed to fit into signed 32-bit integer +Example 1: + +Input: amount = 5, coins = [1, 2, 5] +Output: 4 +Explanation: there are four ways to make up the amount: +5=5 +5=2+2+1 +5=2+1+1+1 +5=1+1+1+1+1 +Example 2: + +Input: amount = 3, coins = [2] +Output: 0 +Explanation: the amount of 3 cannot be made up just with coins of 2. +Example 3: + +Input: amount = 10, coins = [10] +Output: 1 +*/ + +/* +Thoughts: +dp[i]: sum of all possibilities that adds up the value to be i from last step dp[i - coins[j]]. +Avoid redundant calculation: put j = coins[i] as start of inner for loop +*/ +class Solution { + public int change(int amount, int[] coins) { + if (amount == 0) { + return 1; + } + int n = coins.length; + int[] dp = new int[amount + 1]; + dp[0] = 1; // for dp, not quite meaning full: 1 way to build 0 amount + for (int i = 0; i < n; i++) { + for (int j = coins[i]; j <= amount; j++) { + dp[j] += dp[j - coins[i]]; + } + } + return dp[amount]; + } +} +``` \ No newline at end of file diff --git a/Java/Coin Change.java b/Java/Coin Change.java new file mode 100644 index 0000000..a19565e --- /dev/null +++ b/Java/Coin Change.java @@ -0,0 +1,159 @@ +M +1516583739 +tags: DP, Backpack DP, Memoization + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + +#### DP +- 找对方程dp[x], 积累到amount x最少用多少个coin: #coin是value, index是 [0~x]. +- 子问题的关系是: 如果用了一个coin, 那么就应该是f[x - coinValue]那个位置的#coins + 1 + +##### initialization +- 处理边界, 一开始0index的时候, 用value0. +- 中间利用Integer.MAX_VALUE来作比较, initialize dp[x] +- 注意, 一旦 Integer.MAX_VALUE + 1 就会变成负数. 这种情况会在coin=0的时候发生. + +##### Optimization +- 方法1: 直接用Integer.MAX_VALUE +- 方法2: 用-1, 稍微简洁一点, 每次比较dp[i]和 dp[i - coin] + 1, 然后save. 不必要做多次min比较. + +#### Memoization +- dp[i] 依然表示: min # of coints to make amount i +- initialize dp[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录dp[amount] 如果已经给过value, 不要重复计算, 直接return. +- 但是这道题没必要强行做memoization, 普通DP的状态和方程相对来说很好找到 + +``` +/* +You are given coins of different denominations and a total amount of money amount. +Write a function to compute the fewest number of coins that you need to make up that amount. +If that amount of money cannot be made up by any combination of the coins, return -1. + +Example 1: +coins = [1, 2, 5], amount = 11 +return 3 (11 = 5 + 5 + 1) + +Example 2: +coins = [2], amount = 3 +return -1. + +Note: +You may assume that you have an infinite number of each kind of coin. + +*/ + +/* +Thoughts: +Last step: which coin can make amount? enumerate it +dp[i]: min # of coins to make i amount. +dp[i] = Math.min(dp[i - coinValueA], dp[i - coinValueB], dp[i - coinValueC], ...) + 1 + +init: dp[0] = 0. No value, no coins. + +If dp[i] not found, set it to -1 +*/ +class Solution { + public int coinChange(int[] coins, int amount) { + if (coins == null || coins.length == 0) { + return -1; + } + int[] dp = new int[amount + 1]; + dp[0] = 0; + for (int i = 1; i <= amount; i++) { + dp[i] = Integer.MAX_VALUE; + for (int coin : coins) { + if (i >= coin && dp[i - coin] != -1 && coin != 0) { + dp[i] = Math.min(dp[i], dp[i - coin] + 1); + } + } + if (dp[i] == Integer.MAX_VALUE) { + dp[i] = -1; + } + } + return dp[amount]; + } +} + +/* +Thoughts: +DP, memoization, DFS? +Pick one coin => amount - coin => coinChange(coins, amount - coin). +- dp[i]: min # for amount i. +- create dp[amount + 1] +- make dp global and memorize results +*/ +class Solution { + int[] dp; + public int coinChange(int[] coins, int amount) { + if (coins == null || coins.length == 0) { + return -1; + } + dp = new int[amount + 1]; + for (int i = 1; i <= amount; i++) { + dp[i] = Integer.MAX_VALUE; + } + return dfs(coins, amount); + } + + private int dfs(int[] coins, int amount) { + if (amount < 0) { + return -1; + } + if (dp[amount] != Integer.MAX_VALUE) { + return dp[amount]; + } + + for (int coin : coins) { + int temp = dfs(coins, amount - coin); + if (temp >= 0) { + dp[amount] = Math.min(dp[amount], temp + 1); + } + } + + return dp[amount] = dp[amount] == Integer.MAX_VALUE ? - 1 : dp[amount]; + } +} + +/* +Previous thoughts: +count 'fewest' -> DP. +11 = 5 + 5 + 1, but not = 1 + 1 + 1...+1. +f[x] = fewests num of coins need to accumulate amount x. +f[x] = f[x - coin1] + 1, or f[x - coin2] + 1, f[x - coin3] + 1 +Boundary: +x = 1,2,5 -> return 1. +x < 1, return -1. +if can't work at a particular ( 0 ~ x), let's say: we only have 2 and 5 but we want to get f[3], +put Integer.MAX_VALUE there. + +step: +initialize f[0] +loop over 0 ~ amount, calculate f[x] +*/ + +// simplify: +// if dp[i] == -1, which is just initialized, so dp[i] has to accept dp[i - coin] + 1. +// i>= coin is to limit such that coin won't over-jump index. +class Solution { + public int coinChange(int[] coins, int amount) { + if (coins == null || coins.length == 0) { + return -1; + } + final int[] dp = new int[amount + 1]; + dp[0] = 0; + for (int i = 1; i < dp.length; i++) { // 1: 1, 2: 2, 5 + dp[i] = -1; + for (int coin : coins) { + if (i >= coin && dp[i - coin] != -1) { + if (dp[i] == -1 || dp[i - coin] + 1 < dp[i]) { + dp[i] = dp[i - coin] + 1; + } + } + } + } + return dp[amount]; + } +} + +``` \ No newline at end of file diff --git a/Java/Coins in a Line II.java b/Java/Coins in a Line II.java new file mode 100644 index 0000000..c19767c --- /dev/null +++ b/Java/Coins in a Line II.java @@ -0,0 +1,120 @@ +M +1521616414 +tags: DP, Array, Game Theory, Memoization, MiniMax + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + +``` +/* +There are n coins with different value in a line. +Two players take turns to take one or two coins from left side +until there are no more coins left. + +The player who take the coins with the most value wins. + +Could you please decide the first player will win or lose? + +Have you met this question in a real interview? +Example +Given values array A = [1,2,2], return true. + +Given A = [1,2,4], return false. +*/ + +/* +Thoughts 较为复杂, 写中文: +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 + + +Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +更加简化一点: 如果我是先手, dp[i]代表我的最大值. +取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +Note: +这个做法是从最后往前推的, 注意initialize dp末尾的值. +dp = new int[n + 1] +dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. + +sum = new int[n + 1] +sum[n] = 0; // 啥也不选的时候, 自然等于0 +然后initialize (n-1), (n-2) +*/ +public class Solution { + public boolean firstWillWin(int[] values) { + if (values == null || values.length == 0) { + return false; + } + if (values.length <= 2) { + return true; + } + int n = values.length; + int[] dp = new int[n + 1]; // max value for [i ~ n] + int[] sum = new int[n + 1]; // sum of [i ~ n] + // Init dp + dp[n] = 0; + dp[n - 1] = values[n - 1]; // only 1 item for max + dp[n - 2] = values[n - 1] + values[n - 2]; // only 2 items, get all for max + + // Init Sum + sum[n] = 0; + sum[n - 1] = values[n - 1]; + + for (int i = n - 2; i >= 0; i--) { + sum[i] = sum[i + 1] + values[i]; + dp[i] = Math.max(sum[i] - dp[i + 1], sum[i] - dp[i + 2]); // Maximize the value during playerA's worst scenario + } + + return sum[0] - dp[0] < dp[0]; + } +} + +``` \ No newline at end of file diff --git a/Java/Coins in a Line III.java b/Java/Coins in a Line III.java new file mode 100644 index 0000000..256bfdf --- /dev/null +++ b/Java/Coins in a Line III.java @@ -0,0 +1,240 @@ +H +1521702603 +tags: Array, DP, Game Theory, Interval DP, Memoization + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + +``` +/* +There are n coins in a line. Two players take turns to take a coin from one of the ends of the line +until there are no more coins left. The player with the larger amount of money wins. + +Could you please decide the first player will win or lose? + +Example +Given array A = [3,2,2], return true. + +Given array A = [1,2,4], return true. + +Given array A = [1,20,4], return false. + +Challenge +Follow Up Question: + +If n is even. Is there any hacky algorithm that can decide whether first player will win +or lose in O(1) memory and O(n) time? + +Tags +Array Dynamic Programming Game Theory +*/ + + +/* + +博弈. 这题, 是区间型. +区间型标志: 每人每次只能取第一个数,或者最后一个数 +翻译: 每次砍头, 或者砍尾 + +trick, 记录自己的数字与对手的数字和之差: +S(x) = X - Y, 找最大数字差. 如果最大值都大于0, 就是赢了; 如果小于0, 就输了. +这里我们用S(x)表示对于x先手而言的数字差, S(y)表示对于opponent y 先手而言的数字差. +假设x先手的时候, S(x) = X - Y. 这一步拿掉了大小为m的coin. +当opponent变成先手时候, 剩下的coins 被分割成x’, y’, 就有subset的S’(y) = y’ - x’ +Overall S(y) = Y - X = y’ - x’ - m = S’(y) - m +同时S(x) = X - Y = -(S’(y) - m) = m - S’(y) +注意: 这里的S’(y)面对的是拿过coins剩下的局面. + +dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x) = X - Y. + +那么S(x) = X - Y = a[i] - dp[i + 1][j]; // 砍头 +X = a[i]. 那里第i个coin +dp[i + 1][j]: opponent从 i 位之后能积累的最大值 +a[j] - dp[i][j - 1]//砍尾 + +dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} + +最后看dp[0][n] >= 0 + + + */ +public class Solution { + public boolean firstWillWin(int[] values) { + if (values == null || values.length == 0) { + return false; + } + + int n = values.length; + int[][] dp = new int[n][n]; + + // len = 1 + for (int i = 0; i < n; i++) { + dp[i][i] = values[i]; + } + + // len = 2 + for (int len = 2; len <= n; len++) { + for (int i = 0; i <= n - len; i++) { + int j = len + i - 1; + dp[i][j] = Math.max(values[i] - dp[i + 1][j], values[j] - dp[i][j - 1]); + } + } + return dp[0][n - 1] >= 0; + } +} + +/* +Thoughts: +MiniMax concept, memoization dp. +dp[i][j]: max sum of values a player can get in range [i, j] +sum[i][j]: sum of value in range [i, j] +dp[i][j] = sum[i][j] - Math.min(dp[i + 1][j], dp[i][j - 1]); +*/ +public class Solution { + public boolean firstWillWin(int[] values) { + if (values == null || values.length == 0) { + return false; + } + int n = values.length; + + int[][] sum = new int[n + 1][n + 1]; + int[][] dp = new int[n + 1][n + 1]; + boolean[][] visited = new boolean[n + 1][n + 1]; + + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + if (i == j) { + sum[i][j] = values[i]; + } else { + sum[i][j] = sum[i][j - 1] + values[j]; + } + } + } + + // total + int total = 0; + for (int value : values) { + total += value; + } + + search(0, n - 1, visited, dp, sum, values); + return dp[0][n - 1] > total / 2; + } + + private void search(int i, int j, boolean[][] visited, int[][] dp, int[][] sum, int[] values) { + if (visited[i][j]) { + return; + } + + visited[i][j] = true; + + if (i == j) { + dp[i][j] = values[i]; + } else if (i > j) { + dp[i][j] = 0; + } else if (i + 1 == j) { + dp[i][j] = Math.max(values[i], values[j]); + } else { + search(i + 1, j, visited, dp, sum, values); + search(i, j - 1, visited, dp, sum, values); + dp[i][j] = sum[i][j] - Math.min(dp[i + 1][j], dp[i][j - 1]); + } + } +} + +//using flat to mark visited; actually dp[i][j] > 0 will mean visited, since coin value > 0 +public class Solution { + public boolean firstWillWin(int[] values) { + if (values == null || values.length == 0) { + return false; + } + int n = values.length; + + int[][] sum = new int[n + 1][n + 1]; + int[][] dp = new int[n + 1][n + 1]; + + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + if (i == j) { + sum[i][j] = values[i]; + } else { + sum[i][j] = sum[i][j - 1] + values[j]; + } + } + } + + // total + int total = 0; + for (int value : values) { + total += value; + } + + search(0, n - 1, dp, sum, values); + return dp[0][n - 1] > total / 2; + } + + private void search(int i, int j, int[][] dp, int[][] sum, int[] values) { + if (dp[i][j] > 0) { + return; + } + if (i == j) { + dp[i][j] = values[i]; + } else if (i > j) { + dp[i][j] = 0; + } else if (i + 1 == j) { + dp[i][j] = Math.max(values[i], values[j]); + } else { + search(i + 1, j, dp, sum, values); + search(i, j - 1, dp, sum, values); + dp[i][j] = sum[i][j] - Math.min(dp[i + 1][j], dp[i][j - 1]); + } + } +} + + +``` \ No newline at end of file diff --git a/Java/Coins in a Line.java b/Java/Coins in a Line.java old mode 100644 new mode 100755 index d7adc09..284f0e7 --- a/Java/Coins in a Line.java +++ b/Java/Coins in a Line.java @@ -1,8 +1,27 @@ -这题没见过的时候,要分析分析。分析出来了些个DP解决。 -However, 分析过后简直屌炸天。一个 n%3就解决了。纯粹math. +M +1521605526 +tags: DP, Greedy, Game Theory + +拿棋子游戏, 每个人可以拿1个或者2个, 拿走最后一个子儿的输. 问: 根据给的棋子输, 是否能确定先手的输赢? + +Game Theory: 如果我要赢, 后手得到的局面一定要'有输的可能'. + +#### DP, Game Theory +- 要赢, 必须保证对手拿到棋盘时, 在所有他可走的情况中, '有可能败', 那就足够. +- 设计dp[i]:表示我面对i个coins的局面时是否能赢, 取决于我拿掉1个,或者2个时, 对手是不是会可能输? +- dp[i] = !dp[i - 1] || !dp[i-2] +- 时间: O(n), 空间O(n) +- 博弈问题, 常从'我的第一步'角度分析, 因为此时局面最简单. + +#### Rolling Array +空间优化O(1). Rolling array, %2 + ``` /* -There are n coins in a line. Two players take turns to take one or two coins from right side until there are no more coins left. The player who take the last coin wins. +There are n coins in a line. +Two players take turns to take one or two coins from right side +until there are no more coins left. +The player who take the last coin wins. Could you please decide the first play will win or lose? @@ -26,6 +45,104 @@ /* Thoughts: +Game theory: if the first-hand wants to win, the opponent should be able to lose in the following move. +The player wins if there is only 1 or 2 coins on the plate. +Goal: at current state, make a move and make sure the next play definitely loses. +Meet either one of the conditions: +- 1st player picks 1 coin, 2nd play must lose with n - 1 coins; +- 1st player picks 2 coins, 2nd play must lose with n - 2 coins; + +dp[i] = true/false: facing i coins,can player win? +dp[i] = !dp[i - 1] || !dp[i - 2]; +Meaning: either at (i-1) or (i-2), as long as the opponent player has possibilty to lose, +it's 100% win for first player. + +- Size of dp should be n + 1; we are counting 0 coins. +- init: true for 1 or 2 coins +O(n) +*/ + +public class Solution { + /** + * @param n: an integer + * @return: a boolean which equals to true if the first player will win + */ + public boolean firstWillWin(int n) { + if (n <= 0) { + return false; + } + boolean[] dp = new boolean[n + 1]; + dp[0] = false; // no coins to pick. + dp[1] = true; // the player wins; of course dp[2]=true as well for the player, but it'll be calculated below + + for (int i = 2; i <= n; i++) { + dp[i] = !dp[i - 2] || !dp[i - 1]; + } + return dp[n]; + } +} + +/* +Rolling Array +[i] only associates with [i - 1] && [i - 2]: rolling array % + +Time: O(n) +Space: O(1) +*/ + +public class Solution { + public boolean firstWillWin(int n) { + if (n <= 0) { + return false; + } + boolean[] dp = new boolean[2]; + dp[0] = false; + dp[1] = true; + for (int i = 2; i <= n; i++) { + dp[i % 2] = !dp[(i - 1) % 2] || !dp[(i - 2) % 2]; + } + + return dp[n % 2]; + } +} + + + +/* +Previous notes: +Rolling array: only need to use i, i-1, i-2 + */ +public class Solution { + public boolean firstWillWin(int n) { + if (n <= 0) { + return false; + } + if (n <= 2) { + return true; + } + + int start = 0; + int mid = 1; + int end = 2; + boolean[] dp = new boolean[3]; + dp[start] = false; // no coins to pick. + dp[mid] = true; + + for (int i = 2; i <= n; i++) { + dp[end] = !dp[start] || !dp[mid]; + if (i != n) { + start = (start + 1) % 3; + mid = (mid + 1) % 3; + end = (end + 1) % 3; + } + } + return dp[end]; + } +} + +/* +Not recommended. +Thoughts: Clarify: '1st play will win' means: if play properly, 1st play will surely have a way to win that 2nd play can't beat. Make dp[i]: the result when n = i. diff --git a/Java/ColorGrid.java b/Java/ColorGrid.java new file mode 100755 index 0000000..4e368fc --- /dev/null +++ b/Java/ColorGrid.java @@ -0,0 +1,158 @@ +M +1527959005 +tags: Hash Table, Design + +#### basic implementation +- 用HashMap, 理解题目规律,因为重复的计算可以被覆盖,所以是个优化题。没有什么太大的悬念和意义. +- 消灭重合点: +- 如果process当下col, 其实要减去过去所有加过的row的交接点。。。 +- 再分析,就是每次碰到row 取一个单点, sumRow += xxx。 +- 然后process当下col时候, sum += colValue * N - sumRow. 就等于把交叉所有row(曾经Process过的row)的点减去了。很方便。 +- 最后read in 是O(P), process也是O(P). + + +``` + +/* +HackerRank. +You are given an N×NN×N grid. Each cell has the color white (color 0) in the beginning. + +Each row and column has a certain color associated with it. +Filling a row or column with a new color VV means changing all the cells of +that row or column to VV (thus overriding the previous colors of the cells). + +Now, given a sequence of PP such operations, calculate the sum of the colors in the final grid. + +For simplicity, the colors will be positive integers whose values will be most 109109. + +Input Format +The first line of input contains two integers NN and PP separated by a space. +The next PP lines each contain a filling operation. There are two types of filling operations. + +ROW I V which means "fill row II with color VV". +COL I V which means "fill column II with color VV". +Output Format +Output one line containing exactly one integer which is the sum of the colors in the final grid. + +Constraints +1≤N≤60001≤N≤6000 +1≤P≤4000001≤P≤400000 +1≤I≤N1≤I≤N +1≤V≤1091≤V≤109 + +Sample Input + +5 4 +COL 1 6 +COL 4 11 +ROW 3 9 +COL 1 24 +Sample Output + +200 +Explanation +There are four operations. After the second operation, the grid looks like + + 6 0 0 11 0 + 6 0 0 11 0 + 6 0 0 11 0 + 6 0 0 11 0 + 6 0 0 11 0 +After the third operation (ROW 3 9), the third row was colored with 9, overriding any previous color in the cells. + + 6 0 0 11 0 + 6 0 0 11 0 + 9 9 9 9 9 + 6 0 0 11 0 + 6 0 0 11 0 +After the fourth operation (COL 1 24), the grid becomes: + +24 0 0 11 0 +24 0 0 11 0 +24 9 9 9 9 +24 0 0 11 0 +24 0 0 11 0 +The sum of the colors in this grid is 200. + +*/ + +import java.io.*; +import java.util.*; +import java.text.*; +import java.math.*; +import java.util.regex.*; +/* +Thoughts: +This is for practice. I didn't run much tests on the code. + Store info into class Cell {int x; boolean isRow; long value} + Save to arraylist. Later need to call list.remove(object) + Use hash map to store the appearance + process the final data: + keep track of curr single row cell sum = rowSum; also colSum + during process: add up n*colorValue. + if row, minus rowSum + if col, minus colSum +*/ +public class Solution { + class Cell { + int x; + boolean isRow; + long value; + public Cell(String s) { + String[] ss = s.split(" "); + this.isRow = ss[0].charAt(0) == 'R'; + this.x = Integer.parseInt(ss[1]); + this.value = Long.parseLong(ss[2]); + } + } + public static void main(String[] args) { + Solution sol = new Solution(); + + Scanner in = new Scanner(System.in); + String[] ss = in.nextLine().split(" "); + int N = Integer.parseInt(ss[0]); + int P = Integer.parseInt(ss[1]); + + //Storage + HashMap map = new HashMap(); + ArrayList list = new ArrayList(); + + while (P != 0) {//O(P) + //create Cell + String s = in.nextLine(); + Cell cell = sol.new Cell(s); + //add into list + list.add(cell); + //Check if cell exist in map. + //if exist in map, replace it with current cell, and remove old cell from list + String key = s.substring(0, s.lastIndexOf(" ")); + if (!map.containsKey(key)) { + map.put(key, cell); + } else { + Cell oldCell = map.get(key); + map.put(key, cell); + list.remove(oldCell); + } + P--; + } + + //Process final results + int sumCol = 0; + int sumRow = 0; + long sum = 0; + for (int i = 0; i < list.size(); i++) {//O(P) + Cell cell = list.get(i); + sum += cell.value * N; + if (cell.isRow) { + sum -= sumCol; + sumRow += cell.value; + } else { + sum -= sumRow; + sumCol += cell.value; + } + } + + System.out.println(sum); + } +} +``` \ No newline at end of file diff --git a/Java/Combination Sum II.java b/Java/Combination Sum II.java old mode 100644 new mode 100755 index 001b450..3578b25 --- a/Java/Combination Sum II.java +++ b/Java/Combination Sum II.java @@ -1,7 +1,104 @@ -确保Helper是用i+1,下一层的数字。 +M +1531549900 +tags: Array, DFS, Backtracking, Combination + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + ``` /* +Given a collection of candidate numbers (candidates) and a target number (target), +find all unique combinations in candidates where the candidate numbers sums to target. + +Each number in candidates may only be used once in the combination. + +Note: + +All numbers (including target) will be positive integers. +The solution set must not contain duplicate combinations. +Example 1: + +Input: candidates = [10,1,2,7,6,1,5], target = 8, +A solution set is: +[ + [1, 7], + [1, 2, 5], + [2, 6], + [1, 1, 6] +] +Example 2: + +Input: candidates = [2,5,2,1,2], target = 5, +A solution set is: +[ + [1,2,2], + [5] +] + + + */ + +/* +- one item can be picked once. the input candidates may have duplicates. +- IMPORTANT: 1. sort. 2. Skip adjacent item in for loop +- use dfs, for loop to aggregate candidates +- do target - val to track, and when target == 0, that’s a solution +- dfs(curr index i), instead of (i + 1): allows reuse of item + +*/ +class Solution { + public List> combinationSum2(int[] candidates, int target) { + List> result = new ArrayList<>(); + if (validate(candidates, target)) return result; + + Arrays.sort(candidates); // critical to skip duplicates + dfs(result, new ArrayList<>(), candidates, 0, target); + return result; + } + + private void dfs(List> result, List list, + int[] candidates, int index, int target) { + // for loop, where dfs is performed + for (int i = index; i < candidates.length; i++) { + // ensures at same for loop round, the same item (sorted && neighbor) won't be picked 2 times + if (i > index && candidates[i] == candidates[i - 1]) continue; + + int value = candidates[i]; + list.add(value); + if (target == value) result.add(new ArrayList<>(list)); + else if (target - value > 0) dfs(result, list, candidates, i + 1, target - value); + list.remove(list.size() - 1); // backtrack + } + } + + private boolean validate(int[] candidates, int target) { + return candidates == null || candidates.length == 0 || target <= 0; + } +} + + + +/* +LincCode Given a collection of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T. diff --git a/Java/Combination Sum III.java b/Java/Combination Sum III.java new file mode 100644 index 0000000..aad901a --- /dev/null +++ b/Java/Combination Sum III.java @@ -0,0 +1,78 @@ +M +1531549915 +tags: Array, DFS, Backtracking, Combination + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + +``` +/* +Find all possible combinations of k numbers that add up to a number n, +given that only numbers from 1 to 9 can be used and each combination should be a unique set of numbers. + +Note: + +All numbers will be positive integers. +The solution set must not contain duplicate combinations. +Example 1: + +Input: k = 3, n = 7 +Output: [[1,2,4]] +Example 2: + +Input: k = 3, n = 9 +Output: [[1,2,6], [1,3,5], [2,3,4]] + +*/ + +/* +- only 1-9 can be used. can use each number only once. +- find k numbers to sum up to n +- each solution must be unique. +- dfs, for loop, each dfs, (i+1) to next level +- decreasing n in each dfs, when n == i, return. +- be careful with the case n - i < 0, no need to dfs +- dfs: result, list, index, k, n +*/ + +class Solution { + public List> combinationSum3(int k, int n) { + List> result = new ArrayList<>(); + // init result, check edge case + if (k <= 0 || n <= 0) return result; + + dfs(result, new ArrayList<>(), 1, k, n); + return result; + } + + private void dfs(List> result, List list, + int index, int k, int n) { + // for loop + for (int i = index; i <= 9; i++) { + list.add(i); + if (n == i && list.size() == k) { // found a success solution + result.add(new ArrayList<>(list)); + } else if (n > i){ + dfs(result, list, i + 1, k, n - i); + } + list.remove(list.size() - 1); + } + } +} + + +``` \ No newline at end of file diff --git a/Java/Combination Sum IV.java b/Java/Combination Sum IV.java new file mode 100644 index 0000000..419d9db --- /dev/null +++ b/Java/Combination Sum IV.java @@ -0,0 +1,140 @@ +M +1526760445 +tags: Array, DP, Backpack DP + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + +#### Backpack DP +- 计数问题, 可以想到DP. 其实就是Backpack VI. +- 从x个数字里面找candidate(可以重复用同一个数字), 来sum up to target. 找: # of ways to form the sequence. +- Backpack VI: 给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法 +- dp[i]: # of ways to build up to target i +- consider last step: 如果上一步取的是 candidate A, 那么就该加到dp[i]: +- dp[i] += dp[i - A] +- 要找overall dp[i], 就做一个for loop: dp[i] = sum{dp[i - num]}, where for (num: nums) +- Time: O(mn). m = size of nums, n = target +- If we optimize dp for loop, 需要Sort nums. O(mlogm). will efficient 如果m是constant或者relatively small. Overall: O(n) + +#### DFS, backtracking +- 尽管思考方式是对的, 但是 times out +- 可以重复使用数字的时候, 比如用1 来拼出 999, 这里用1就可以走999 dfs level, 不efficient + +/* +Given an integer array with all positive numbers and no duplicates, +find the number of possible combinations that add up to a positive integer target. + +Example: + +nums = [1, 2, 3] +target = 4 + +The possible combination ways are: +(1, 1, 1, 1) +(1, 1, 2) +(1, 2, 1) +(1, 3) +(2, 1, 1) +(2, 2) +(3, 1) + +Note that different sequences are counted as different combinations. + +Therefore the output is 7. +Follow up: +What if negative numbers are allowed in the given array? +How does it change the problem? +What limitation we need to add to the question to allow negative numbers? + +*/ + +/* +same as the backpack problem +- use numbers in nums to sum up to target +- num can be used multiple times. + +consider last step: +nums[i - 1] was picked, or not picked to sum up to target +dp[w] = # ways to sum up to target +if last num[i - 1] picked, dp[target] += dp[target - nums[i - 1]] + +dp[0] = 0; +dp[w] = sum (dp[w - num1], dp[W - num2], dp[w - num3]) +*/ + +class Solution { + public int combinationSum4(int[] nums, int target) { + // check edge case + if (nums == null || nums.length == 0) { + return 0; + } + int[] dp = new int[target + 1]; + dp[0] = 1; // target == 0, 1 way to form, as base condition + + for (int i = 1; i <= target; i++) { // weight + for (int num : nums) { // choose number + if (i >= num) + dp[i] += dp[i - num]; + } + } + return dp[target]; + } +} + +// Sort, and then skip any num larger than target i +class Solution { + public int combinationSum4(int[] nums, int target) { + // check edge case + if (nums == null || nums.length == 0) { + return 0; + } + Arrays.sort(nums); + int[] dp = new int[target + 1]; + dp[0] = 1; // target == 0, 1 way to form, as base condition + + for (int i = 1; i <= target; i++) { // weight + for (int j = 0; j < nums.length && nums[j] <= i; j++) { // choose number + dp[i] += dp[i - nums[j]]; + } + } + return dp[target]; + } +} + + + + +/* +- backtracking times out +- use numbers in nums to sum up to target +- num can be used multiple times. +- dfs, for loop, each for loop, can use same index for next dfs level +- if target = num, return 1; other count += dfs(...) +- dfs: nums, index, target. Perform target - val +*/ +class Solution { + public int combinationSum4(int[] nums, int target) { + // check edge case + if (nums == null || nums.length == 0) { + return 0; + } + return dfs(nums, 0, target); + } + + private int dfs(int[] nums, int index, int target) { + int count = 0; + // for loop + for (int i = index; i < nums.length; i++) { + if (target == nums[i]) { + count++; + } else if (target > nums[i]){ + // dfs + count += dfs(nums, index, target - nums[i]); + } + } + return count; + } +} \ No newline at end of file diff --git a/Java/Combination Sum.java b/Java/Combination Sum.java old mode 100644 new mode 100755 index 0566a71..00b9e92 --- a/Java/Combination Sum.java +++ b/Java/Combination Sum.java @@ -1,12 +1,108 @@ -递归,backtracking. 非常normal。 -记得求sum时候也pass 一个sum进去,backtracking一下sum也,这样就不必每次都sum the list了。 +M +1531549407 +tags: Array, DFS, Backtracking, Combination +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + ``` +/** +Given a set of candidate numbers (candidates) (without duplicates) +and a target number (target), find all unique combinations in candidates +where the candidate numbers sums to target. + +The same repeated number may be chosen from candidates unlimited number of times. + +Note: + +All numbers (including target) will be positive integers. +The solution set must not contain duplicate combinations. +Example 1: + +Input: candidates = [2,3,6,7], target = 7, +A solution set is: +[ + [7], + [2,2,3] +] +Example 2: + +Input: candidates = [2,3,5], target = 8, +A solution set is: +[ + [2,2,2,2], + [2,3,3], + [3,5] +] + + */ + /* -Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T. +- one item can be picked for multiple times: +- use dfs, for loop to aggregate candidates +- do target - val to track, and when target == 0, that’s a solution +- dfs(curr index i), instead of (i + 1): allows reuse of item -The same repeated number may be chosen from C unlimited number of times. +*/ +class Solution { + public List> combinationSum(int[] candidates, int target) { + List> result = new ArrayList<>(); + if (validate(candidates, target)) return result; + + dfs(result, new ArrayList<>(), candidates, 0, target); + return result; + } + + // for loop, where dfs is performed + private void dfs(List> result, List list, + int[] candidates, int index, int target) { + for (int i = index; i < candidates.length; i++) { + int value = candidates[i]; + list.add(value); + if (target == value) result.add(new ArrayList<>(list)); // one closure + else if (target - value > 0) dfs(result, list, candidates, i, target - value); + list.remove(list.size() - 1); // backtrack + } + } + + private boolean validate(int[] candidates, int target) { + return candidates == null || candidates.length == 0 || target <= 0; + } +} +/* +LintCode: +Given a set of candidate numbers (C) and a target number (T), +find all unique combinations in C where the candidate numbers sums to T. + +The same repeated number may be chosen from C unlimited number of times. For example, given candidate set 2,3,6,7 and target 7, A solution set is: @@ -32,6 +128,25 @@ Elements in a combination (a1, a2, … , ak) must be in non-descending order. (i In particular, I pass a 'sum' to compare with 'target' (want to have sum == target). Some solution prefer to use 'target - someVlaue' == 0 to find solution. */ +/* +Previous Note: +Seems like LintCode makes the input containing duplicates: that's why it needs to sort. + +递归,backtracking. 非常normal, 需要先sort. +记得求sum时候也pass 一个sum进去,backtracking一下sum也,这样就不必每次都sum the list了。 + +题目里面所同一个元素可以用n次,但是,同一种solution不能重复出现。如何handle? + +1. 用一个index (我们这里用了start)来mark每次recursive的起始点。 +2. 每个recursive都从for loop里面的i开始,而i = start。 也就是,下一个iteration,这个数字会有机会被重复使用。 +3. 同时,确定在同一个for loop里面,不同的Index上面相同的数字,不Process两遍。用一个prev 作为checker. + +假如[x1, x2, y, z], where x1 == x2, 上面做法的效果: +我们可能有这样的结果: x1,x1,x1,y,z +但是不会有:x1,x2,x2,y,z +两个solution从数字上是一样的,也就是duplicated solution, 要杜绝第二种。 + + */ public class Solution { /** * @param candidates: A list of integers diff --git a/Java/Combinations.java b/Java/Combinations.java old mode 100644 new mode 100755 index 2286e9b..3d21f25 --- a/Java/Combinations.java +++ b/Java/Combinations.java @@ -1,24 +1,79 @@ +M +1526624015 +tags: DFS, Backtracking, Combination + +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +#### DFS, Backtracking +- for loop, recursive (dfs). +- 每个item用一次, 下一个level dfs(index + 1) +- Combination DFS. 画个图想想. 每次从1~n里面pick一个数字i +- 因为下一层不能重新回去 [0~i]选,所以下一层recursive要从i+1开始选。 + +``` /* Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. -Example -For example, -If n = 4 and k = 2, a solution is: -[[2,4],[3,4],[2,3],[1,2],[1,3],[1,4]] -Tags Expand -Backtracking Array +Example: + +Input: n = 4, k = 2 +Output: +[ + [2,4], + [3,4], + [2,3], + [1,2], + [1,3], + [1,4], +] +*/ + +/* +dfs, for loop, track index, track size of list. +once size met, add to result, also no need to further dfs. +dfs: result,list, index, k, n +*/ + +class Solution { + public List> combine (int n, int k) { + List> result = new ArrayList<>(); + // check edge case + if (k <= 0 || n <= 0) { + return result; + } + + // init result, dfs + dfs(result, new ArrayList<>(), 1, k , n); + return result; + } + + private void dfs(List> result, List list, int index, int k, int n) { + // for loop + // check size, dfs with i + 1 + for (int i = index; i <= n; i++) { + list.add(i); + // add to result + if (list.size() == k) { + result.add(new ArrayList<>(list)); + list.remove(list.size() - 1); + continue; + } + dfs(result, list, i + 1, k, n); + list.remove(list.size() - 1); + } + } +} + + +/* Thinking process: -Use a helper method to perform recursive backtracking:add an element to next-level recursive call, and remote the entry after the recursive call. +Use a helper method to perform recursive backtracking:add an element to next-level recursive call, +and remote the entry after the recursive call. Note: When 'new' something, cannot use 'List' because it's a abstract class. Need to new 'ArrayList' -*/ + */ public class Solution { - /** - * @param n: Given the range of numbers - * @param k: Given the numbers of combinations - * @return: All the combinations of k numbers out of 1..n - */ public List> combine(int n, int k) { List> rst = new ArrayList>(); if (n <= 0 || k <= 0) { @@ -42,3 +97,5 @@ public void helper(List> rst, } } + +``` \ No newline at end of file diff --git a/Java/Compare Strings.java b/Java/Compare Strings.java old mode 100644 new mode 100755 index 1cf9b3a..fcb7ab3 --- a/Java/Compare Strings.java +++ b/Java/Compare Strings.java @@ -1,3 +1,16 @@ +E +1522012438 +tags: String + +看StringA是不是包括所有 StringB的字符. + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + +``` /* Compare two strings A and B, determine whether A contains all of the characters in B. @@ -11,18 +24,42 @@ Tags Expand Basic Implementation String LintCode Copyright +*/ + +/* +Thoughts: +Loop over A, B and ++/-- chars +count arr should have no negative results +*/ +public class Solution { + public boolean compareStrings(String A, String B) { + if (A == null || B == null || A.length() < B.length()) { + return false; + } + int[] count = new int[26]; + + for (char c : A.toCharArray()) { + count[c - 'A']++; + } + + for (char c : B.toCharArray()) { + count[c - 'A']--; + if (count[c - 'A'] < 0) { + return false; + } + } + return true; + } +} + +/* +Previous notes Thinking process: Count the number of occurance for StringA. Count the number of occurance for StringB. Check if all of StringB's char# <= StringA's char# at each index. -*/ - + */ public class Solution { - /** - * @param A : A string includes Upper Case letters - * @param B : A string includes Upper Case letter - * @return : if string A contains all of the characters in B return true else return false - */ public boolean compareStrings(String A, String B) { if (A == null || B == null || A.length() < B.length()) { return false; @@ -42,3 +79,5 @@ public boolean compareStrings(String A, String B) { } } + +``` \ No newline at end of file diff --git a/Java/Compare Version Numbers.java b/Java/Compare Version Numbers.java new file mode 100644 index 0000000..6b766a8 --- /dev/null +++ b/Java/Compare Version Numbers.java @@ -0,0 +1,130 @@ +M +1521777505 +tags: String + +给两串version number, 由数字和'.' 组成. 比较先后顺序. + +If version1 > version2 return 1, if version1 < version2 return -1, otherwise return 0. + +#### divide and conquer +- 用 str.split("\\.") 分割string +- Convert成integer, 逐个击破 + +#### 注意 +- '1.0' 和 '0' 是相等的 +- 如果可以假设version integer都是valid, 直接Integer.parseInt()就可以了 +- 不然的话, 可以compare string + +``` +/* +Compare two version numbers version1 and version2. +If version1 > version2 return 1, if version1 < version2 return -1, otherwise return 0. + +You may assume that the version strings are non-empty and contain only digits and the . character. +The . character does not represent a decimal point and is used to separate number sequences. +For instance, 2.5 is not "two and a half" or "half way to version three", it is the fifth second-level revision of the second first-level revision. + +Here is an example of version numbers ordering: + +0.1 < 1.1 < 1.2 < 13.37 +*/ + +/* +Thoughts: +- divide and conqure: divide by '.' +- during the same cun, the one with no sub-division, it's the leading version +- use Integer.parseInt() with assumption that the numbers are valid integer. +*/ +class Solution { + public int compareVersion(String version1, String version2) { + if (version1.equals(version2)) { + return 0; + } + String[] subVersion1 = version1.split("\\."); + String[] subVersion2 = version2.split("\\."); + int size = Math.max(subVersion1.length, subVersion2.length); + int rst = 0; + for (int i = 0; i < size; i++) { + if (i >= subVersion1.length) { + rst = Integer.parseInt(subVersion2[i]) == 0 ? 0 : -1; // assume the missing subVersion1 = 0 + } else if (i >= subVersion2.length) { + rst = Integer.parseInt(subVersion1[i]) == 0 ? 0 : 1; // assume the missing subVersion2 = 0 + } else { // both exist + if (Integer.parseInt(subVersion1[i]) != Integer.parseInt(subVersion2[i])) { + rst = Integer.parseInt(subVersion1[i]) < Integer.parseInt(subVersion2[i]) ? -1 : 1; + } + } + if (rst != 0) { + return rst; + } + } + return rst; + } +} + + +/* +Thoughts: +- divide and conqure: divide by '.' +- during the same cun, the one with no sub-division, it's the leading version +- compare string +*/ +class Solution { + public int compareVersion(String version1, String version2) { + if (version1.equals(version2)) { + return 0; + } + if (!version1.contains(".") && !version2.contains(".")) { + return compareString(version1, version2); + } + String[] subVersion1 = version1.split("\\."); + String[] subVersion2 = version2.split("\\."); + int size = Math.max(subVersion1.length, subVersion2.length); + int rst = 0; + for (int i = 0; i < size; i++) { + if (i >= subVersion1.length) { + rst = compareString("0", subVersion2[i]); + } else if (i >= subVersion2.length) { + rst = compareString(subVersion1[i], "0"); + } else { + rst = compareString(subVersion1[i], subVersion2[i]); + } + if (rst != 0) { + return rst; + } + } + return rst; + } + + /* + Assume the number can be super large, and can't be saved in Integer, or Long. + Compare number by each digit + */ + private int compareString(String str1, String str2) { + if (str1.equals(str2)) { + return 0; + } + while (str1 != null && str1.length() > 1 && str1.charAt(0) == '0') { + str1 = str1.substring(1); + } + + while (str2 != null && str2.length() > 1 && str2.charAt(0) == '0') { + str2 = str2.substring(1); + } + + if (str1.length() != str2.length()) { + return str1.length() < str2.length() ? -1 : 1; + } + for (int i = 0; i < str1.length(); i++) { + int digit1 = str1.charAt(i) - '0'; + int digit2 = str2.charAt(i) - '0'; + if (digit1 < digit2) { + return -1; + } else if (digit1 > digit2) { + return 1; + } + } + return 0; + } +} +``` \ No newline at end of file diff --git a/Java/Complete Binary Tree.java b/Java/Complete Binary Tree.java old mode 100644 new mode 100755 index 03fbba4..3c530a6 --- a/Java/Complete Binary Tree.java +++ b/Java/Complete Binary Tree.java @@ -1,13 +1,23 @@ -Use a flag . 当出现了第一次有 null children的node的时候, -说明complete tree的最低level出现了。 -自此以后,再不该有node再有child, queue后面出现的node应该左右孩子都是null. +E +1522011523 +tags: Tree, BFS + +A complete binary tree is a binary tree in which every level, except possibly the last, + +is completely filled, and all nodes are as far left as possible + +#### BFS +- 当出现了第一次有 null children的node的时候, 说明到了leaf level, mark flag = true; +- 自此以后,queue再不该有node再有child; queue后面出现的node的left/right child应该都是null +- 否则就是有问题, return false; + -用BFS ``` /* -Check a binary tree is completed or not. A complete binary tree is not binary tree that every level is completed filled except the deepest level. In the deepest level, all nodes must be as left as possible. See more definition +Check a binary tree is completed or not. +A complete binary tree is a binary tree that every level is completed filled except the deepest level. +In the deepest level, all nodes must be as left as possible. See more definition -Have you met this question in a real interview? Yes Example 1 / \ @@ -82,7 +92,7 @@ public boolean isComplete(TreeNode root) { return true; } - } + ``` \ No newline at end of file diff --git a/Java/Connecting Graph II.java b/Java/Connecting Graph II.java new file mode 100644 index 0000000..adf09f2 --- /dev/null +++ b/Java/Connecting Graph II.java @@ -0,0 +1,88 @@ +M +1520320968 +tags: Union Find + +Lint还不能跑, 全部按照题意和答案document的. + +``` +/* +LintCode: +Given n nodes in a graph labeled from 1 to n. There is no edges in the graph at beginning. + +You need to support the following method: +1. connect(a, b), add an edge to connect node a and node b. +2. query(a), Returns the number of connected component nodes which include node a. + + +Example +5 // n = 5 +query(1) return 1 +connect(1, 2) +query(1) return 2 +connect(2, 4) +query(1) return 3 +connect(1, 4) +query(1) return 3 + +*/ + +/** +Thoughts: +No chance to run on LintCode. + + + */ +public class ConnectingGraph { + // Placeholder for all the UninFind relationships + private int[] father = null; + + // Stores the number of union elements connected from downstream to each individual node + private int[] size = null; + + /** + Initialize one element to each individual union; size will be 1 for all unions as well. + */ + public ConnectingGraph(int n) { + father = new int[n + 1]; + size = new int[n + 1]; + for (int i = 1; i <= n; i++) { + father[i] = i; + size[i] = 1; + } + } + + /** + Union function. + Find the root father, if not the same, union them together. + */ + public void connect(int a, int b) { + int rootA = find(a); + int rootB = find(b); + if (rootA != rootB) { + parent[a] = rootB; // doesn't mater which assigns to which one. + // Merge union of A into the union of B, so unionB should grow. + size[rootB] += size[rootA] + } + } + + /** + Returns the number of connected component nodes which include node a. + */ + public boolean query(int x) { + int rootX = find(x); + return size[rootX]; + } + + /* + Find function: find the root parent as the head for entire union. + If found parent as itself, return it. + Otherwise, recursively look for father and assign the result eventually. + */ + private int find(int x) { + if (father[x] == x) { + return x; // x is the root parent, return itself. + } + return father[x] = find(father[x]); + } +} +``` \ No newline at end of file diff --git a/Java/Connecting Graph III.java b/Java/Connecting Graph III.java new file mode 100644 index 0000000..1c9ae10 --- /dev/null +++ b/Java/Connecting Graph III.java @@ -0,0 +1,81 @@ +M +1520323016 +tags: Union Find + +还是UnionFind的变形, 这次是算有剩下多少个union. 其实非常简单, 维持一个全局变量count: +一开始count=n, 因为全是散装elements; 每次union, count--. + +``` +/* +Given n nodes in a graph labeled from 1 to n. There is no edges in the graph at beginning. + +You need to support the following method: +1. connect(a, b), add an edge to connect node a and node b. +2. query(), Returns the number of connected component in the graph + + +Example +5 // n = 5 +query() return 5 +connect(1, 2) +query() return 4 +connect(2, 4) +query() return 3 +connect(1, 4) +query() return 3 +*/ + +/** +Thoughts: +Not able to run on LintCode. + +Count the number of unions left +*/ + +public class ConnectingGraph { + // Placeholder for all the UninFind relationships + private int[] father = null; + private int count; + + /** + Initialize one element to each individual union. + */ + public ConnectingGraph(int n) { + father = new int[n + 1]; + count = n; + for (int i = 1; i <= n; i++) { + father[i] = i; + } + } + + /** + Union function. + Find the root father, if not the same, union them together. + */ + public void connect(int a, int b) { + int rootA = find(a); + int rootB = find(b); + if (rootA != rootB) { + count--; + parent[a] = rootB; // doesn't mater which assigns to which one. + } + } + + /** Return count of union left */ + public boolean query() { + return count; + } + + /* + Find function: find the root parent as the head for entire union. + If found parent as itself, return it. + Otherwise, recursively look for father and assign the result eventually. + */ + private int find(int x) { + if (father[x] == x) { + return x; // x is the root parent, return itself. + } + return father[x] = find(father[x]); + } +} +``` \ No newline at end of file diff --git a/Java/Connecting Graph.java b/Java/Connecting Graph.java new file mode 100644 index 0000000..405db07 --- /dev/null +++ b/Java/Connecting Graph.java @@ -0,0 +1,77 @@ +M +1520320428 +tags: Union Find + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + +``` +/* +LintCode +Given n nodes in a graph labeled from 1 to n. There is no edges in the graph at beginning. + +You need to support the following method: +1. connect(a, b), add an edge to connect node a and node b. +2. query(a, b), check if two nodes are connected + + +Example +5 // n = 5 +query(1, 2) return false +connect(1, 2) +query(1, 3) return false +connect(2, 4) +query(1, 4) return true + +*/ + +/* +Thoughts: +Not tested on lintcode. +Implementation of Union Find +*/ + +public class ConnectingGraph { + // Placeholder for all the UninFind relationships + private int[] father = null; + + /** + Initialize one element to each individual union. + */ + public ConnectingGraph(int n) { + father = new int[n + 1]; + for (int i = 1; i <= n; i++) { + father[i] = i; + } + } + + /** + Union function. + Find the root father, if not the same, union them together. + */ + public void connect(int a, int b) { + int rootA = find(a); + int rootB = find(b); + if (rootA != rootB) { + parent[a] = rootB; // doesn't mater which assigns to which one. + } + } + + /** Check if the two integer are in the same union */ + public boolean query(int a, int b) { + return find(a) == find(b); + } + + /* + Find function: find the root parent as the head for entire union. + If found parent as itself, return it. + Otherwise, recursively look for father and assign the result eventually. + */ + private int find(int x) { + if (father[x] == x) { + return x; // x is the root parent, return itself. + } + return father[x] = find(father[x]); + } +} +``` \ No newline at end of file diff --git a/Java/Construct Binary Tree from Inorder and Postorder Traversal.java b/Java/Construct Binary Tree from Inorder and Postorder Traversal.java old mode 100644 new mode 100755 index fcdb29a..f4fff05 --- a/Java/Construct Binary Tree from Inorder and Postorder Traversal.java +++ b/Java/Construct Binary Tree from Inorder and Postorder Traversal.java @@ -1,3 +1,18 @@ +M +1528731390 +tags: Array, Tree, DFS, Divide and Conquer + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + +``` /* Given inorder and postorder traversal of a tree, construct the binary tree. @@ -19,18 +34,18 @@ Thinking process: Know that the last element of PostOrder array is the root of the Binary tree. -Find this root from the InOrder array, which will be the middle point. The front-part of the inorder array will be left-tree, the end-part of the inorder array will be the right-tree. +Find this root from the InOrder array. The front-part of the inorder array will be left-tree, the end-part of the inorder array will be the right-tree. Trick part: 1. Need a helper function to perfrom divide/conquer. 2. Need to be careful when cutting the inorder and postorder array. - For inorder array, left array: (instart, middlePosition -1), right array: (middlePosition + 1, inend) - For postorder array: when cutting, we know the very last node is cut off already, so we just need to evenly split the rest array. - left array(postStart, postStart + (middlePosition - instart) - 1). - Note: (middlePositon - instart) means the length of the left-array/size of the left-tree - However, postStart + left-array-length exceed 1 over postorder-left-tree, hence minus 1 here. - right array(postStart + (middlePosition - instart), postend - 1) - Note: postStart + left-tree-length is exactly the starting point of the post-right-array. - Because the ending element is cut off previously to serve as root, we need to do (postend - 1) for correct postorder-right-tree. + For inorder array, left array: (instart, middlePosition -1), right array: (middlePosition + 1, inend) + For postorder array: when cutting, we know the very last node is cut off already, so we just need to evenly split the rest array. + left array(postStart, postStart + (middlePosition - instart) - 1). + Note: (middlePositon - instart) means the length of the left-array/size of the left-tree + However, postStart + left-array-length exceed 1 over postorder-left-tree, hence minus 1 here. + right array(postStart + (middlePosition - instart), postend - 1) + Note: postStart + left-tree-length is exactly the starting point of the post-right-array. + Because the ending element is cut off previously to serve as root, we need to do (postend - 1) for correct postorder-right-tree. */ @@ -48,11 +63,7 @@ Because the ending element is cut off previously to serve as root, we need to do public class Solution { - /** - *@param inorder : A list of integers that inorder traversal of a tree - *@param postorder : A list of integers that postorder traversal of a tree - *@return : Root of a tree - */ + public TreeNode buildTree(int[] inorder, int[] postorder) { if (inorder.length != postorder.length) { return null; @@ -85,3 +96,5 @@ public int findMid(int[] arr, int start, int end, int key) { } } + +``` \ No newline at end of file diff --git a/Java/Construct Binary Tree from Preorder and Inorder Traversal.java b/Java/Construct Binary Tree from Preorder and Inorder Traversal.java new file mode 100755 index 0000000..f8d2f4e --- /dev/null +++ b/Java/Construct Binary Tree from Preorder and Inorder Traversal.java @@ -0,0 +1,149 @@ +M +1519663383 +tags: Array, Tree, DFS, Divide and Conquer, Hash Table + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + +``` +/* +Given preorder and inorder traversal of a tree, construct the binary tree. + +Note +You may assume that duplicates do not exist in the tree. + +Example +Given inorder [1,2,3] and preorder [2,1,3] + +return a tree + + 2 + + / \ + +1 3 + +Tags Expand +Binary Tree +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + + /* +Thougths: +DFS with tracking of preorder/inorder sequence indexes. +preorder: start from root, traverse all left children, and then all right children. +inorder: if found the root node in the sequence, all indexes less than the root is left sub tree; same applies to right indexes. + +1. Use preorder head index as root +2. Find the root node index in inorder sequence. +3. split into subproblems: track by indexes +*//** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + Map map = new HashMap<>(); + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder == null || inorder == null || preorder.length != inorder.length) { + return null; + } + for (int i = 0; i < inorder.length; i++) { + map.put(inorder[i], i); + } + return dfs(preorder, 0, preorder.length - 1, inorder, 0, inorder.length - 1); + } + + public TreeNode dfs(int[] preorder, int preStart, int preEnd, + int[] inorder, int inStart, int inEnd) { + if (preStart > preEnd) { + return null; + } + TreeNode root = new TreeNode(preorder[preStart]); + int mid = map.get(preorder[preStart]); + + if (mid < 0) { + return null; + } + //root.left + root.left = dfs(preorder, preStart + 1, preStart + (mid - inStart), + inorder, inStart, mid - 1); + //root.right + root.right = dfs(preorder, preStart + (mid - inStart) + 1, preEnd, + inorder, mid + 1, inEnd); + + return root; + } +} + + +/** +Previous notes +Thinking process: +See 'Construct tree from inorder + postorder' as example. +This problem uses divide and conquer idea as well. +For preorder: the front node is the root of the tree. +For inorder: find the root in the middle of the array, then the left-side is left-tree, and the right-side is the right-tree. + */ +public class Solution { + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder.length != inorder.length) { + return null; + } + return buildTreeHelper(inorder, 0, inorder.length - 1, + preorder, 0, preorder.length - 1); + } + + public TreeNode buildTreeHelper(int[] inorder, int inStart, int inEnd, + int[] preorder, int preStart, int preEnd) { + if (inStart > inEnd) { + return null; + } + TreeNode root = new TreeNode(preorder[preStart]); + int mid = findMid(inorder, inStart, inEnd, preorder[preStart]); + + root.left = buildTreeHelper(inorder, inStart, mid - 1, + preorder, preStart + 1, preStart + (mid - inStart)); + root.right = buildTreeHelper(inorder, mid + 1, inEnd, + preorder, preStart + (mid - inStart) + 1, preEnd); + return root; + } + + public int findMid(int[] arr, int start, int end, int key) { + for (int i = start; i <= end; i++) { + if (arr[i] == key) { + return i; + } + } + return -1; + } +} + + +``` diff --git a/Java/Container With Most Water.java b/Java/Container With Most Water.java old mode 100644 new mode 100755 index 0b0f55f..997f392 --- a/Java/Container With Most Water.java +++ b/Java/Container With Most Water.java @@ -1,9 +1,20 @@ -类似木桶理论。盛水的最高取决于最低的那面墙。 -左右两墙,往中间跑动。 -另,若一面墙已经小于另外一面,就要移动,换掉矮墙(可能下一面更高,或更低);但决不能换掉当下的高墙,因为低墙已经limit的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) +M +1527960660 +tags: Two Pointers, Array + +#### Two Pointers +- 木桶理论。盛水的最高取决于最低的那面墙。 +- 左右两墙,往中间跑动。 +- 另:若一面墙已经小于另外一面,就要移动,换掉矮墙(可能下一面更高,或更低) +- 但决不能换掉当下的高墙,因为低墙已经limit的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + ``` /* -Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water. +Given n non-negative integers a1, a2, ..., an, +where each represents a point at coordinate (i, ai). +n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). +Find two lines, which together with x-axis forms a container, +such that the container contains the most water. Example Given [1,3,2], the max area of the container is 2. @@ -25,10 +36,6 @@ On the other hand, if lett wall > right wall, right--. */ public class Solution { - /** - * @param heights: an array of integers - * @return: an integer - */ public int maxArea(int[] heights) { if (heights == null || heights.length == 0) { return 0; @@ -37,7 +44,8 @@ public int maxArea(int[] heights) { int right = heights.length - 1; int maxWater = Integer.MIN_VALUE; while (left < right) { - maxWater = Math.max(maxWater, (right-left) * (heights[left] < heights[right] ? heights[left] : heights[right])); + int lowWall = heights[left] < heights[right] ? heights[left] : heights[right]; + maxWater = Math.max(maxWater, (right - left) * lowWall); if (heights[left] < heights[right]) { left++; } else { diff --git a/Java/Contains Duplicate II.java b/Java/Contains Duplicate II.java new file mode 100644 index 0000000..990f6e9 --- /dev/null +++ b/Java/Contains Duplicate II.java @@ -0,0 +1,86 @@ +E +1522013551 +tags: Array, Hash Table + +Unsorted array, 找出是否有duplicate elemenets: 必要条件是, 这两个element的index i,j 的大小最多相差k. + +#### HashSet +- 很巧妙地根据k range地条件, 把HashSet里面的值控制在[i - k, i] +- 每次不断地往set里面加新元素, 从set里减去末尾index的元素 +- 而set.add(x)如果遇到重复, 会return false. +- 一旦在这个length k 的 range里面, 有重复, 就符合条件. +- Time O(n) + +#### HashTable +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k +- Time O(nm), m = # of duplicates + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + +``` +/* +Given an array of integers and an integer k, +find out whether there are two distinct indices i and j in the array such that: +nums[i] = nums[j] and the absolute difference between i and j is at most k. +*/ + +/* +Thoughts: +1. Think of k as the upper limit of the range that we want to pick our i and j. +2. When i is in [0 ~ k], we can pick and j that's also in [0 ~ k]. This can be checked in a for loop, with a HashSet. +3. Once i pass k, we need to remove any value that's in range [0, i-k) from the set, because they are out of range. +They are no longer fit the condition to duplicate with nums[i], regardless if they are duplicates or not + +Note: set.add(..) return false if the value already exist in set. +*/ +class Solution { + public boolean containsNearbyDuplicate(int[] nums, int k) { + if (nums == null || nums.length == 0 || k <= 0) { + return false; + } + final Set set = new HashSet<>(); + for (int i = 0; i < nums.length; i++) { + if (i > k) { + set.remove(nums[i - k - 1]); + } + if (!set.add(nums[i])) { + return true; + } + } + return false; + } +} + +/* +Thoughts: +Store in hashmap. When there is a duplicate, check against k. +Though, quite slow: O(n * h), where h is the possible duplicates. In the extreme case when n = h, it becomes O(n^2) +*/ +class Solution { + public boolean containsNearbyDuplicate(int[] nums, int k) { + if (nums == null || nums.length == 0 || k <= 0) { + return false; + } + final Map> map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(nums[i])) { + for (int index : map.get(nums[i])) { + if (Math.abs(index - i) <= k) { + return true; + } + } + } else { + map.put(nums[i], new ArrayList<>()); + } + map.get(nums[i]).add(i); + } + return false; + } +} + +``` \ No newline at end of file diff --git a/Java/Contains Duplicate III.java b/Java/Contains Duplicate III.java new file mode 100644 index 0000000..b0c28c8 --- /dev/null +++ b/Java/Contains Duplicate III.java @@ -0,0 +1,119 @@ +M +1522013628 +tags: BST + +给一个unsorted array, 问, 是否有两个element, value相差最大为t, 而两个element的index 相差最大为k. + +Note: 虽然题目名字是Contains Duplicate, 但其实要找的两个element不是duplicate, 而是Math.abs(value1 - value2) <= t. + +#### TreeSet +- TreeSet还是一个set, 我们用来装已经visit过得item +- 如果window大小超过K, 那么把nums[i - k - 1] 去掉, 并且加上新的element +- 这里有个公式推算: (Math.abs(A-B) <= t) =>>>>> (-t <= A - B <= t) =>>>>>> A >= B - t, A <= B + t +- 也就是说, 如果对于 B = nums[i], 来说, 能找到一个target A, 满足上面的公式, 那么就可以 return true. +- Time O(nLogk), treeSet的大小不会超过k, 而 treeSet.ceiling(), treeSet.add(), treeSet.remove() 都是 O(logK) +- Space O(k) + +#### Note +- 与Contains Duplicate II 类似概念. TreeSet有BST 因此可以直接用, 而不用自己构建BST +- 简化题目里面的重要条件 Math.abs(A-B) <= t 而推断出 A >= B - t, A <= B + t +- 并且需要需要用 TreeSet.ceiling(x): return number greater or equal to x. 这个用法要记住吧, 没别的捷径. + +``` + +/* +Given an array of integers, find out whether there are two distinct indices i and j +in the array such that the absolute difference between nums[i] and nums[j] +is at most t and the absolute difference between i and j is at most k. +*/ + +/* +Thoughts: +Given Math.abs(A-B) <= t ->>> -t <= A - B <= t. Therefor, we want find A such as: +A >= B - t +A <= B + t +1. Need to find an element A thats greater to equal to (B-t), and also use the A needs to less or equal to (B+t) +For this, we can use a binary search tree, where to can quickly find the (B-t) and hence A. +Utilize TreeSet.ceiling() to find A +2. This binary search tree needs to be flexible in terms of removing item: +Exact same idea as in 'Contains Duplicate II': once i pass k, we need to remove any value that's in range [0, i-k) from the set, because they are out of range. +*/ +class Solution { + public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { + if (nums == null || nums.length == 0 || k <= 0 || t < 0) { + return false; + } + TreeSet treeSet = new TreeSet<>(); + for (int i = 0; i < nums.length; i++) { + Long target = treeSet.ceiling((long)nums[i] - t); + if (target != null && target <= (long)nums[i] + t) { + return true; + } + if (i >= k) { + treeSet.remove((long)nums[i - k]); + } + treeSet.add((long)nums[i]); + } + return false; + } +} + +/* +Incorrect: the index-based BST won't work. If item appears on both size of the tree, it's no longer possible to trace. Ex: [1, 3, 1], k=2, t=1 + +Also, if want to use BST, we can directly use TreeSet. +Thoughts: +1. Create binary search tree based on index, and store value/index in the node. +2. Traverse over each node of the tree, and compare the root against all children possible, to find abs(value diff) < t. +3. When abs(index diff) > k, stop diving deep. +*/ +class Solution { + class Node { + int value; + int index; + Node left; + Node right; + public Node(int value, int index) { + this.value = value; + this.index = index; + } + } + + public boolean containsNearbyAlmostDuplicate(int[] nums, int k, int t) { + if (nums == null || nums.length == 0) { + return false; + } + final Node root = buildBinarySearchTree(nums, 0, nums.length - 1); + return findMatch(null, root, k, t); + } + + private boolean findMatch(Node parent, Node node, int k, int t) { + if (node == null || (parent != null && Math.abs(parent.index - node.index) > k)) { + return false; + } + return validateDiff(node, node.left, k, t) || validateDiff(node, node.right, k, t) + || findMatch(node, node.left, k, t) || findMatch(node, node.right, k, t); + } + + private boolean validateDiff(Node nodeA, Node nodeB, int k, int t) { + if (nodeA == null || nodeB == null) { + return false; + } + return Math.abs(nodeA.value - nodeB.value) <= t && Math.abs(nodeA.index - nodeB.index) <= k; + } + + private Node buildBinarySearchTree(int[] nums, int start, int end) { + if (start > end || start < 0 || end >= nums.length ) { + return null; + } + if (start == end) { + return new Node(nums[start], start); + } + int mid = start + (end - start) / 2; + Node node = new Node(nums[mid], mid); + node.left = buildBinarySearchTree(nums, start, mid - 1); + node.right = buildBinarySearchTree(nums, mid + 1, end); + return node; + } +} +``` \ No newline at end of file diff --git a/Java/Contains Duplicate.java b/Java/Contains Duplicate.java new file mode 100644 index 0000000..ffad5be --- /dev/null +++ b/Java/Contains Duplicate.java @@ -0,0 +1,58 @@ +E +1522012573 +tags: Array, Hash Table + +无序数组, 找是否有重复element, return true/false. + +#### HashSet +- No brain: HashSet. +- Time O(n), Space O(n) + +#### Sort, Binary Search +- Arrays.sort(x): Time O(nLogN), Space O(1) +- 排序后, 重复数会排在一起, 然后 binary search + +``` +/* +Given an array of integers, find if the array contains any duplicates. +Your function should return true if any value appears at least twice in the array, +and it should return false if every element is distinct. +*/ + +/* +Use a hashSet. Cost: extra space. +*/ +class Solution { + public boolean containsDuplicate(int[] nums) { + if (nums == null || nums.length == 0) { + return false; + } + final HashSet set = new HashSet<>(); + for (int num : nums) { + if (set.contains(num)) { + return true; + } + set.add(num); + } + return false; + } +} + +/* +Sorry array, nLog(n) +*/ +class Solution { + public boolean containsDuplicate(int[] nums) { + if (nums == null || nums.length == 0) { + return false; + } + Arrays.sort(nums); + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] == nums[i + 1]) { + return true; + } + } + return false; + } +} +``` \ No newline at end of file diff --git a/Java/Contiguous Array.java b/Java/Contiguous Array.java new file mode 100644 index 0000000..8784cdb --- /dev/null +++ b/Java/Contiguous Array.java @@ -0,0 +1,76 @@ +M +1531902072 +tags: Hash Table + +TODO: how aout without chaning the input nums? + +``` +/* +Given a binary array, find the maximum length of a contiguous subarray with equal number of 0 and 1. + +Example 1: +Input: [0,1] +Output: 2 +Explanation: [0, 1] is the longest contiguous subarray with equal number of 0 and 1. + +Example 2: +Input: [0,1,0] +Output: 2 +Explanation: [0, 1] (or [1, 0]) is a longest contiguous subarray with equal number of 0 and 1. +Note: The length of the given binary array will not exceed 50,000. +*/ +/* +k = sum - i - 1 +check if map.containsKey(preSum - k). If so, Math.max(max, i - map.get(preSum - k)) +*/ + + +class Solution { + public int findMaxLength(int[] nums) { + if (nums == null || nums.length == 0) return 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] == 0) nums[i] = -1; + } + Map map = new HashMap<>(); + map.put(0, -1); + + int preSum = 0, max = 0; + for (int i = 0; i < nums.length; i++) { + preSum += nums[i]; + if (map.containsKey(preSum)) { + max = Math.max(max, i - map.get(preSum)); + } + if (!map.containsKey(preSum)) { + map.put(preSum, i); + } + } + + return max; + } +} + +// TODO: what if not reseting 0 -> -1, can we solve this? +// Also, inspired by Buy/Sell Stock https://leetcode.com/problems/contiguous-array/discuss/99655/Python-O(n)-Solution-with-Visual-Explanation +class Solution { + public int findMaxLength(int[] nums) { + if (nums == null || nums.length == 0) return 0; + Map map = new HashMap<>(); + map.put(0, -1); + + int preSum = 0, max = 0; + for (int i = 0; i < nums.length; i++) { + preSum += nums[i]; + int k = preSum - i - 1; + if (map.containsKey(preSum - k)) { + max = Math.max(max, i - map.get(preSum - k)); + } + if (!map.containsKey(preSum)) { + map.put(preSum, i); + } + } + + return max; + } +} + +``` \ No newline at end of file diff --git a/Java/Continuous Subarray Sum.java b/Java/Continuous Subarray Sum.java new file mode 100644 index 0000000..4ca8bcf --- /dev/null +++ b/Java/Continuous Subarray Sum.java @@ -0,0 +1,101 @@ +M +1522910259 +tags: Math, DP, Coordinate DP, Subarray + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + +``` +/* +Given a list of non-negative numbers and a target integer k, +write a function to check if the array has a continuous subarray of size +at least 2 that sums up to the multiple of k, that is, sums up to n*k where n is also an integer. + +Example 1: +Input: [23, 2, 4, 6, 7], k=6 +Output: True +Explanation: Because [2, 4] is a continuous subarray of size 2 and sums up to 6. +Example 2: +Input: [23, 2, 6, 4, 7], k=6 +Output: True +Explanation: Because [23, 2, 6, 4, 7] is an continuous subarray of size 5 and sums up to 42. +Note: +The length of the array won't exceed 10,000. +You may assume the sum of all the numbers is in the range of a signed 32-bit integer. +*/ + + +/* +Thoughts: +If iterate over range[0 ~ n], the move the range by [1 ~ n] steps +Time will become O(n^2) + +Utilize rangeSum[0 ~ i] and rangeSum[0 ~ j] +Calculate the diff + +dp[i] = sum up to index i +dp[0]: nums[0] +dp[i] = dp[i - 1] + nums[i]; + +O(n^2) +*/ + +class Solution { + public boolean checkSubarraySum(int[] nums, int k) { + if (nums == null || nums.length == 0) { + return false; + } + int n = nums.length; + int[] dp = new int[n]; // sum up to index i + dp[0] = nums[0]; + for (int i = 1; i < n; i++) { + dp[i] = dp[i - 1] + nums[i]; + } + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) { + int sum = dp[j] - dp[i] + nums[i]; + if (sum == k || (k != 0 && sum % k == 0)) { + return true; + } + } + } + + return false; + } +} + +/* +Thoughts: +If iterate over range[0 ~ n], the move the range by [1 ~ n] steps +Time will become O(n^2) +*/ + +class Solution { + public boolean checkSubarraySum(int[] nums, int k) { + if (nums == null || nums.length == 0) { + return false; + } + int n = nums.length; + for (int i = 0; i < n; i++) { + int sum = nums[i]; + for (int j = i + 1; j < n; j++) { + sum += nums[j]; + if (sum == k || (k != 0 && sum % k == 0)) { + return true; + } + } + } + + return false; + } +} +``` \ No newline at end of file diff --git a/Java/Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java b/Java/Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java new file mode 100755 index 0000000..e423290 --- /dev/null +++ b/Java/Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java @@ -0,0 +1,111 @@ +M +1531695678 +tags: Tree, Linked List, Stack +time: O(n) +space: O(n) + +给一个BST, convert成 sorted doubly DoublyListNode. + +#### Inorder Traversal, Linked List +- 会iterative traverse Binary Search Tree(Stack && handle left-dig-down) +- create Doubly-ListNode, 注意用一个dNode作为tail node of the list + +##### Iterative inorder traversal +- 在check right node的事后, +- 不论right == null or != null, 每次都要强行move to right. +- 如果不node = node.right, +- 很可能发生窘境: +- node always = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 + +``` +/* +LintCode +Convert a binary search tree to doubly linked list with in-order traversal. + +Example +Given a binary search tree: + + 4 + / \ + 2 5 + / \ +1 3 +return 1<->2<->3<->4<->5 + +Tags Expand +Linked List +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + * Definition for Doubly-ListNode. + * public class DoublyListNode { + * int val; + * DoublyListNode next, prev; + * DoublyListNode(int val) { + * this.val = val; + * this.next = this.prev = null; + * } + * } + */ + + +/* + Thoughts: + Inorder with 1 stack: peek add left till end, pop and add, then push right node. + + Everytime when pop out a node and add, make it a new boubllistnode + tail.next = curr + curr.pre = tail.next + tail = tail.next + + boarder case: if null, return a null. +*/ +public class Solution { + public DoublyListNode bstToDoublyList(TreeNode root) { + if (root == null) { + return null; + } + //Init stack + Stack stack = new Stack<>(); + TreeNode node = root; + stack.push(node); + + //Create DoublyListNode header + DoublyListNode dummy = new DoublyListNode(0); + DoublyListNode tail = dummy; + + while(!stack.isEmpty()) { + // Add left till leaf + while (node != null && node.left != null) { + stack.push(node.left); + node = node.left; + } + + //add node, and doubly link with prev node + node = stack.pop(); + DoublyListNode curr = new DoublyListNode(node.val); + tail.next = curr; + curr.prev = tail; + tail = tail.next; + + //check right node and add to stack + node = node.right; + if (node != null) { + stack.push(node); + } + } + + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Java/Convert Binary Search Tree to Sorted Doubly Linked List.java b/Java/Convert Binary Search Tree to Sorted Doubly Linked List.java new file mode 100644 index 0000000..a35a8e9 --- /dev/null +++ b/Java/Convert Binary Search Tree to Sorted Doubly Linked List.java @@ -0,0 +1,107 @@ +M +1533494672 +tags: Linked List, Tree, DFS, BST, Divide and Conquer +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + +``` +/* +LeetCode: https://leetcode.com/problems/convert-binary-search-tree-to-sorted-doubly-linked-list/description/ +Convert a BST to a sorted circular doubly-linked list in-place. +Think of the left and right pointers as synonymous to the previous and next pointers in a doubly-linked list. + +Let's take the following BST as an example, it may help you understand the problem better: + +We want to transform this BST into a circular doubly linked list. +Each node in a doubly linked list has a predecessor and successor. +For a circular doubly linked list, the predecessor of the first element is the last element, +and the successor of the last element is the first element. + +The figure below shows the circular doubly linked list for the BST above. +The "head" symbol means the node it points to is the smallest element of the linked list. + +Specifically, we want to do the transformation in place. After the transformation, +the left pointer of the tree node should point to its predecessor, +and the right pointer should point to its successor. +We should return the pointer to the first element of the linked list. + +The figure below shows the transformed BST. +The solid line indicates the successor relationship, +while the dashed line means the predecessor relationship. + +*/ + + +/* +// Definition for a Node. +class Node { + public int val; + public Node left; + public Node right; + + public Node() {} + + public Node(int _val,Node _left,Node _right) { + val = _val; + left = _left; + right = _right; + } +}; +*/ +/* +dfs, L, R, +Link L(head, tail) -> curr -> R(head, tail) +Return head in dfs +In main func: return head, smallest element +*/ +class Solution { + public Node treeToDoublyList(Node root) { + if (root == null) return root; + if (root.left == null && root.right == null) { + link(root, root); + return root; + } + + // link them + Node leftHead = treeToDoublyList(root.left); // left head + Node rightHead = treeToDoublyList(root.right); // right head + Node leftTail = leftHead != null ? leftHead.left : null; + Node rightTail = rightHead != null ? rightHead.left : null; + + Node head = null; + if (rightHead == null) { + head = leftHead; + link(leftTail, root); + link(root, leftHead); + } else if (leftHead == null){ + head = root; + link(root, rightHead); + link(rightTail, root); + } else { // both leftHead,rightHead exist + head = leftHead; + link(leftTail, root); + link(root, rightHead); + link(rightTail, leftHead); + } + + return head; + } + + private void link(Node nodeA, Node nodeB) { + nodeA.right = nodeB; + nodeB.left = nodeA; + } +} +``` \ No newline at end of file diff --git a/Java/Convert Expression to Polish Notation.java b/Java/Convert Expression to Polish Notation.java old mode 100644 new mode 100755 index 7046567..0be3671 --- a/Java/Convert Expression to Polish Notation.java +++ b/Java/Convert Expression to Polish Notation.java @@ -1,12 +1,29 @@ -还是Expression Tree (Min-Tree). -根据题意,Tree出来以后,来个Pre-order-traversal. +H +1524551317 +tags: Stack, Binary Tree, DFS, Expression Tree + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + ``` /* -Given an expression string array, return the Polish notation of this expression. (remove the parentheses) +LintCode Exercise + +Given an expression string array, +return the Polish notation of this expression. +(remove the parentheses) -Have you met this question in a real interview? Yes Example -For the expression [(5 − 6) * 7] (which represented by ["(", "5", "−", "6", ")", "*", "7"]), the corresponding polish notation is [* - 5 6 7] (which the return value should be ["*", "−", "5", "6", "7"]). +For the expression [(5 − 6) * 7] +(which represented by ["(", "5", "−", "6", ")", "*", "7"]), + +the corresponding polish notation is [* - 5 6 7] +(which the return value should be ["*", "−", "5", "6", "7"]). Clarification Definition of Polish Notation: @@ -18,100 +35,115 @@ */ /* +The expression tree will be something like below. '()' are not recorded by just used as weights: + * + / \ + - 7 + / \ + 5 6 Thoughts: -Build the expression tree, and do a pre-order-traversal, and record all nodes in the array list. +Build the expression tree, and do a pre-order-traversal, +and record all nodes in the array list. Let's practice expression tree build again. */ public class Solution { - class TreeNode { - String s; - int val; - TreeNode left; - TreeNode right; - public TreeNode(int val, String s) { - this.val = val; - this.s = s; - this.left = null; - this.right = null; - } - } - - public TreeNode build(String[] expression) { - if (expression == null || expression.length == 0) { - return null; - } - Stack stack = new Stack(); - int base = 0; - int val = 0; - - for (int i = 0; i < expression.length; i++) { - if (expression[i].equals("(")) { - base += 10; - continue; - } - if (expression[i].equals(")")) { - base -= 10; - continue; - } - val = getWeight(base, expression[i]); - TreeNode node = new TreeNode(val, expression[i]); - while (!stack.isEmpty() && node.val <= stack.peek().val) { - node.left = stack.pop(); - } - if (!stack.isEmpty()) { - stack.peek().right = node; - } - stack.push(node); - } - if (stack.isEmpty()) { - return null; - } - TreeNode rst = stack.pop(); - while (!stack.isEmpty()) { - rst = stack.pop(); - } - return rst; - } - - public int getWeight(int base, String s) { - if (s.equals("+") || s.equals("-")) { - return base + 1; - } - if (s.equals("*") || s.equals("/")) { - return base + 2; - } - return Integer.MAX_VALUE; - } + /********* Build Expression Tree *********/ + class TreeNode { + String s; + int val; + TreeNode left; + TreeNode right; + public TreeNode(int val, String s) { + this.val = val; + this.s = s; + this.left = null; + this.right = null; + } + } + + public TreeNode build(String[] expression) { + if (expression == null || expression.length == 0) { + return null; + } + Stack stack = new Stack(); + int base = 0; + int val = 0; + + for (int i = 0; i < expression.length; i++) { + if (expression[i].equals("(")) { + base += 10; + continue; + } + if (expression[i].equals(")")) { + base -= 10; + continue; + } + val = getWeight(base, expression[i]); + TreeNode node = new TreeNode(val, expression[i]); + + // Use monotonous stack to build minimum binary tree + while (!stack.isEmpty() && node.val <= stack.peek().val) { + node.left = stack.pop(); + } + if (!stack.isEmpty()) { + stack.peek().right = node; + } + stack.push(node); + } + if (stack.isEmpty()) { + return null; + } + + // Find root, witch is the minimum value + TreeNode rst = stack.pop(); + while (!stack.isEmpty()) { + rst = stack.pop(); + } + return rst; + } + + public int getWeight(int base, String s) { + if (s.equals("+") || s.equals("-")) { + return base + 1; + } + if (s.equals("*") || s.equals("/")) { + return base + 2; + } + return Integer.MAX_VALUE; + } + /********* Build Expression Tree *********/ + /** * @param expression: A string array * @return: The Polish notation of this expression */ public ArrayList convertToPN(String[] expression) { - ArrayList rst = new ArrayList(); - if (expression == null || expression.length == 0) { - return rst; - } - TreeNode root = build(expression); - preTraversal(rst, root); - - return rst; + ArrayList rst = new ArrayList(); + if (expression == null || expression.length == 0) { + return rst; + } + TreeNode root = build(expression); + preTraversal(rst, root); + + return rst; } public void preTraversal(ArrayList rst, TreeNode node){ - if (node == null) { - return; - } - if (node.left == null && node.right == null) { - rst.add(node.s); - return; - } - rst.add(node.s); - preTraversal(rst, node.left); - preTraversal(rst, node.right); + if (node == null) { + return; + } + if (node.left == null && node.right == null) { + rst.add(node.s); + return; + } + rst.add(node.s); + preTraversal(rst, node.left); + preTraversal(rst, node.right); } } + ``` \ No newline at end of file diff --git a/Java/Convert Expression to Reverse Polish Notation.java b/Java/Convert Expression to Reverse Polish Notation.java old mode 100644 new mode 100755 index 6b0df9f..a8c9fa5 --- a/Java/Convert Expression to Reverse Polish Notation.java +++ b/Java/Convert Expression to Reverse Polish Notation.java @@ -1,12 +1,25 @@ -用build expression tree开头。 -这个里面把TreeNode就当做成我们需要的node,里面扩展成有left/right child的node. -这题,目的是建造tree,然后来个post-traversal就行了。 +H +1524551537 +tags: Stack, Binary Tree, DFS, Expression Tree + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + ``` /* -Given an expression string array, return the Reverse Polish notation of this expression. (remove the parentheses) +LintCode Exercise + +Given an expression string array, +return the Reverse Polish notation of this expression. +(remove the parentheses) Example -For the expression [3 - 4 + 5] (which denote by ["3", "-", "4", "+", "5"]), return [3 4 - 5 +] (which denote by ["3", "4", "-", "5", "+"]) +For the expression [3 - 4 + 5] (which denote by ["3", "-", "4", "+", "5"]), +return [3 4 - 5 +] (which denote by ["3", "4", "-", "5", "+"]) Tags: LintCode Copyright Stack @@ -93,9 +106,6 @@ public ArrayList convertToRPN(String[] expression) { return rst; } TreeNode root = build(expression); - if (root == null) { - return rst; - } postTraversal(rst, root); return rst; } @@ -104,12 +114,8 @@ public void postTraversal(ArrayList rst, TreeNode node){ if (node == null) { return; } - if (node.left != null) { - postTraversal(rst, node.left); - } - if (node.right != null) { - postTraversal(rst, node.right); - } + postTraversal(rst, node.left); + postTraversal(rst, node.right); rst.add(node.s); } } diff --git a/Java/Convert Integer A to Integer B.java b/Java/Convert Integer A to Integer B.java old mode 100644 new mode 100755 index 97b3974..5120ebc --- a/Java/Convert Integer A to Integer B.java +++ b/Java/Convert Integer A to Integer B.java @@ -1,4 +1,18 @@ +E +1522132721 +tags: Bit Manipulation + +把Integer A 转换成 Integer B 需要改变多少bits? + +#### Bit Manipulation +- a^b 显示出bit format里面有不同binary code的数位. +- 每次 (a^b)>>i 移动i位之后, 再 & 1时其实是指留下这一位的数字. +- count +- 其实用到了 ^ 找不同的bit, >> 移位, &1 mask + +``` /* +LintCode Determine the number of bits required to convert integer A to integer B Example @@ -32,3 +46,5 @@ public static int bitSwapRequired(int a, int b) { }; + +``` \ No newline at end of file diff --git a/Java/Convert Sorted Array to Binary Search Tree.java b/Java/Convert Sorted Array to Binary Search Tree.java new file mode 100644 index 0000000..b9a238e --- /dev/null +++ b/Java/Convert Sorted Array to Binary Search Tree.java @@ -0,0 +1,133 @@ +E +1519630976 +tags: Tree, DFS, Divide and Conquer + +如题, build balanced BST from sorted array + +#### DFS +- Binary Search Tree特点: 左边的node都比右边的node小. +- height balance, subtree height 相差<1, 必须左右sub tree均分. 做DFS(num, start, end) +- 在每一个level, 找到中间点, 然后分割2半, 继续dfs +- Divide and Conquer +- time/space: O(n), visit all nodes, no redundant visits. + +``` +/* +Given an array where elements are sorted in ascending order, +convert it to a height balanced BST. + +For this problem, a height-balanced binary tree is defined as a binary tree + in which the depth of the two subtrees of every node never differ by more than 1. + + +Example: + +Given the sorted array: [-10,-3,0,5,9], + +One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST: + + 0 + / \ + -3 9 + / / + -10 5 +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Thoughts: to keep height diff < 1, need make sure left-sub and right sub tree have relatively same amount of nodes. +Divide the nums from mid point, DFS with index +*/ +class Solution { + public TreeNode sortedArrayToBST(int[] nums) { + if (nums == null || nums.length == 0) { + return null; + } + return dfs(nums, 0, nums.length - 1); + } + + public TreeNode dfs(int[] nums, int start, int end) { + if (start == end) { + return new TreeNode(nums[start]); + } + if (start > end || end >= nums.length) { + return null; + } + + int mid = start + (end - start) / 2; + TreeNode node = new TreeNode(nums[mid]); + node.left = dfs(nums, start, mid - 1); + node.right = dfs(nums, mid + 1, end); + return node; + } +} + + + + + +/* +LintCode +Given a sorted (increasing order) array, Convert it to create a binary tree with minimal height. + +Have you met this question in a real interview? Yes +Example +Given [1,2,3,4,5,6,7], return + + 4 + / \ + 2 6 + / \ / \ +1 3 5 7 +Note +There may exist multiple valid solutions, return any of them. + +Tags Expand +Cracking The Coding Interview Recursion Binary Tree + +Thoughts: +1. Find middle point x. +2. All index before x, goes to left of the tree. Same apply to right tree + build sub array and pass alone: we can pass index start, end. + use parent node and pass along +3. Recur on left side array. + +*/ + +//Binary Search的感觉. 中间一开两半, divde and conquer,左右各自recursive下去build left/right child. +public class Solution { + /** + * @param A: an integer array + * @return: a tree node + */ + public TreeNode sortedArrayToBST(int[] A) { + TreeNode root = null; + if (A == null || A.length == 0) { + return root; + } + root = helper(0, A.length - 1, A); + return root; + } + + public TreeNode helper(int start, int end, int[] A) { + if (start > end) { + return null; + } + //add middle node + int mid = start + (end - start)/2; + TreeNode node = new TreeNode(A[mid]); + //Split and append child + node.left = helper(start, mid - 1, A); + node.right = helper(mid + 1, end, A); + return node; + } +} +``` diff --git a/Java/Convert Sorted List to Binary Search Tree.java b/Java/Convert Sorted List to Binary Search Tree.java old mode 100644 new mode 100755 index 3295e98..5382f64 --- a/Java/Convert Sorted List to Binary Search Tree.java +++ b/Java/Convert Sorted List to Binary Search Tree.java @@ -1,84 +1,142 @@ -Divide and Conquer +M +1519792143 +tags: Linked List, DFS, BST, Divide and Conquer -找到mid。 -然后把root = mid.next +如题, 把一个sorted singly linked list 转换成一个 height balanced BST -然后开始sortedListToBST(mid.next.next); //后半段 -mid.next = null;//非常重要,要把后面拍过序的断掉 -sortedListToBST(head); //从头开始的前半段 +#### DFS +- Divide and Conquer +- 找到mid node +- 然后分割两半, 分别dfs做各自两个subtree: node.left,node.right +- 用长度来定位mid, 每次找中间点做root, 然后前半段, 后半段分别dfs with length. +- 用快慢pointer 找到mid. Better: 不用traverse entire linked list - -最后root.left, root.right merge一下。 +#### Details +- slowPointer = node; +- fastPointer = node.next; +- 然后把root = mid.next +- 然后开始sortedListToBST(mid.next.next); //后半段 +- mid.next = null;//非常重要,要把后面拍过序的断掉 +- sortedListToBST(head); //从头开始的前半段 +- 最后root.left, root.right merge一下。 ``` /* Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. +For this problem, a height-balanced binary tree is defined as a binary tree +in which the depth of the two subtrees of every node never differ by more than 1. + + +Example: + +Given the sorted linked list: [-10,-3,0,5,9], + +One possible answer is: [0,-3,9,-10,null,5], which represents the following height balanced BST: + + 0 + / \ + -3 9 + / / + -10 5 + Example Tags Expand Recursion Linked List - -Thinking Process: -Find the middle point of the list. -Left of the mid will be left-tree, right of the mid node will be right-tree. - */ /** - * Definition for ListNode. + * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; - * ListNode(int val) { - * this.val = val; - * this.next = null; - * } - * } - * Definition of TreeNode: - * public class TreeNode { - * public int val; - * public TreeNode left, right; - * public TreeNode(int val) { - * this.val = val; - * this.left = this.right = null; - * } + * ListNode(int x) { val = x; } * } - */ -public class Solution { - /** - * @param head: The first node of linked list. - * @return: a tree node - */ - public TreeNode sortedListToBST(ListNode head) { + */ + +/* +Thoughts: +Slow/fast pointer to find mid +*/ +class Solution { + public TreeNode sortedListToBST(ListNode head) { if (head == null) { return null; - } else if (head.next == null) { + } else if (head.next == null) { // be carefull in dfs return new TreeNode(head.val); } - ListNode mid = findMiddle(head); - TreeNode root = new TreeNode(mid.next.val); - TreeNode right = sortedListToBST(mid.next.next); - mid.next = null; - TreeNode left = sortedListToBST(head); - - root.left = left; - root.right = right; + ListNode midNode = findMid(head); + TreeNode root = new TreeNode(midNode.next.val); + root.right = sortedListToBST(midNode.next.next); + // Establish end point for remaining left list + midNode.next = null; + root.left = sortedListToBST(head); return root; } + /* + Always check if the fastNode can move forward for 2 steps + */ + public ListNode findMid(ListNode node) { + ListNode fastNode = node.next; + while (fastNode.next != null && fastNode.next.next != null) { + node = node.next; + fastNode = fastNode.next.next; + } + return node; + } +} + + +/* +Thoughts: +0. find length of elements +1. Pass length into dfs function, find mid element, use it as root. +2. Set left,right, repeat dfs +*/ +class Solution { + public TreeNode sortedListToBST(ListNode head) { + if (head == null) { + return null; + } + ListNode node = head; + int end = 0; + while (node != null) { + end++; + node = node.next; + } + return dfs(head, end); + } - public ListNode findMiddle(ListNode head) { - ListNode slow = head; - ListNode fast = head.next; - while (fast.next != null && fast.next.next != null) { - slow = slow.next; - fast = fast.next.next; + public TreeNode dfs(ListNode node, int end) { + if (node == null || end < 0) { + return null; } - return slow; + int mid = end / 2; + int index = 0; + ListNode dummy = new ListNode(-1); + dummy.next = node; + while (index < mid) { + index++; + node = node.next; + } + + TreeNode root = new TreeNode(node.val); + root.left = dfs(dummy.next, mid - 1); + root.right = dfs(node.next, end - mid - 1); + + return root; } } -``` \ No newline at end of file +/* +Previous notes: +Find the middle point of the list. +Left of the mid will be left-tree, right of the mid node will be right-tree. +*/ + + +``` diff --git a/Java/Copy Books.java b/Java/Copy Books.java new file mode 100644 index 0000000..45a5153 --- /dev/null +++ b/Java/Copy Books.java @@ -0,0 +1,273 @@ +H +1518424818 +tags: DP, Binary Search, Partition DP + +给一串书pages[i], k个人, pages[i] 代表每本书的页数. k个人从不同的点同时开始抄书. + +问, 最快什么时候可以抄完? + +#### Partition DP +- 第一步, 理解题目要求的问题: 前k个人copy完n本书, 找到最少的用时; 也可以翻译成: `n本书, 让k个人来copy, 也就是分割成k段`. +- 最后需要求出 dp[n][k]. 开: int[n+1][k+1]. +- 原理: +- 1. 考虑最后一步: 在[0 ~ n - 1]本书里, 最后一个人可以选择copy 1 本, 2 本....n本, 每一种切割的方法的结果都不一样 +- 2. 讨论第k个人的情况, 在 j = [0 ~ i] 循环. 而循环j时候最慢的情况决定 第k个人的结果(木桶原理): `Math.max(dp[j][k - 1], sum)`. +- 3. 其中: `dp[j][k-1]` 是 [k-1]个人读完j本书的结果, 也就是著名的`上一步`. 这里循环考虑的是第k个人不同的j种上一步 : ) +- 4. 循环的结果, 是要存在 dp[i][k] = Math.min(Math.max(dp[j][k - 1], sum[j, i]), loop over i, k, j = [i ~ 0]) +- Time: O(kn^2), space O(nk) + +##### Init +- Init: dp[0][0] = 0, 0个人0本书 +- Integer.MAX_VALUE的运用: +- 当 i = 1, k = 1, 表达式: dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); +- 唯一可行的情况就只有一种: i=0, k=0, 刚好 0 个人 copy 0 本书, dp[0][0] = 0. +- 其他情况, i = 1, k = 0, 0 个人读 1本书, 不可能发生: 所以用Integer.MAX_VALUE来冲破 Math.max, 维持荒谬值. +- 当 i=0, k=0 的情况被讨论时候, 上面的方程式才会按照实际情况计算出 dp[i][k] +- 这道题的init是非常重要而tricky的 + +##### 计算顺序 +- k个人, 需要一个for loop; +- k个人, 从copy1本书开始, 2, 3, ... n-1,所以 i=[1, n], 需要第二个for loop +- 在每一个i上, 切割的方式可以有[0 ~ i] 中, 我们要计算每一种的worst time + +##### 滚动数组 +- [k] 只有和 [k - 1] 相关 +- Space: O(n) + +#### Binary Search +- 根据: 每个人花的多少时间(time)来做binary search: 每个人花多久时间, 可以在K个人之内, 用最少的时间完成? +- time variable的范围不是index, 也不是page大小. 而是[minPage, pageSum] +- validation 的时候注意3种情况: 人够用 k>=0, 人不够所以结尾减成k<0, 还有一种是time(每个人最多花的时间)小于当下的页面, return -1 +- O(nLogM). n = pages.length; m = sum of pages. + + +``` +/* +LintCode +Given n books and the ith book has A[i] pages. +You are given k people to copy the n books. + +n books list in a row and each person can claim a continous range of the n books. +For example one copier can copy the books from ith to jth continously, +but he can not copy the 1st book, 2nd book and 4th book (without 3rd book). + +They start copying books at the same time and they all cost 1 minute to copy 1 page of a book. +What's the best strategy to assign books so that the slowest copier can finish at earliest time? + +*/ + +/* +// A easier/consistent way to write the dp: +Thoughts: +1. dp[i][k]: divide i books into k dividions, for k people to read. +2. Within each division, we need to know where the cut will be. + Use j [0 ~ i] to loop all possible division spots. +*/ +public class Solution { + public int copyBooks(int[] pages, int K) { + if (pages == null || pages.length == 0) { + return 0; + } + + int n = pages.length; + if (K > n) { + K = n; + } + int[][] dp = new int[n + 1][K + 1]; + + //dp[0][0~n] = Integer.MAX_VALUE; // 0 people read n books + dp[0][0] = 0; // 0 people read 0 book + for (int i = 1; i <= n; i++) { + dp[i][0] = Integer.MAX_VALUE; + } + + for (int i = 1; i <= n; i++) {// read i books + for (int k = 1; k <= K; k++) { // k people + int sum = 0; + dp[i][k] = Integer.MAX_VALUE; + for (int j = i; j >= 0; j--) { // last person k read from [j ~ i] which uses 'sum' time + dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); + if (j > 0) { + sum += pages[j - 1]; + } + } + } + } + + return dp[n][K]; + } +} + +/* +Thoughts: +Considering k people finishing i books. +If last person read some books, then all the rest people just need to: +k - 1 people to finish j books, and the kth person read [j, j + 1, ..., i-1] books + +Overall possible time spent is determined by the slowest person(bucket theory): +Either dp[k - 1][j] or (pages[j] + pages[j + 1] + ... + pages[i - 1]) += Max{dp[k - 1][j], pages[j] + pages[j + 1] + ... + pages[i - 1]} + +Of course we weant to minimize the overall time spent: +take min of the above equation, where j = 0 ~ i + +dp[k][i]: time spent for k people to read i books. +dp[k][i] = Min{Max{dp[k - 1][j] + sum(pages[j ~ i-1])}}, where j = 0 ~ i + +Return dp[k][i] + +init: dp[0][0] = 0; time spent for 0 people copy 0 books + +*/ +public class Solution { + public int copyBooks(int[] pages, int K) { + if (pages == null || pages.length == 0) { + return 0; + } + + int n = pages.length; + if (K > n) { + K = n; + } + int[][] dp = new int[K + 1][n + 1]; + + //dp[0][0~n] = Integer.MAX_VALUE; // 0 people read n books + dp[0][0] = 0; // 0 people read 0 book + for (int i = 1; i <= n; i++) { + dp[0][i] = Integer.MAX_VALUE; + } + + for (int k = 1; k <= K; k++) { // k people + for (int i = 1; i <= n; i++) {// read i books + int sum = 0; + dp[k][i] = Integer.MAX_VALUE; + for (int j = i; j >= 0; j--) { // person k read from j -> i + dp[k][i] = Math.min(dp[k][i], Math.max(dp[k - 1][j], sum)); + if (j > 0) { + sum += pages[j - 1]; + } + } + } + } + + return dp[K][n]; + } +} + +// Rolling array +public class Solution { + public int copyBooks(int[] pages, int K) { + if (pages == null || pages.length == 0) { + return 0; + } + + int n = pages.length; + if (K > n) { + K = n; + } + int[][] dp = new int[2][n + 1]; + + //dp[0][0~n] = Integer.MAX_VALUE; // 0 people read n books + dp[0][0] = 0; // 0 people read 0 book + for (int i = 1; i <= n; i++) { + dp[0][i] = Integer.MAX_VALUE; + } + + for (int k = 1; k <= K; k++) { // k people + for (int i = 1; i <= n; i++) {// read i books + int sum = 0; + dp[k % 2][i] = Integer.MAX_VALUE; + for (int j = i; j >= 0; j--) { // person k read from j -> i + dp[k % 2][i] = Math.min(dp[k % 2][i], Math.max(dp[(k - 1) % 2][j], sum)); + if (j > 0) { + sum += pages[j - 1]; + } + } + } + } + + return dp[K % 2][n]; + } +} + +/* +Thoughts: +Stupid way of asking 'slowest copier to finish at earliest time'. It does not make sense. +The question does not provide speed, what does 'slow/fast' mean? +The question does not distinguish each copier, what does 'earliest' time mean? + +Given the sample results, it's asking: +what's the minimum time can be used to finish all books with given k people. + +- 1 book cannot be split to give to multiple people +- cannot sort the book array + +A person at minimum needs to finish 1 book, and a person at maximum can read all books. +should find x in [min, max] such that all books can be finished. +The person can read at most x mins, but don't over-read if next book in the row can't be covered within x mins. + +It's find to not use up all copiers. +*/ +public class Solution { + public int copyBooks(int[] pages, int k) { + if (pages == null || pages.length == 0) { + return 0; + } + int m = pages.length; + int min = Integer.MAX_VALUE; + int sum = 0; + + // Find minimum read mins + for (int page : pages) { + min = Math.min(min, page); + sum += page; + } + + int start = min; + int end = sum; + while (start + 1 < end) { + int mid = start + (end - start) / 2; // mid: total time spent per person + int validation = validate(pages, mid, k); + if (validation < 0) { + start = mid; + } else { + end = mid; + } + } + + if (validate(pages, start, k) >= 0) { + return start; + } else { + return end; + } + } + + /* + Validate if all copies are utilized to finish. + Return 0 if k used to finish books. + Return negative needing more copier, which means value is too small. + Return postivie, if value is too big, and copies are not utilized. + */ + private int validate(int[] pages, int totalTimePerPerson, int k) { + int temp = 0; + for (int i = 0; i < pages.length; i++) { + temp += pages[i]; + if (temp == totalTimePerPerson) { + temp = 0; + k--; + } else if (temp < totalTimePerPerson) { + if (i + 1 >= pages.length || temp + pages[i + 1] > totalTimePerPerson) { + // no next page; or not enough capacity to read next page + temp = 0; + k--; + } + // else: (i + 1 < pages.length && temp + pages[i + 1] <= totalTimePerPerson) + } else { + return -1; + } + } + return k; + } +} + +``` \ No newline at end of file diff --git a/Java/Copy List with Random Pointer.java b/Java/Copy List with Random Pointer.java old mode 100644 new mode 100755 index 481895f..dc0f71f --- a/Java/Copy List with Random Pointer.java +++ b/Java/Copy List with Random Pointer.java @@ -1,15 +1,33 @@ +M +1527962398 +tags: Hash Table, Linked List +time: O(n) +space: O(1) + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List +- Basic Implementation of copy linked list: +- use node and dummy to hold new list, 遍历head.next .... null. +- Map 在这里用来: 1. avoid creating same node; 2. return the item if existing +- map 的 key全部是old object, 新的key全部是 newly created object +- 每一步都check map里面有没有head. 没有? 加上 +- 每一步都check map里面有没有head.random. 没有? 加上 + +``` /* -31% Accepted + A linked list is given such that each node contains an additional random pointer which could point to any node in the list or null. Return a deep copy of the list. -Example -Tags Expand -Hash Table Linked List +Hide Company Tags Microsoft Uber +Hide Tags Hash Table Linked List +Hide Similar Problems (M) Clone Graph -*/ +LeetCode: Hard +*/ /** * Definition for singly-linked list with a random pointer. @@ -21,7 +39,6 @@ */ /* - Recap: 12.10.2015 Iterative through the list. Use a dummyHead and return dummyHead.next at the end. In each iteration, check if Head is already exist, or make a new one @@ -31,34 +48,30 @@ border case: if head == null, return null */ - -public class Solution { +class Solution { public RandomListNode copyRandomList(RandomListNode head) { if (head == null) { - return null; + return head; } //creat node, used to link all nodes - RandomListNode dummy = new RandomListNode(0); - RandomListNode node = dummy; - RandomListNode newNode; + RandomListNode node = new RandomListNode(0); + RandomListNode dummy = node; //HashMap to mark node - HashMap map = new HashMap(); + HashMap map = new HashMap<>(); while(head != null) { //process head. (we already know head!=null) if (!map.containsKey(head)) { map.put(head, new RandomListNode(head.label)); } - newNode = map.get(head); - node.next = newNode; + node.next = map.get(head); //process head.random if (head.random != null) { if(!map.containsKey(head.random)) { map.put(head.random, new RandomListNode(head.random.label)); } - newNode = map.get(head.random); - node.next.random = newNode; + node.next.random = map.get(head.random); } node = node.next; head = head.next; @@ -67,6 +80,7 @@ public RandomListNode copyRandomList(RandomListNode head) { } } + /* Thinking process: 1. Loop through the original list @@ -80,10 +94,6 @@ public RandomListNode copyRandomList(RandomListNode head) { */ public class Solution { - /** - * @param head: The head of linked list with a random pointer. - * @return: A new head of a deep copy of the list. - */ public RandomListNode copyRandomList(RandomListNode head) { if (head == null) { return null; @@ -118,3 +128,44 @@ public RandomListNode copyRandomList(RandomListNode head) { } } +//Same soluton as above, but split populating map && deep copy, which is not as efficient as above +//Save all possible nodes into HashMap + //Deep copy the list, before adding any node, check map +public class Solution { + public RandomListNode copyRandomList(RandomListNode head) { + if (head == null) { + return null; + } + //Populate map + HashMap map = new HashMap(); + RandomListNode node = head; + RandomListNode newNode; + while(node != null) { + if (!map.containsKey(node)) { + newNode = new RandomListNode(node.label); + map.put(node, newNode); + } + if (node.random != null && !map.containsKey(node.random)) { + newNode = new RandomListNode(node.random.label); + map.put(node.random, newNode); + } + node = node.next; + } + //Deep copy + node = head; + newNode = new RandomListNode(0); + RandomListNode dummy = newNode; + while (node != null) { + newNode.next = map.get(node); + if (node.random != null) + newNode.next.random = map.get(node.random); + newNode = newNode.next; + + node = node.next; + } + + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Java/Cosine Similarity.java b/Java/Cosine Similarity.java old mode 100644 new mode 100755 index c71919e..4c68482 --- a/Java/Cosine Similarity.java +++ b/Java/Cosine Similarity.java @@ -1,7 +1,14 @@ -按题目意思,写出来就好了。 +E +1522133014 +tags: Basic Implementation + +根据 Cosine Similarity 的公式, basic implementation + ``` /* -Cosine similarity is a measure of similarity between two vectors of an inner product space that measures the cosine of the angle between them. +LintCode +Cosine similarity is a measure of similarity between two vectors of an inner product space +that measures the cosine of the angle between them. The cosine of 0° is 1, and it is less than 1 for any other angle. See wiki: Cosine Similarity diff --git a/Java/Count 1 in Binary.java b/Java/Count 1 in Binary.java old mode 100644 new mode 100755 index 1059210..058777f --- a/Java/Count 1 in Binary.java +++ b/Java/Count 1 in Binary.java @@ -1,4 +1,19 @@ +E +1522133098 +tags: Bit Manipulation + +count 一个 32-bit number binary format 里面有多少1 + +#### Bit Manipulation +- shift >> i +- apply mask & 1 + +#### Convert to string O(n) space +可以把integer -> string -> char array. + +``` /* +LintCode Count how many 1 in binary representation of a 32-bit integer. Example @@ -19,7 +34,19 @@ 2. convert char[] into integer using Character.getNumericValue() */ - +/* +Thoughts: +Shift the 32 bit integer and apply mask 1 +*/ +public class Solution { + public int countOnes(int num) { + int count = 0; + for (int i = 1; i <= 32; i++) { + count += num >> i & 1; + } + return count; + } +}; @@ -41,3 +68,5 @@ public int countOnes(int num) { return sum; } }; + +``` \ No newline at end of file diff --git a/Java/Count Complete Tree Nodes.java b/Java/Count Complete Tree Nodes.java new file mode 100644 index 0000000..160bd40 --- /dev/null +++ b/Java/Count Complete Tree Nodes.java @@ -0,0 +1,161 @@ +M +1521783524 +tags: Binary Search, Tree + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样, 如果一样, 直接 2 ^ h - 1就好 +- 不一样的话, 再DFS + +##### Trick +- 直接DFS会timeout, O(n), 其实可以optimize +- to pass the test with O(h^2), 位运算: Math.pow(2, h) = 2 << (h - 1). 神奇! +- 2 << 1就是把所有bits往左移动一位, 也就是 * 2 + +#### Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +``` +/* +Given a complete binary tree, count the number of nodes. + +Definition of a complete binary tree from Wikipedia: +In a complete binary tree every level, except possibly the last, is completely filled, +and all nodes in the last level are as far left as possible. +It can have between 1 and 2h nodes inclusive at the last level h. + +https://en.wikipedia.org/wiki/Binary_tree#Types_of_binary_trees + */ + + + /** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Thoughts: +Complate tree: last level may not be complete + +Can dfs to traverse the tree: countNodes(root.left) + countNodes(root.right) + 1; +However, time exceeds: we don't really need to go through all. + +Check if the tree of current root is perfect binary tree. If so, Pow(2, height), otherwise, dfs +Time: O(h^2) + +Note: Using Math.pow(2, x) will time out. +*/ +class Solution { + public int countNodes(TreeNode root) { + if (root == null) { + return 0; + } + int leftDepth = 0; + int rightDepth = 0; + TreeNode node = root; + while (node != null) { + leftDepth++; + node = node.left; + } + node = root; + while (node != null) { + rightDepth++; + node = node.right; + } + if (leftDepth == rightDepth) { + return (2 << (leftDepth - 1)) - 1; + } + return countNodes(root.left) + countNodes(root.right) + 1; + } +} + + +/* +Sampe approach, in different format: + */ +class Solution { + public int countNodes(TreeNode root) { + if (root == null) { + return 0; + } + int leftDepth = getLeftHeight(root) + 1; + int rightDepth = getRightHeight(root) + 1; + + if (leftDepth == rightDepth) { + return (2 << (leftDepth - 1)) - 1; + } + return countNodes(root.left) + countNodes(root.right) + 1; + } + + private int getLeftHeight(TreeNode node) { + int count = 0; + while (node.left != null) { + node = node.left; + count++; + } + return count; + } + + private int getRightHeight(TreeNode node) { + int count = 0; + while (node.right != null) { + node = node.right; + count++; + } + return count; + } +} + + + +/* +Thoughts: +Iteratively, sort of. Height calculation dfs is mandatory. +Calculate left-child depth +If leftChild.left depth === rightChild.left depth: +- We can count all left nodes + root, which is precisely: 1 << h = 2 << (h - 1) = 2 ^ h. +- Then move to root.right, and h-- +If leftChild.left depth > rightChild.left depth, that indicates right has a missing left-leaf. +- If draw this out on paper, this actually completely cuts off all right node's bottom level +- We can count all right nodes + root, which is precisely: 1 << (h - 1) = 2 ^ (h - 1); // last level was wiped out +- Then move to root.left, and h-- + +Note: when root==null, height shall be -1 for edge case purpose. +*/ +class Solution { + private int leftMostLeafDepth(TreeNode root) { + if (root == null) + return -1; + int height = 0; + while (root.left != null) { + height++; + root = root.left; + } + return height; + } + + public int countNodes(TreeNode root) { + int nodes = 0; + int h = leftMostLeafDepth(root); // h: leftLeafDepthForRoot + while (root != null) { + int LeafHeightForRightChild = leftMostLeafDepth(root.right); + if (LeafHeightForRightChild == h - 1) { + // Cut off all left children + root + nodes += 1 << h; // 2 ^ h + root = root.right; + } else { + nodes += 1 << (h - 1); // 2 ^ (h - 1) + root = root.left; + } + h--; // Proceed to next level + } + return nodes; + } +} +``` \ No newline at end of file diff --git a/Java/Count Primes.java b/Java/Count Primes.java new file mode 100755 index 0000000..a922f2a --- /dev/null +++ b/Java/Count Primes.java @@ -0,0 +1,92 @@ +E +1524450592 +tags: Hash Table, Math + +计数: 所有小于n的prime number. + +#### prime number定义 +- >=2的没有除自己和1以外公约数的数。 +- 还有另外一个定义方法: 这个n,有没有小于n的一个i, 而达到: i * i + # of i = n. 如果有,那就不是 prime + +#### Steps +- 一个boolean长条,存isPrime[]。 然后从i=2, 全部变true. +- hash key: the number itself +- 然后利用这个因子的性质,非prime满足条件: self*self, self * self + self ... etc. +- 所以就check每一个j, j+i, j+i+i, 然后把所有non-prime全部mark成false. +- 最后,数一遍还剩下的true个数就好了 + +``` +/* +Description: +Count the number of prime numbers less than a non-negative number, n. +Tags: Hash Table, Math +Similar Problems: (E) Ugly Number, (M) Ugly Number II, (M) Perfect Squares +*/ + +/* +Attempt2: https://leetcode.com/problems/count-primes/ explains it well +1. Ignore 1 and n. Don't count 1 and the number itself in. +2. Assume all numbers are prime in a boolean[]. Check off those are certainly not prime, the remaining will be prime. +3. For any n, only need to check up to i * i < n; more than that, +for example 2 x 6 is same as checking 6x2, but 6x2 is not necessary to check. +4. How to mark things off: + The first non-prime is always i^2: self * self. + Then more non-primes:self * self, self * (self + 1), self * (self + 2) ... etc. + So, mark all of these index of in the boolean[] + +*/ +public class Solution { + public int countPrimes(int n) { + if (n <= 1) { + return 0; + } + boolean[] primes = new boolean[n]; // less than n, end prime[n-1] + for (int i = 2; i < primes.length; i++) { + primes[i] = true; + } + + for (int i = 2; i * i < n; i++) { + if (!primes[i]) { + continue; + } + for (int j = i * i; j < n; j += i) { + primes[j] = false; + } + } + int count = 0; + for (int i = 2; i < primes.length; i++) { + count += primes[i] ? 1 : 0; + } + return count; + } +} + + +/*Timeout version*/ +//prime is a number n that cannot be divided by any number < n. +//In fact, only need to check sqrt(n) numbers from 1 + +public class Solution { + public int countPrimes(int n) { + int count = 0; + for (int i = 1; i < n; i++) { + if (isPrime(i)) { + count++; + } + } + return count; + } + + public boolean isPrime(int num) { + if (num <= 1) return false; + for (int i = 2; i * i <= num; i++) { + if (num % i == 0) { + return false; + } + } + return true; + } +} + + +``` \ No newline at end of file diff --git a/Java/Count and Say.java b/Java/Count and Say.java old mode 100644 new mode 100755 index 2d0fc5b..ceae6fe --- a/Java/Count and Say.java +++ b/Java/Count and Say.java @@ -1,33 +1,82 @@ -/* -The count-and-say sequence is the sequence of integers beginning as follows: +E +1522213680 +tags: String, Basic Implementation -1, 11, 21, 1211, 111221, ... +介绍一种count数字的方法, 然后每一行读出上一行的结果, 一行一行推算. 问nth行是啥样? -1 is read off as "one 1" or 11. +#### Basic Implementation +- 主要是题意很难理解, 非常misleading, 等到看明白题目, 其实没有什么算法要求. +- Count duplicates and print -11 is read off as "two 1s" or 21. +``` +/* +The count-and-say sequence is the sequence of integers with the first five terms as following: +1. 1 +2. 11 +3. 21 +4. 1211 +5. 111221 +1 is read off as "one 1" or 11. +11 is read off as "two 1s" or 21. 21 is read off as "one 2, then one 1" or 1211. +Given an integer n, generate the nth term of the count-and-say sequence. -Given an integer n, generate the nth sequence. +Note: Each term of the sequence of integers will be represented as a string. -Example -Given n = 5, return "111221". +Example 1: -Note -The sequence of integers will be represented as a string. +Input: 1 +Output: "1" +Example 2: -Tags Expand -String +Input: 4 +Output: "1211" + +*/ + +/* +Thoughts: Don't get confused about the index number of (1 ~ 5) from the questions, +it's not trying to encode integer. +nth 'term' means nth row. Each row is constructed based on last row. +Count the occurance of same digit and print it. +*/ +class Solution { + public String countAndSay(int n) { + if (n <= 1) { + return n + ""; + } + String curr = "1"; + for (int i = 2; i <= n; i++) { + int count = 1; + char c = curr.charAt(0); + int size = curr.length(); + StringBuffer sb = new StringBuffer(); + for (int j = 1; j < size; j++) { + if (curr.charAt(j) == curr.charAt(j - 1)) { + count++; + } else { + sb.append(count + String.valueOf(c)); + c = curr.charAt(j); + count = 1; + } + } + sb.append(count + String.valueOf(c)); // append end letter for each row + curr = sb.toString(); + } + + return curr; + } +} +/* +Previous notes: 1. Set up initial value '11' 2. use while loop to build on past variable 3. In each while loop case, break the string into charArray, count and name mark the type 4. In for loop: when different, append string (count+type); when same, count++. */ - - public class Solution { /** * @param n the nth @@ -40,23 +89,25 @@ public String countAndSay(int n) { String str = "11"; int ind = 2; while (ind < n) { - StringBuffer sb = new StringBuffer(); - char[] arr = str.toCharArray(); - int count = 1; - int type = Character.getNumericValue(arr[0]); - for (int i = 1; i < arr.length; i++) { - if (arr[i] == arr[i - 1]) { - count++; - } else { - sb.append(count + "" + type); - type = Character.getNumericValue(arr[i]); - count = 1; - } - } - ind++; - sb.append(count + "" + type); - str = sb.toString(); + StringBuffer sb = new StringBuffer(); + char[] arr = str.toCharArray(); + int count = 1; + int type = Character.getNumericValue(arr[0]); + for (int i = 1; i < arr.length; i++) { + if (arr[i] == arr[i - 1]) { + count++; + } else { + sb.append(count + "" + type); + type = Character.getNumericValue(arr[i]); + count = 1; + } + } + ind++; + sb.append(count + "" + type); + str = sb.toString(); } return str; } } + +``` \ No newline at end of file diff --git a/Java/Count of Range Sum.java b/Java/Count of Range Sum.java new file mode 100644 index 0000000..6dd5d6a --- /dev/null +++ b/Java/Count of Range Sum.java @@ -0,0 +1,105 @@ +H +1528434667 +tags: Divide and Conquer, Merge Sort, BST, PreSum + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + +``` +/* +Given an integer array nums, return the number of range sums +that lie in [lower, upper] inclusive. + +Range sum S(i, j) is defined as the sum of the elements in nums +between indices i and j (i ≤ j), inclusive. + +Note: +A naive algorithm of O(n2) is trivial. You MUST do better than that. + +Example: + +Input: nums = [-2,5,-1], lower = -2, upper = 2, +Output: 3 +Explanation: The three ranges are : [0,0], [2,2], [0,2] and their respective sums are: -2, -1, 2. +*/ + +class Solution { + public int countRangeSum(int[] nums, int lower, int upper) { + // edge case + if (nums == null || nums.length <= 0) { + return 0; + } + // mergeSort() + long[] preSum = calcPreSum(nums); + + return mergeSort(preSum, lower, upper, 0, preSum.length); + } + + private int mergeSort(long[] preSum, int lower, int upper, int start, int end) { + if (start + 1 >= end) { + return 0; + } + int mid = (start + end) / 2, m = mid, n = mid, count = 0; + + // sort two sides + count += mergeSort(preSum, lower, upper, start, mid); + count += mergeSort(preSum, lower, upper, mid, end); + + // calculate count in range [m, n] + for (int i = start; i < mid; i++) { + while (m < end && preSum[m] - preSum[i] < lower) { + m++; + } + while (n < end && preSum[n] - preSum[i] <= upper) { + n++; + } + count += n - m; + } + + // merge two list for range [start, end] + Arrays.sort(preSum, start, end); + //merge(preSum, start, mid - 1, end - 1); SHOULD use a merge function + return count; + } + + private long[] calcPreSum(int[] nums) { + long[] preSum = new long[nums.length + 1]; + for (int i = 1; i < preSum.length; i++) { + preSum[i] = preSum[i - 1] + nums[i - 1]; + } + return preSum; + } +} + + + +``` \ No newline at end of file diff --git a/Java/Count of Smaller Number before itself.java b/Java/Count of Smaller Number before itself.java old mode 100644 new mode 100755 index 1d0b9a8..87df7ee --- a/Java/Count of Smaller Number before itself.java +++ b/Java/Count of Smaller Number before itself.java @@ -1,21 +1,28 @@ -与Count of Smaller Number非常类似。 -Trick: 先Query,再modify. -每次Query时候,A[i]都还没有加入到Segment Tree 里面,而A[i+1...etc]自然也还没有加进去。 -那么就自然是coutning smaller number before itself. -刁钻啊! +H + +与Count of Smaller Number非常类似。以实际的value来构成segment tree,leaf上存(count of smaller number)。 + +Trick: 先Query,再modify. +每次Query时候,A[i]都还没有加入到Segment Tree 里面,而A[i+1,...etc]自然也还没有加进去。 +那么就自然是coutning smaller number before itself. +刁钻啊! + +另外注意: +在modify里面:多Check了root.start <= index 和 index <= root.end。 过去都忽略了。以后可以把这个也写上。 +(其实是Make sense的,就是更加严格地check了index再 root.left 或者 root.right里面的站位) -另外注意: -在modify里面:多Check了root.start <= index 和 index <= root.end。 过去都忽略了。以后可以把这个也写上。 -(其实是Make sense的,就是更加严格地check了index再 root.left 或者 root.right里面的站位) ``` /* -Give you an integer array (index from 0 to n-1, where n is the size of this array, value from 0 to 10000) . For each element Ai in the array, count the number of element before this element Ai is smaller than it and return count number array. +Give you an integer array (index from 0 to n-1, where n is the size of this array, value from 0 to 10000) . +For each element Ai in the array, count the number of element before this element Ai is smaller than +it and return count number array. Example For array [1,2,7,8,5], return [0,1,2,3,2] Note -We suggest you finish problem Segment Tree Build, Segment Tree Query II and Count of Smaller Number before itself I first. +We suggest you finish problem Segment Tree Build, Segment Tree Query II and +Count of Smaller Number before itself I first. Tags Expand LintCode Copyright Binary Tree Segment Tree diff --git a/Java/Count of Smaller Number.java b/Java/Count of Smaller Number.java old mode 100644 new mode 100755 index c8b4083..d05fc8e --- a/Java/Count of Smaller Number.java +++ b/Java/Count of Smaller Number.java @@ -1,15 +1,40 @@ -和平时的segment tree问题不同。 -这个给了实际的value,而还是造一个based on index的segment tree才行。 -Thought1是失败的,因为虽然省了空间,但是search time还是O(n). -Thought2才是真正的segment tree (based on index interval). +M +1532929143 +tags: Segment Tree, Binary Search, Lint + +给一串数字, array size = n. 给一串query: 每个query是一个数, 目的找 count# items smaller than query element. + +#### Segment Tree +- 和平时的segment tree问题不同。 [0 ~ n] 代表实际数字: based on real value的segment tree. +- Modify时,把array里面的value带进去,找到特定的位子, 然后count + 1. +- 最终在SegmentTree leaf上面全是array里面实际的数字。 +- node.count: 在node range里面的有多少个数字 + +##### right use of modify() +- build() 只是 empty segment tree, 没有property +- modify() 需要: 1. 找到left, update count+=1; 2. aggregate all parent when after returning +- 所以每一个modify 都是在整个path上所有的node上 + count + +##### query trick +- 在query前,给进去的start和end是: 0 ~ value-1. +- `value-1`: 找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. + +##### About other basic segment tree setup +- [那么其他做过的SegmentTree是怎么样呢?] +- 那些构成好的SegmentTree(找min,max,sum)也有一个Array。但是构成Tree时候,随Array的index而构架。 +- 也就是说,假如有Array[x,y,....]:在leaf,会有[0,0] with value = x. [1,1] with value = y. +- [但是这题] +- 构成时,是用actual value.也就是比如Array[x,y,....]会产生leaf:[x,x]with value = ..; [y,y]with value =... +- 其实很容易看穿: +- 若给出一个固定的array构成 SegmentTree,那估计很简单:按照index从0~array.lengh,leaf上就是[0,0] with value = x. +- 若题目让构造一个空心SegmentTree, `based on value 0 ~ n-1 (n <= 10000)`, 然后把一个Array的value modify 进去。 +- 这样八成是另外一种咯。 -重要trick: -在query前,给进去的start和end是: 0 ~ value-1. -value-1就是说,找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. -这个trick还挺刁钻的。 ``` /* -Give you an integer array (index from 0 to n-1, where n is the size of this array, value from 0 to 10000) and an query list. For each query, give you an integer, return the number of element in the array that are smaller than the given integer. +Give you an integer array (index from 0 to n-1, where n is the size of this array, value from 0 to 10000) +and an query list. For each query, give you an integer, return the number of element in the array that +are smaller than the given integer. Example @@ -21,12 +46,21 @@ Give you an integer array (index from 0 to n-1, where n is the size of this arra Challenge Could you use three ways to do it. +1. Just loop +2. Sort and binary search +3. Build Segment Tree and Search. + Just loop Sort and binary search Build Segment Tree and Search. Tags Expand Binary Search LintCode Copyright Segment Tree +*/ + +/* +Thought1是失败的,因为虽然省了空间,但是search time还是O(n). +Thought2才是真正的segment tree (based on index interval). */ /* Thought2: http://www.jiuzhang.com/solutions/count-of-smaller-number/ @@ -34,9 +68,10 @@ Give you an integer array (index from 0 to n-1, where n is the size of this arra Use query method to search for final results. Each A[i] will be stored at index value of A[i]. Count: how many numbers do we have from bottom till this level, including the A[i] itself. - For example, at the lowest A[i] spot, SegmentTreeNode(i,i), the count == 1. + For example, at the lowest A[i] spot, SegmentTreeNode(i,i), the count == 1. - Note:Again, be careful on calculating the mid. It's usually based on root.start and root.end, instead of the target start,end interval. + Note:Again, be careful on calculating the mid. It's usually based on root.start and root.end, + instead of the target start,end interval. */ public class Solution { @@ -49,19 +84,34 @@ public SegmentTreeNode(int start, int end) { this.start = start; this.end = end; this.count = 0; - this.left = null; - this.right = null; } } + + public List countOfSmallerNumber(int[] A, int[] queries) { + List rst = new ArrayList<>(); + + // build segment tree based off value with node.count = 0 + SegmentTreeNode root = build(0, 10000); + // populate count from leaf -> root + for (int value : A) { + modify(root, value, 1); + } + // Find item in target range [0~ n - 1] + for (int query : queries) { + int count = 0; + if (query > 0) {//Given value has to be in n's range: [0, 10000] + count = query(root, 0, query - 1); + } + rst.add(count); + } + return rst; + } - /* Build a empty segment tree based on index*/ + /* Build a empty segment tree based on index */ public SegmentTreeNode build(int start, int end) { - if (start > end) { - return null; - } - if (start == end) { - return new SegmentTreeNode(start, end); - } + if (start > end) return null; + if (start == end) return new SegmentTreeNode(start, end); + SegmentTreeNode root = new SegmentTreeNode(start, end); int mid = start + (end - start) / 2; root.left = build(start, mid); @@ -69,17 +119,19 @@ public SegmentTreeNode build(int start, int end) { return root; } - /* Update the tree with 'count': from bottom to this specific tree node, how many integers do we have.*/ + /* + Update the tree with 'count': from bottom to this specific tree node, how many integers do we have. + Reason of modify function: once children node is updated, parent node will update as well. + */ public void modify(SegmentTreeNode root, int index, int count){ if (root.start == index && root.end == index) { - root.count += count; + root.count += count; // basically + 1 return; } int mid = root.start + (root.end - root.start)/2; if (index <= mid) { modify(root.left, index, count); - } - if (index > mid) { + } else { //index > mid modify(root.right, index, count); } root.count = root.left.count + root.right.count; @@ -87,60 +139,105 @@ public void modify(SegmentTreeNode root, int index, int count){ /* Look for that number based on start&&end*/ public int query(SegmentTreeNode root, int start, int end) { - if (root.start == start && root.end == end) { - return root.count; - } - int sum = 0; + if (root.start == start && root.end == end) return root.count; + int mid = root.start + (root.end - root.start)/2; if (end <= mid) { - sum += query(root.left, start, end); + return query(root.left, start, end); } else if (start > mid) { - sum += query(root.right, start, end); - } else if (start <= mid && mid < end) { - sum += query(root.left, start, mid); - sum += query(root.right, mid + 1, end); + return query(root.right, start, end); } - return sum; + // start <= mid && mid < end) + return query(root.left, start, mid) + query(root.right, mid + 1, end); } +} +// All the same, execpt, modify() does not need to take `count` input. Count is always +1 when the target element is found. +public class Solution { - /** - * @param A: An integer array - * @return: The number of element in the array that - * are smaller that the given integer - */ - public ArrayList countOfSmallerNumber(int[] A, int[] queries) { - ArrayList rst = new ArrayList(); + public class SegmentTreeNode { + public int start,end; + public int count; + public SegmentTreeNode left, right; + public SegmentTreeNode(int start, int end) { + this.start = start; + this.end = end; + this.count = 0; + } + } + + public List countOfSmallerNumber(int[] A, int[] queries) { + List rst = new ArrayList<>(); SegmentTreeNode root = build(0, 10000); for (int value : A) { - modify(root, value, 1); + modify(root, value); } - for (int value : queries) { + for (int query : queries) { int count = 0; - if (value > 0) { - count = query(root, 0, value - 1); + if (query > 0) {//Given value has to be in n's range: [0, 10000] + count = query(root, 0, query - 1); } rst.add(count); } return rst; } -} - - - + /* Build a empty segment tree based on index*/ + public SegmentTreeNode build(int start, int end) { + if (start > end) return null; + if (start == end) return new SegmentTreeNode(start, end); + + SegmentTreeNode root = new SegmentTreeNode(start, end); + int mid = start + (end - start) / 2; + root.left = build(start, mid); + root.right = build(mid + 1, end); + return root; + } + /* Update the tree with 'count': from bottom to this specific tree node, how many integers do we have.*/ + public void modify(SegmentTreeNode root, int index){ + if (root.start == index && root.end == index) { + root.count += 1; + return; + } + int mid = root.start + (root.end - root.start)/2; + if (index <= mid) { + modify(root.left, index); + } else { //index > mid + modify(root.right, index); + } + root.count = root.left.count + root.right.count; + } + + /* Look for that number based on start&&end*/ + public int query(SegmentTreeNode root, int start, int end) { + if (root.start == start && root.end == end) return root.count; + + int mid = root.start + (root.end - root.start)/2; + if (end <= mid) { + return query(root.left, start, end); + } else if (start > mid) { + return query(root.right, start, end); + } + // start <= mid && mid < end) + return query(root.left, start, mid) + query(root.right, mid + 1, end); + } +} /* Time limit exceeded... - Because: If we build the tree based on given index 0~n, and build 'query' method based on its max values. It will work for small scale, but when it gets larger, we could be doing O(n)*m all the time. Everytime we search root.left and root.right, which is not binary search style : ) + Because: If we build the tree based on given index 0~n, and build 'query' method based on its max values. + It will work for small scale, but when it gets larger, we could be doing O(n)*m all the time. + Everytime we search root.left and root.right, which is not binary search style : ) Thoughts: Build SegmentTree, store max - 1st attempt: time exceeds. Because the 'query part' is actually not segment tree search. Doing search based on max value can find answer, but it's O(n) search. + 1st attempt: time exceeds. Because the 'query part' is actually not segment tree search. + Doing search based on max value can find answer, but it's O(n) search. Note: - This segment tree problem gives queries of actaul value, rather than a range(start,end) that we can directly use in segment tree. So we need some sort of conversion, that still provide (start,end) to search + This segment tree problem gives queries of actaul value, rather than a range(start,end) that we can directly use in segment tree. + So we need some sort of conversion, that still provide (start,end) to search */ public class Solution { diff --git a/Java/Count of Smaller Numbers After Self.java b/Java/Count of Smaller Numbers After Self.java new file mode 100644 index 0000000..ca09050 --- /dev/null +++ b/Java/Count of Smaller Numbers After Self.java @@ -0,0 +1,264 @@ +H +1527913608 +tags: BST, Binary Search, Divide and Conquer, Binary Indexed Tree, Segment Tree + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + +``` +/* +You are given an integer array nums and you have to return a new counts array. +The counts array has the property where counts[i] is the number of smaller elements to the right of nums[i]. + +Example: + +Input: [5,2,6,1] +Output: [2,1,1,0] +Explanation: +To the right of 5 there are 2 smaller elements (2 and 1). +To the right of 2 there is only 1 smaller element (1). +To the right of 6 there is 1 smaller element (1). +To the right of 1 there is 0 smaller element. +*/ + +/* +1. Sort the element and insert into new list. +2. During sort/insertion: use two pointer + binary search: the right pointer will be # of items greater than current index +3. save the # into result +*/ + + +class Solution { + public List countSmaller(int[] nums) { + // check edge case, and create result + List result = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return result; + } + // create new list for sorting && insertion + List list = new ArrayList<>(); + + // for loop, add each number to list + // each insertion will be a binary search against the new list + for (int i = nums.length - 1; i >= 0; i--) { + int start = 0, end = list.size(); + while (start < end) { + int mid = start + (end - start) / 2; + if (list.get(mid) >= nums[i]) { + end = mid; + } else { + start = mid + 1; + } + } + result.add(0, end); + list.add(end, nums[i]); + } + return result; + } +} + +// Binary Indexed Tree? (Fenwick tree) +class Solution { + class Node{ + int val, smallerCount; + Node left, right; + public Node(int val){ + this.val = val; + } + } + + public List countSmaller(int[] nums) { + List result = new ArrayList<>(); + if(nums == null || nums.length == 0) { + return result; + } + int n = nums.length; + Node root = new Node(nums[n - 1]); + for(int i = n - 1; i >= 0; i--) { + result.add(0, smaller(root, nums[i])); + } + + return result; + } + public int smaller(Node node, int val){ + node.smallerCount++; + if(node.val < val){ + if(node.right == null) node.right = new Node(val); + return node.smallerCount - 1 - node.right.smallerCount + smaller(node.right, val); + } else if(node.val > val){ + if(node.left == null) node.left = new Node(val); + return smaller(node.left, val); + } + + // else + return node.left == null ? 0 : node.left.smallerCount; + } +} + +class Solution { + class SegmentTreeNode{ + int start, end, count; + SegmentTreeNode left, right; + public SegmentTreeNode(int start, int end){ + this.start = start; + this.end = end; + } + } + SegmentTreeNode root; + public List countSmaller(int[] nums) { + List result = new ArrayList<>(); + if(nums == null || nums.length == 0) return result; + int n = nums.length; + int min = Integer.MAX_VALUE, max = Integer.MIN_VALUE; + int diff = 0; + for (int num : nums) { + min = Math.min(min, num); + max = Math.max(max, num); + } + if (min < 0) { + diff = Math.abs(min); + max += diff; + min += diff; + } + // build tree + root = build(min, max); + for (int num : nums) { + modify(root, num + diff, 1); + } + // remove curr element, and query for all remaining elements + for (int num : nums) { + modify(root, num + diff, -1); + result.add(query(root, min, num + diff - 1)); + } + + return result; + } + + // build empty map + private SegmentTreeNode build(int start, int end) { + if (start > end) return null; + if (start == end) return new SegmentTreeNode(start, end); + int mid = (start + end) / 2; + SegmentTreeNode node = new SegmentTreeNode(start, end); + node.left = build(start, mid); + node.right = build(mid + 1, end); + + return node; + } + + // modify leaf segment node with value, and update all parents alone the way. + private void modify(SegmentTreeNode root, int value, int count) { + if (root.start == value && root.end == value) { + root.count += count; + return; + } + int mid = (root.start + root.end) / 2; + if (value <= mid) { + modify(root.left, value, count); + } else { + modify(root.right, value, count); + } + root.count = root.left.count + root.right.count; + } + + // start query: get count # of elements within range [start, end] + private int query(SegmentTreeNode root, int start, int end) { + if (root == null) return 0; + if (root.start == start && root.end == end) return root.count; + + int mid = (root.start + root.end) / 2; + if (end < mid) { + return query(root.left, start, end); + } + if (start > mid) { + return query(root.right, start, end); + } + // start <= mid < end + return query(root.left, start, mid) + query(root.right, mid + 1, end); + } +} + + + + +// Segment tree, timesout. Same reason as in `count of smaller number` +class Solution { + class SegmentTreeNode{ + int start, end, val; + SegmentTreeNode left, right; + public SegmentTreeNode(int start, int end){ + this.start = start; + this.end = end; + } + } + SegmentTreeNode root; + public List countSmaller(int[] nums) { + List result = new ArrayList<>(); + if(nums == null || nums.length == 0) return result; + int n = nums.length; + // build tree + root = build(0, n - 1); + + // query for each elements + for (int i = 0; i < n; i++) { + result.add(query(root, nums, i, n - 1, nums[i])); + } + + return result; + } + + // build empty map + private SegmentTreeNode build(int start, int end) { + if (start > end) return null; + if (start == end) return new SegmentTreeNode(start, end); + int mid = (start + end) / 2; + SegmentTreeNode node = new SegmentTreeNode(start, end); + node.left = build(start, mid); + node.right = build(mid + 1, end); + + return node; + } + + private int query(SegmentTreeNode root, int[] nums, int start, int end, int target) { + // compare when start==end + if (root.start == start && root.end == end && start == end) { + if (nums[start] < target) return 1; + return 0; + } + int mid = (root.start + root.end) / 2; + if (end <= mid) { + return query(root.left, nums, start, end, target); + } + if (start > mid) { + return query(root.right, nums, start, end, target); + } + // aggregate left + right query result + return query(root.left, nums, start, root.left.end, target) + query(root.right, nums, root.right.start, end, target); + } +} +``` \ No newline at end of file diff --git a/Java/Counting Bits.java b/Java/Counting Bits.java new file mode 100644 index 0000000..226cba7 --- /dev/null +++ b/Java/Counting Bits.java @@ -0,0 +1,80 @@ +M +1522856793 +tags: DP, Bit Manipulation, Bitwise DP + +给一个数组, 算里面有多少bit 1. + +#### Bitwise DP +- 对于每一个数字, 其实很简单就能算出来: 每次 >>1, 然后 & 1 就可以count 1s. Time: 一个数字可以 >>1 O(logN) 次 +- 现在要对[0 ~ num] 都计算, 也就是N个数字, 时间复杂度: O(nLogN). +- 用DP来优化, 查找过的number的1s count, 存下来在 dp[number]里面. +- 计算你顺序从 0 -> num, count过的数字就可以重复利用. +- Bit题目 用num的数值本身表示DP的状态. +- 这里, dp[i] 并不是和 dp[i-1]有逻辑关系; 而是dp[i] 和dp[i>>1], 从binary representation看出有直接关系. + +``` +/* +Given a non negative integer number num. +For every numbers i in the range 0 ≤ i ≤ num calculate the number of 1's +in their binary representation and return them as an array. + +Example: +For num = 5 you should return [0,1,1,2,1,2]. + +Follow up: + +It is very easy to come up with a solution with run time O(n*sizeof(integer)). +But can you do it in linear time O(n) /possibly in a single pass? +Space complexity should be O(n). +Can you do it like a boss? Do it without using any builtin function like __builtin_popcount in c++ or in any other language. +*/ + +/* +Thoughts: +Just looking at the bit representation: +0: 0000 +1: 0001 +2: 0010 +3: 0011 + +check 1 and 2: 2 >> 1 becomes 1. '0001' was calculated before, so 2 should use it. +dp[i]: represents num <= i, then how many 1's are there. +dp[i>>1]: represents the binary number has less 1 bit. +dp[i]: + - if i's binary has a tailing '1', then dp[i] = dp[i >> 1] + 1 + - if i's binary has a tailing '0', then dp[i] = dp[i >> 1] +Combine: +dp[i] = dp[i >> 1] + i % 2; +Usually use num value itself as DP's status index. +*/ +class Solution { + public int[] countBits(int num) { + if (num < 0) { + return null; + } + int[] dp = new int[num + 1]; + dp[0] = 0; + for (int i = 1; i <= num; i++) { + int prevNum = i >> 1; + dp[i] = dp[prevNum] + (i % 2); + } + return dp; + } +} + +// & 1 will do as well, make sure to parentheses (i & 1) +class Solution { + public int[] countBits(int num) { + if (num < 0) { + return null; + } + int[] dp = new int[num + 1]; + dp[0] = 0; + for (int i = 1; i <= num; i++) { + int prevNum = i >> 1; + dp[i] = dp[prevNum] + (i & 1); + } + return dp; + } +} +``` \ No newline at end of file diff --git a/Java/Course Schedule II.java b/Java/Course Schedule II.java new file mode 100755 index 0000000..5a256d7 --- /dev/null +++ b/Java/Course Schedule II.java @@ -0,0 +1,265 @@ +M +1521929738 +tags: DFS, BFS, Graph, Topological Sort + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + +``` +/* +There are a total of n courses you have to take, labeled from 0 to n - 1. + +Some courses may have prerequisites, for example to take course 0 you have to first take course 1, +which is expressed as a pair: [0,1] + +Given the total number of courses and a list of prerequisite pairs, +return the ordering of courses you should take to finish all courses. + +There may be multiple correct orders, you just need to return one of them. +If it is impossible to finish all courses, return an empty array. + +For example: + +2, [[1,0]] +There are a total of 2 courses to take. To take course 1 you should have finished course 0. +So the correct course order is [0,1] + +4, [[1,0],[2,0],[3,1],[3,2]] +There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. +Both courses 1 and 2 should be taken after you finished course 0. So one correct course order is [0,1,2,3]. +Another correct ordering is[0,2,1,3]. + +Note: + +The input prerequisites is a graph represented by a list of edges, not adjacency matrices. +Read more about how a graph is represented. +You may assume that there are no duplicate edges in the input prerequisites. + + +Hints: + +- This problem is equivalent to finding the topological order in a directed graph. + If a cycle exists, no topological ordering exists and therefore it will be impossible to take all courses. +- Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining + the basic concepts of Topological Sort. +- Topological sort could also be done via BFS. +*/ + +/* +Thoughs: +Return the fianl topologically sorted list with BFS. +Refering to Course Schedule I. +Instead of just couting the item when inDegree == 0, we add them into a list. +*/ +class Solution { + public int[] findOrder(int numCourses, int[][] prerequisites) { + if (numCourses == 0) { + return new int[numCourses]; + } + if (prerequisites == null || prerequisites.length == 0 + || prerequisites[0] == null || prerequisites[0].length == 0) { + int[] rst = new int[numCourses]; + for (int i = 0; i < numCourses; i++) { + rst[i] = i; + } + return rst; + } + + List[] edges = new ArrayList[numCourses]; + int[] inDegree = new int[numCourses]; + + // Initialize + for (int i = 0; i < numCourses; i++) { + edges[i] = new ArrayList<>(); + } + + // Build graph edges + for (int[] prerequisite : prerequisites) { + edges[prerequisite[1]].add(prerequisite[0]); + inDegree[prerequisite[0]]++; + } + + // BFS to build the final sorted list + Queue queue = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (inDegree[i] == 0) { + queue.add(i); + } + } + + List sortedList = new ArrayList<>(); + while (!queue.isEmpty()) { + int startNode = (int) queue.poll(); + sortedList.add(startNode); + for (int i = 0; i < edges[startNode].size(); i++) { + int childNode = (int) edges[startNode].get(i); + inDegree[childNode]--; + if (inDegree[childNode] == 0) { + queue.add(childNode); + } + } + } + + // If cycle exist (missing node in final sorted list), then return [], no topological sorted result + if (sortedList.size() != numCourses) { + return new int[0]; + } + + int[] rst = new int[sortedList.size()]; + for (int i = 0; i < sortedList.size(); i++) { + rst[i] = sortedList.get(i); + } + return rst; + } +} + +// DFS +class Solution { + int[] visited; + List[] edges; + List sortedList; + + public int[] findOrder(int numCourses, int[][] prerequisites) { + if (numCourses == 0) { + return new int[numCourses]; + } + if (prerequisites == null || prerequisites.length == 0 + || prerequisites[0] == null || prerequisites[0].length == 0) { + int[] rst = new int[numCourses]; + for (int i = 0; i < numCourses; i++) { + rst[i] = i; + } + return rst; + } + sortedList = new ArrayList<>(); + edges = new ArrayList[numCourses]; + visited = new int[numCourses]; + + // Initialize + for (int i = 0; i < numCourses; i++) { + edges[i] = new ArrayList(); + } + + // Build graph edges + for (int[] prerequisite : prerequisites) { + edges[prerequisite[1]].add(prerequisite[0]); + } + + // DFS serach && marking visited + for (int i = 0; i < numCourses; i++) { + if(!dfs(i)) { + return new int[]{ }; + } + } + + int[] rst = new int[numCourses]; + for (int i = 0; i < numCourses; i++) { + rst[i] = sortedList.get(i); + } + + return rst; + } + + public boolean dfs(int node) { + if (visited[node] == 1) { + return true; + } + if (visited[node] == -1) { // cyclic + return false; + } + + visited[node] = -1; + List childNodes = edges[node]; + for (Integer childNode : childNodes) { + if (!dfs(childNode)) { + return false; + } + } + visited[node] = 1; + sortedList.add(0, node); + + return true; + } +} + +/* + http://blog.csdn.net/ljiabin/article/details/45847019 + + Based on Course Schedule I, now we need to return all nodes with by the seq number. + + Note: + The map is built based on +*/ +public class Solution { + HashMap> map; + int[] visited; + int seq; + int[] order; + public int[] findOrder(int numCourses, int[][] prerequisites) { + order = new int[numCourses]; + seq = numCourses - 1; + visited = new int[numCourses]; + map = new HashMap>(); + //Put all start node into map. + for (int i = 0; i < prerequisites.length; i++) { + if (!map.containsKey(prerequisites[i][1])) { + map.put(prerequisites[i][1], new ArrayList()); + } + map.get(prerequisites[i][1]).add(prerequisites[i][0]); + } + //dfs on each start node in the pair + for (int i = 0; i < numCourses; i++) { + if (!dfs(i)) { + return new int[]{}; + } + } + return order; + } + + public boolean dfs (int node) { + if (visited[node] == 1) {//has been through this path, true. + return true; + } + if (visited[node] == -1) {//visiting a visited node from a deper level node, cycle + return false; + } + //mark it -1 then after dfs mark it 1. Marking and backtracking skills + visited[node] = -1; + + //Visit each child and make sure there is no cycle. + if (map.containsKey(node)) { + for (int nextNode : map.get(node)) { + if (!dfs(nextNode)) { + return false; + } + } + } + visited[node] = 1; + order[seq--] = node; + return true; + } + +} + + + + + + +``` \ No newline at end of file diff --git a/Java/Course Schedule.java b/Java/Course Schedule.java new file mode 100755 index 0000000..b7501fa --- /dev/null +++ b/Java/Course Schedule.java @@ -0,0 +1,395 @@ +M +1521928225 +tags: DFS, BFS, Graph, Topological Sort, Backtracking + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + +``` +/* +There are a total of n courses you have to take, labeled from 0 to n - 1. + +Some courses may have prerequisites, for example to take course 0 you have to first take course 1, +which is expressed as a pair: [0,1] + +Given the total number of courses and a list of prerequisite pairs, +is it possible for you to finish all courses? + +For example: + +2, [[1,0]] +There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible. + +2, [[1,0],[0,1]] +There are a total of 2 courses to take. To take course 1 you should have finished course 0, +and to take course 0 you should also have finished course 1. +So it is impossible. + +Note: +The input prerequisites is a graph represented by a list of edges, not adjacency matrices. +Read more about how a graph is represented. +https://www.khanacademy.org/computing/computer-science/algorithms/graph-representation/a/representing-graphs + +click to show more hints. + +Hints: +1. This problem is equivalent to finding if a cycle exists in a directed graph. + If a cycle exists, no topological ordering exists and therefore it will be impossible to take all courses. +2. Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera + explaining the basic concepts of Topological Sort. +3. Topological sort could also be done via BFS. +*/ + +/* +Thoughts: +Topological sort, BFS Kahn's algorithem. +- build map of +- count in-degree (if need to return actaul source schedule sequence, we should maintain a list) +- start without in-degree, add to queue +- iterate over queue, visit each directed node and decrease the in-degree on them +- when the in-degree on a node == 0, add it to the 'final list'; in this question, no need to return course scheduel, so just add to count. +- If size of the final list (count) == numCourse, means we scheduled all courses and there is no cycle, so return true. +*/ +class Solution { + public boolean canFinish(int numCourses, int[][] prerequisites) { + if (validateInput(numCourses, prerequisites)) { + return true; + } + List[] edges = new ArrayList[numCourses]; + int[] inDegree = new int[numCourses]; + + // Initialize + for (int i = 0; i < numCourses; i++) { + edges[i] = new ArrayList<>(); + inDegree[0] = 0;// though, 0 by default + } + + // Build graph edges + for (int[] prerequisite : prerequisites) { + edges[prerequisite[1]].add(prerequisite[0]); + inDegree[prerequisite[0]]++; + } + + // BFS to build the final sorted list + Queue queue = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (inDegree[i] == 0) { + queue.add(i); + } + } + + int count = 0; + while (!queue.isEmpty()) { + int startNode = (int)queue.poll(); + count++; + for (int i = 0; i < edges[startNode].size(); i++) { + int childNode = (int) edges[startNode].get(i); + inDegree[childNode]--; + if (inDegree[childNode] == 0) { + queue.add(childNode); + } + } + } + + // BFS will always end. But count should == # of unique course. + // If not (likely less than), then there is cycle + return count == numCourses; + } + + private boolean validateInput(int numCourses, int[][] prerequisites) { + return numCourses == 0 || prerequisites == null || prerequisites.length == 0 + || prerequisites[0] == null || prerequisites[0].length == 0; + } +} + + +/* +Thoughts: +DFS. This question only asks about true/false (to detect cycle), +so there is no need to record the actual final list. +We'll simply traverse all of the nodes via the map . +If there is a cycle, there must be a node being visited again in one loop: +mark last visited node = -1 +mark visited starting node after dfs: node = 1 +*/ +class Solution { + int[] visited; + List[] edges; + public boolean canFinish(int numCourses, int[][] prerequisites) { + if (numCourses == 0 || prerequisites == null || prerequisites.length == 0 + || prerequisites[0] == null || prerequisites[0].length == 0) { + return true; + } + edges = new ArrayList[numCourses]; + visited = new int[numCourses]; + + // Initialize + for (int i = 0; i < numCourses; i++) { + edges[i] = new ArrayList(); + } + + // Build graph edges + for (int[] prerequisite : prerequisites) { + edges[prerequisite[1]].add(prerequisite[0]); + } + + // DFS serach && marking visited + for (int i = 0; i < numCourses; i++) { + if(!dfs(i)) { + return false; + } + } + + return true; + } + + public boolean dfs(int node) { + if (visited[node] == 1) { + return true; + } + if (visited[node] == -1) { // cyclic + return false; + } + + visited[node] = -1; + List childNodes = edges[node]; + for (Integer childNode : childNodes) { + if (!dfs(childNode)) { + return false; + } + } + visited[node] = 1; + + return true; + } +} + + +/* + Thoughts: Try some help. make shorter version. + This version does not use a class, but does the exact same algorithm: mark visited and check cycle with dfs. + The idea is almost exaclty same (http://www.jyuan92.com/blog/leetcode-course-schedule/) + Instead of using a seq,visited to check mark seq, and visted, we can do a 'mark' backtracking. + This means: we can mark the visited = -1 before the dfs, and if anything in the dfs sees a -1, + it returns false; + After the dfs of children nodes in the map, wwe mark a node.visited = 1. That means the spot has been visited : ) + + Note: + SHould build he map: map +*/ + +public class Solution { + HashMap> map; + int[] visited; + public boolean canFinish(int numCourses, int[][] prerequisites) { + if (numCourses <= 0 || prerequisites == null || prerequisites.length == 0 || + prerequisites[0] == null || prerequisites[0].length == 0) { + return true; + } + visited = new int[numCourses]; + map = new HashMap>(); + //Put all start node into map. + for (int i = 0; i < prerequisites.length; i++) { + if (!map.containsKey(prerequisites[i][1])) { + map.put(prerequisites[i][1], new ArrayList()); + } + map.get(prerequisites[i][1]).add(prerequisites[i][0]); + } + //dfs on each start node in the pair + for (int i = 0; i < prerequisites.length; i++) { + if (!dfs(prerequisites[i][0])) { + return false; + } + } + + return true; + } + + public boolean dfs (int node) { + if (visited[node] == 1) {//has been through this path, true. + return true; + } + if (visited[node] == -1) {//visiting a visited node from a deper level node, cycle + return false; + } + //mark it -1 then after dfs mark it 1. Marking and backtracking skills + visited[node] = -1; + + //Visit each child and make sure there is no cycle. + if (map.containsKey(node)) { + for (int nextNode : map.get(node)) { + if (!dfs(nextNode)) { + return false; + } + } + } + + visited[node] = 1; + return true; + } + +} + + + + + + +/* + Thoughts: TO LONG. Should try shorter version. + ALSO, built the map based map: this will cause trouble and hard to work if we want to return + the sequence of course. + + Though, no need to find the correct order of course, but we can do Topological Sort via DFS, + where in the process we check if there is cycle. + 0. Create node {val, visited, list of child node}. OR: just a map + 1. Put all prerequisites in map + 2. For loop on all nodes in the map. + Each node, DFS on it, add all outgoing child on stack if that node is not visited + Check on cycle: after DFS on all child, we need to mark the parent node itself to be visited. + At this moment, if this node has been visited once, that means a cycle has happended, then return false; + + Implementation: + Use a node, track value, sequence number, and if visited, and its child nodes. + In DFS: first mark curr node visited, then DFS on child, then mark curr node with a sequence number. + Cycle: if a node has been visited but does not have a sequence number yet , + (that means some nodes at the earlier level, which has no been back-tracking to yet) + then there must be a cycle backwards. +*/ +public class Solution { + public class Node { + int val; + int seq; + boolean visited; + ArrayList children; + public Node(int val){ + this.val = val; + this.visited = false; + this.children = new ArrayList(); + this.seq = -1; + } + } + public int n; + public boolean canFinish(int numCourses, int[][] prerequisites) { + if (numCourses <= 0 || prerequisites == null || prerequisites.length == 0 || + prerequisites[0] == null || prerequisites[0].length == 0) { + return true; + } + HashMap map = new HashMap(); + for (int i = 0; i < prerequisites.length; i++) { + Node node; + //Add curr nodes + if (map.containsKey(prerequisites[i][0])) { + node = map.get(prerequisites[i][0]); + } else { + node = new Node(prerequisites[i][0]); + } + node.children.add(prerequisites[i][1]); + map.put(node.val, node); + //Add Child nodes + if (!map.containsKey(prerequisites[i][1])) { + map.put(prerequisites[i][1], new Node(prerequisites[i][1])); + } + } + n = prerequisites.length; + for (int i = 0; i < prerequisites.length; i++) { + if (!DFS(prerequisites[i][0], map)) { + return false; + } + } + + return true; + } + + public boolean DFS(int val, HashMap map) { + Node node = map.get(val); + node.visited = true; + map.put(val, node); + + for (int child : node.children) { + if (map.get(child).visited && map.get(child).seq == -1) {//Check cycle + return false; + } else if (!map.get(child).visited) { + if(!DFS(child, map)){ + return false; + } + } + } + + node.seq = n--; + map.put(val, node); + return true; + } +} + + + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Java/Cracking the Safe.java b/Java/Cracking the Safe.java new file mode 100644 index 0000000..1adc0e8 --- /dev/null +++ b/Java/Cracking the Safe.java @@ -0,0 +1,105 @@ +H +1534220198 +tags: Math, DFS, Greedy + +#### Greedy, Iterative +- For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +- We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +- Track the newly formed string; if new, add the new char to overall result +- Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] +- Same concept as dfs + +#### DFS +- Same concept: use window to cut out tail, and append with new candidate +- do this for k^n = Math.pow(k, n) times + +``` +/* +There is a box protected by a password. The password is n digits, +where each letter can be one of the first k digits 0, 1, ..., k-1. + +You can keep inputting the password, +the password will automatically be matched against the last n digits entered. + +For example, assuming the password is "345", I can open it when I type "012345", +but I enter a total of 6 digits. + +Please return any string of minimum length that is guaranteed to open the box +after the entire string is inputted. + +Example 1: +Input: n = 1, k = 2 +Output: "01" +Note: "10" will be accepted too. +Example 2: +Input: n = 2, k = 2 +Output: "00110" +Note: "01100", "10011", "11001" will be accepted too. +Note: +n will be in the range [1, 4]. +k will be in the range [1, 10]. +k^n will be at most 4096. + +http://www.cnblogs.com/grandyang/p/8452361.html +*/ + +/* +For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +Track the newly formed string; if new, add the new char to overall result +Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] + +This is more greedy: will generate one correct solution +*/ +class Solution { + public String crackSafe(int n, int k) { + //if (n == 1) return "0"; + + Set set = new HashSet<>(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < n; i++) sb.append("0"); + set.add(sb.toString()); + + for (int i = 0; i < Math.pow(k, n); i++) { + String tail = sb.substring(sb.length() - n + 1); // last n - 1 chars + for (int j = k - 1; j >= 0; j--) { + String newStr = tail + j; + if (!set.contains(newStr)) { + set.add(newStr); + sb.append(j); + break; + } + } + } + + return sb.toString(); + } +} + + +// DFS +class Solution { + public String crackSafe(int n, int k) { + Set visited = new HashSet<>(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < n; i++) sb.append("0"); + visited.add(sb.toString()); + + // recursive, dfs + dfs(n, k, Math.pow(k, n), visited, sb); + return sb.toString(); + } + + private void dfs(int n, int k, double total, Set visited, StringBuffer sb) { + if (visited.size() == total) return; + String tail = sb.substring(sb.length() - n + 1); + for (int j = k - 1; j >= 0; j--) { + String newStr = tail + j; + if (visited.contains(newStr)) continue; + visited.add(newStr); + sb.append(j); + dfs(n, k, total, visited, sb); + } + } +} +``` \ No newline at end of file diff --git a/Java/Decode String.java b/Java/Decode String.java new file mode 100644 index 0000000..f47f61b --- /dev/null +++ b/Java/Decode String.java @@ -0,0 +1,216 @@ +M +1520800825 +tags: Divide and Conquer, Stack, DFS + +给一个expression string. 里面包括数字, 字母, 括号. 其中数字代表括号里面的内容重复几次. + +括号里面可以是String, 也可能是expression. + +目的: 把expression展开成一个正常的String. + + +#### Stack, Iteratively +- Process inner item first: last come, first serve, use stack. +- Record number globally and only use it when '[' is met. +- Stack存 [ ] 里面的内容, detect 括号开头结尾: 结尾时process inner string +- 有很多需要注意的细节才能做对: +- Stack 也可以用, 每个地方要注意 cast. 存进去的需要是Object: String, Integer +- 几个 type check: instanceof String, Character.isDigit(x), Integer.valueOf(int num) +- 出结果时候: `sb.insert(0, stack.pop())` + + +#### DFS +- Bottom->up: find deepest inner string first and expand from inside of `[ ]` +- 与Stack时需要考虑的一些function类似. 特别之处: **检查`[ ]`的结尾** +- 因为DFS时候, 括号里的substring会被保留着进入下一个level, 所以我们在base level要keep track of substring. +- 用int paren 来track 括号的开合, 当paren再次==0的时候 找到closure ']' +- 其他时候, 都要继续 append to substring + + +``` +/* +Given an encoded string, return it's decoded string. + +The encoding rule is: k[encoded_string], +where the encoded_string inside the square brackets is being repeated exactly k times. +Note that k is guaranteed to be a positive integer. + +You may assume that the input string is always valid; +No extra white spaces, square brackets are well-formed, etc. + +Furthermore, you may assume that the original data does not contain any digits and +that digits are only for those repeat numbers, k. +For example, there won't be input like 3a or 2[4]. + +Examples: + +s = "3[a]2[bc]", return "aaabcbc". +s = "3[a2[c]]", return "accaccacc". +s = "2[abc]3[cd]ef", return "abcabccdcdcdef". +*/ +public class Solution { + public String decodeString(String s) { + if (s == null || s.length() == 0) { + return ""; + } + Stack stack = new Stack<>(); + int number = 0; + for (char c : s.toCharArray()) { + if (Character.isDigit(c)) { + number = number * 10 + (c - '0'); + } else if (c == '[') { + stack.push(Integer.valueOf(number)); + number = 0; + } else if (c == ']') { + String str = popStack(stack); + int num = (Integer) stack.pop(); + for (int i = 0; i < num; i++) stack.push(str); + } else { + stack.push(String.valueOf(c)); + } + } + return popStack(stack); + } + + private String popStack(Stack stack) { + StringBuffer sb = new StringBuffer(); + while (!stack.isEmpty() && (stack.peek() instanceof String)) { + sb.insert(0, stack.pop()); + } + return sb.toString(); + } +} + + + +/* +Given an expression s includes numbers, letters and brackets. +Number represents the number of repetitions inside the brackets +(can be a string or another expression). + +Please expand expression to be a string. + +Example +s = abc3[a] return abcaaa +s = 3[abc] return abcabcabc +s = 4[ac]dy, return acacacacdy +s = 3[2[ad]3[pf]]xyz, return adadpfpfpfadadpfpfpfadadpfpfpfxyz +*/ + + +/* +Thoughts: +Can DFS. Also, use stack to hold all inner bucket. +In the original string, block of string is separated by #[ ]. +Can use stack to hold 2 piece of information: +1. # where the str repeats, and it also marks the start of the string +2. Store each char of the string itself +3. Once flatten the inner str, repeat the str # times and add back to stack + +As we iterate over the string +- detect '[' to determine the time to add start marker #. +- detect ']' when it's time to retrieve the # and str, save results + +Important: use Stack to hold generic Object! +*/ +public class Solution { + public String expressionExpand(String s) { + if (s == null || s.length() == 0) { + return ""; + } + Stack stack = new Stack<>(); + int number = 0; + for (char c : s.toCharArray()) { + if (Character.isDigit(c)) { + number = number * 10 + (c - '0'); + } else if (c == '[') { + stack.push(Integer.valueOf(number)); + number = 0; + } else if (c == ']') { + String str = popStack(stack); + Integer num = (Integer) stack.pop(); + for (int i = 0; i < num; i++) { + stack.push(str); + } + } else { + stack.push(String.valueOf(c)); + } + } + + return popStack(stack); + } + + private String popStack(Stack stack) { + StringBuffer sb = new StringBuffer(); + Stack buffer = new Stack<>(); + while (!stack.isEmpty() && (stack.peek() instanceof String)) { + buffer.push((String) stack.pop()); + } + + while (!buffer.isEmpty()) { + sb.append(buffer.pop()); + } + return sb.toString(); + } +} + + +/* +Thoughts: +DFS, process inner string each time. +Use "#[" as detection. +Important: +1. Finding the closure of ']' is tricky: need to track the parentheses +2. Until we find the closure ']', keep track of the un-flatten substring for dfs to use +*/ +public class Solution { + /** + * @param s: an expression includes numbers, letters and brackets + * @return: a string + */ + public String expressionExpand(String s) { + if (s == null || s.length() == 0) { + return ""; + } + StringBuffer sb = new StringBuffer(); + String substring = ""; + int number = 0; + int paren = 0; // parentheses tracker + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '[') { + if (paren > 0) { // if paren == 0, it indicates the outside 1st '[', no need to record + substring += c; + } + paren++; + } else if (c == ']') { + paren--; + if (paren == 0) { + String innerString = expressionExpand(substring); + for (int num = 0; num < number; num++) { + sb.append(innerString); + } + number = 0; + substring = ""; + } else { + substring += c; + } + } else if (Character.isDigit(c)) { + if (paren == 0) { + number = number * 10 + (c - '0'); + } else { + substring += c; + } + } else { + if (paren == 0) { + sb.append(String.valueOf(c)); + } else { + substring += c; + } + } + } + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/Decode Ways II.java b/Java/Decode Ways II.java new file mode 100644 index 0000000..5026152 --- /dev/null +++ b/Java/Decode Ways II.java @@ -0,0 +1,116 @@ +H +1524710652 +tags: DP, Partition DP, Enumeration + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +其中字符可能是 "*", 可以代表 [1 - 9] + +#### DP +- 乘法原理 +- 跟decode way I 一样, 加法原理, 切割点时: 当下是取了 1 digit 还是 2 digits 来decode +- 定义dp[i] = 前i个digits最多有多少种decode的方法. new dp[n + 1]. +- 不同的情况是: 每一个partition里面, 如果有"*", 就会在自身延伸出很多不同的可能 +- 那么: dp[i] = dp[i - 1] * (#variations of ss[i]) + dp[i - 2] * (#variations of ss[i,i+1]) + +##### 特点 +- 枚举的能力: 具体分析 '*' 出现的位置, 枚举出数字, 基本功. +- 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) +- 理解取MOD的原因: 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. +- 枚举好以后, 其实这个题目的写法和思考过程都不难 + + +``` +/* +A message containing letters from A-Z is being encoded to numbers using the following mapping way: + +'A' -> 1 +'B' -> 2 +... +'Z' -> 26 +Beyond that, now the encoded string can also contain the character '*', which can be treated as one of the numbers from 1 to 9. + +Given the encoded message containing digits and the character '*', return the total number of ways to decode it. + +Also, since the answer may be very large, you should return the output mod 109 + 7. + +Example 1: +Input: "*" +Output: 9 +Explanation: The encoded message can be decoded to the string: "A", "B", "C", "D", "E", "F", "G", "H", "I". +Example 2: +Input: "1*" +Output: 9 + 9 = 18 +Note: +The length of the input string will fit in range [1, 105]. +The input string will only contain the character '*' and digits '0' - '9'. +*/ + + +/* +Thoughts: +dp[i]: # ways to decode the first i digits +Carefully handle *: +Single digit: either regular number (1 way) or * (9 ways) +Double digit: +1. [0, *] : 0 ways +2. [1, *] : 9 ways +3. [2, *] : 6 ways +Write a function to calculate the variations + +dp[i] += dp[i - 1] * # of variations +*/ +class Solution { + public int numDecodings(String s) { + if (s == null || s.length() == 0) return 0; + int n = s.length(); + int mod = 1000000007; + + char[] ss = s.toCharArray(); + long[] dp = new long[n + 1]; + dp[0] = 1; + + for (int i = 1; i <= n; i++) { + if (validate(ss[i - 1])) { + dp[i] += dp[i - 1] * calcSingleDigitVariation(ss[i - 1]); + } + if (i >= 2 && validate(ss[i - 1]) && validate(ss[i - 2])) { + dp[i] += dp[i - 2] * calcDoubleDigitVariation(ss[i - 2], ss[i - 1]); + } + dp[i] %= mod; + } + + return (int)dp[n]; + } + + private boolean validate(char c) { + return c == '*' || (c >= '0' && c <= '9'); + } + + private int calcSingleDigitVariation(char c) { + if (c == '*') return 9; // 9 ways + if (c == '0') return 0; // not applicable + return 1; + } + + private int calcDoubleDigitVariation(char a, char b) { + if (a == '0' || (a >= '3' && a <= '9')) return 0; // not applicable to build 2-digit. a must be in [1, 2] + + // Validate all conditioins of char a + if (a == '1') return b == '*' ? 9 : 1; // 1 pair with [1~9] or one of [0~9] + if (a == '2') { // 2 pair with [1~6] or one of [0~6] + if (b == '*') return 6; + if (b <= '6') return 1; + return 0; + } + + // Validate char b. Now a = '*' + if (b >= '0' && b <= '6') return 2; // 1[b], 2[b] + + if (b >= '7' && b <= '9') return 1; // 1[b] + + // if ab = '**', there are ab=1[1~9], ab=2[1~6] => 15 ways + return 15; + } +} +``` \ No newline at end of file diff --git a/Java/Decode Ways.java b/Java/Decode Ways.java new file mode 100644 index 0000000..313266b --- /dev/null +++ b/Java/Decode Ways.java @@ -0,0 +1,108 @@ +M +1522810454 +tags: String, DP, Partition DP +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Partition DP +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- 确定末尾的2种状态: single letter or combos. 然后计算出单个letter的情况, 和双数的情况 +- 定义`dp[i] = 前i个digits最多有多少种decode的方法`. new dp[n + 1]. +- 加法原理: 把不同的情况, single-digit, double-digit 的情况加起来 +- dp[i] += dp[i - x], where x = 1, 2 +- note: calculate number from characters, need to - '0' to get the correct integer mapping. +- 注意: check value != '0', 因为'0' 不在条件之中(A-Z) +- Space, Time O(n) + +#### 引申 +- 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +``` +/* +A message containing letters from A-Z is being encoded to numbers using the following mapping: + +'A' -> 1 +'B' -> 2 +... +'Z' -> 26 +Given an encoded message containing digits, determine the total number of ways to decode it. + +For example, +Given encoded message "12", it could be decoded as "AB" (1 2) or "L" (12). + +The number of ways decoding "12" is 2. +*/ + +/* +Partition DP: a substring represents meaning. +*/ +class Solution { + public int numDecodings(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int n = s.length(); + char[] ss = s.toCharArray(); + int[] dp = new int[n + 1]; + dp[0] = 1; // 1 to help build dp, can be: no digit, means there is only 1 way to decode: no message + dp[1] = s.charAt(0) != '0' ? 1 : 0; // only has s.charAt(0); + for (int i = 2; i <= n; i++) { + int oneDigit = ss[i - 1] - '0'; + if (oneDigit >= 1 && oneDigit <= 9) { + dp[i] += dp[i - 1]; + } + int twoDigit = (ss[i - 1] - '0') + 10 * (ss[i - 2] - '0'); + if (twoDigit >= 10 && twoDigit <= 26) { + dp[i] += dp[i - 2]; + } + } + return dp[n]; + } +} + + + +/* +Thoughts: +Find total number of ways. Think of DP. +At last two letters: treat them as 2 separeate letters, or (if possible) treat them as combo. +dp[i]: # ways to decode at index i. +dp[i - 1]: # ways to decode s[0, i-1] +dp[i - 2]: # ways to decode s[0, i-2] +dp[i] = dp[i-1] + dp[i-2] + +init: +dp[0] = decode s[0,0]: 0 +dp[1] = decode s[0, 1]: 1 +dp[2] = decode s[0, 2]: 1 or 2 +*/ +class Solution { + public int numDecodings(String s) { + if (s == null || s.length() == 0 || s.equals("0")) { + return 0; + } + if (s.length() == 1) { + return 1; + } + int n = s.length(); + int[] dp = new int[n + 1]; + dp[0] = 1; + dp[1] = s.charAt(0) != '0' ? 1 : 0; + + for (int i = 2; i <= n; i++) { + if (s.charAt(i - 1) != '0') { + dp[i] = dp[i - 1]; + } + + int twoDigit = (s.charAt(i - 2) - '0') * 10 + (s.charAt(i - 1) - '0'); + if (twoDigit <= 26 && twoDigit >= 10) { + dp[i] += dp[i - 2]; + } + } + return dp[n]; + } +} +``` \ No newline at end of file diff --git a/Java/Delete Digits.java b/Java/Delete Digits.java old mode 100644 new mode 100755 index a9eaa58..c5435d1 --- a/Java/Delete Digits.java +++ b/Java/Delete Digits.java @@ -1,5 +1,19 @@ +M +1534317486 +tags: Greedy, Priority Queue + +#### Priority Queue +- TODO: parse into node(index, digitValue) +- find the top k, and remove from char array +- O(nlogn) time + +#### Greedy +- 数位靠前的,权值更大. 所以硬来把靠前的相对更大的(跟following digit相比)去掉。 + +``` /* -Given string A representative a positive integer which has N digits, remove any k digits of the number, the remaining digits are arranged according to the original order to become a new positive integer. +Given string A representative a positive integer which has N digits, remove any k digits of the number, +the remaining digits are arranged according to the original order to become a new positive integer. Find the smallest integer after remove k digits. @@ -13,6 +27,10 @@ Tags Expand Greedy LintCode Copyright +*/ + +/* + Attempt2,Thoughts: loop k times: each interation, find one digit to remove Rules: want to remove whatever digit at A[i] that's A[i] > A[i+1]. @@ -21,14 +39,9 @@ Well... thinking straight (attempt2) seems much easier to understand and to code Note: remember to remove the prefixing 0's -*/ +*/ public class Solution { - /** - *@param A: A positive integer which has N digits, A is a string. - *@param k: Remove k digits. - *@return: A string - */ public String DeleteDigits(String A, int k) { if (A == null || A.length() == 0 || k == 0) { return A; @@ -112,3 +125,5 @@ public static String DeleteDigits(String A, int k) { } } + +``` \ No newline at end of file diff --git a/Java/Delete Node in a Linked List.java b/Java/Delete Node in a Linked List.java new file mode 100755 index 0000000..323755c --- /dev/null +++ b/Java/Delete Node in a Linked List.java @@ -0,0 +1,40 @@ +E +1524455545 +tags: Linked List + +Given Singlely linked list, 删除一个任意node (不能是head node) + +#### Basic +- update node.val +- Link curr.next to curr.next.next + +``` +/* +LeetCode +Write a function to delete a node (except the tail) +in a singly linked list, given only access to that node. + +Supposed the linked list is 1 -> 2 -> 3 -> 4 and +you are given the third node with value 3, +the linked list should become 1 -> 2 -> 4 after calling your function. + */ + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +class Solution { + public void deleteNode(ListNode node) { + if (node == null || node.next == null) { + return; + } + node.val = node.next.val; + node.next = node.next.next; + } +} + +``` \ No newline at end of file diff --git a/Java/Design Search Autocomplete System.java b/Java/Design Search Autocomplete System.java new file mode 100644 index 0000000..0520c5d --- /dev/null +++ b/Java/Design Search Autocomplete System.java @@ -0,0 +1,202 @@ +H +1533426244 +tags: Design, Trie, Hash Table, MinHeap, PriorityQueue +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + +``` +/* +LeetCode. +https://leetcode.com/problems/design-search-autocomplete-system/description/ + +Design a search autocomplete system for a search engine. +Users may input a sentence (at least one word and end with a special character '#'). +For each character they type except '#', you need to return the top 3 historical hot sentences +that have prefix the same as the part of sentence already typed. + +Here are the specific rules: + +The hot degree for a sentence is defined as the number of times a user typed the exactly same sentence before. + +The returned top 3 hot sentences should be sorted by hot degree (The first is the hottest one). +If several sentences have the same degree of hot, you need to use ASCII-code order (smaller one appears first). + +If less than 3 hot sentences exist, then just return as many as you can. + +When the input is a special character, it means the sentence ends, and in this case, you need to return an empty list. + +Your job is to implement the following functions: + +The constructor function: + +- AutocompleteSystem(String[] sentences, int[] times): This is the constructor. +The input is historical data. Sentences is a string array consists of previously typed sentences. +Times is the corresponding times a sentence has been typed. Your system should record these historical data. + +- Now, the user wants to input a new sentence. The following function will provide the next character the user types: + +List input(char c): The input c is the next character typed by the user. +The character will only be lower-case letters ('a' to 'z'), blank space (' ') or a special character ('#'). +Also, the previously typed sentence should be recorded in your system. +The output will be the top 3 historical hot sentences that have prefix the same as the part of sentence already typed. + + +Example: +Operation: AutocompleteSystem(["i love you", "island","ironman", "i love leetcode"], [5,3,2,2]) +The system have already tracked down the following sentences and their corresponding times: +"i love you" : 5 times +"island" : 3 times +"ironman" : 2 times +"i love leetcode" : 2 times +Now, the user begins another search: + +Operation: input('i') +Output: ["i love you", "island","i love leetcode"] +Explanation: +There are four sentences that have prefix "i". Among them, "ironman" and "i love leetcode" have same hot degree. +Since ' ' has ASCII code 32 and 'r' has ASCII code 114, "i love leetcode" should be in front of "ironman". +Also we only need to output top 3 hot sentences, so "ironman" will be ignored. + +Operation: input(' ') +Output: ["i love you","i love leetcode"] +Explanation: +There are only two sentences that have prefix "i ". + +Operation: input('a') +Output: [] +Explanation: +There are no sentences that have prefix "i a". + +Operation: input('#') +Output: [] +Explanation: +The user finished the input, the sentence "i a" should be saved as a historical sentence in system. +And the following input will be counted as a new search. + +Note: +The input sentence will always start with a letter and end with '#', and only one blank space will exist between two words. +The number of complete sentences that to be searched won't exceed 100. The length of each sentence including those in the historical data won't exceed 100. +Please use double-quote instead of single-quote when you write test cases even for a character input. +Please remember to RESET your class variables declared in class AutocompleteSystem, as static/class variables are persisted across multiple test cases. Please see here for more details. +*/ + +class AutocompleteSystem { + class TrieNode { + public boolean isEnd; + public Map freq; + public Map children; // Map is more applicable to all chars, not limited to 256 ASCII + public TrieNode() { + this.freq = new HashMap<>(); + this.children = new HashMap<>(); + } + } + class Pair { + String s; + int count; + public Pair(String s, int count) { + this.s = s; + this.count = count; + } + } + TrieNode root, curr; + StringBuffer sb; + public AutocompleteSystem(String[] sentences, int[] times) { + if (sentences == null || times == null || sentences.length != times.length) return; + reset(); + root = new TrieNode(); + for (int i = 0; i < times.length; i++) { + insert(sentences[i], times[i]); + } + } + + public List input(char c) { + List rst = new ArrayList<>(); + if (curr == null) curr = root; + if (c == '#') { // save sentence and reset state + insert(sb.toString(), 1); + reset(); + return rst; + } + + // Update global variable (curr TrieNode and string buffer); or append new character if not exist. + sb.append(c); + curr.children.putIfAbsent(c, new TrieNode()); + curr = curr.children.get(c); + + // MinHeap to find top 3. + rst.addAll(findTopK(curr, 3)); + + return rst; + } + + private List findTopK(TrieNode node, int k) { + List rst = new ArrayList<>(); + if (node.freq.isEmpty()) return rst; + PriorityQueue queue = new PriorityQueue<>( + (a, b) -> a.count == b.count ? b.s.compareTo(a.s) : a.count - b.count); + for (Map.Entry entry : node.freq.entrySet()) { + if (queue.size() < 3 || entry.getValue() >= queue.peek().count) { + queue.offer(new Pair(entry.getKey(), entry.getValue())); + } + if (queue.size() > 3) queue.poll(); + } + + while (!queue.isEmpty()) { + rst.add(0, queue.poll().s); + } + + return rst; + } + + private void reset() { + curr = null; + sb = new StringBuffer(); + } + + private void insert(String sentence, int count) { + if (sentence == null || sentence.length() == 0) return; + TrieNode node = root; + for (char c : sentence.toCharArray()) { + node.children.putIfAbsent(c, new TrieNode()); + node = node.children.get(c); + node.freq.put(sentence, node.freq.getOrDefault(sentence, 0) + count); + } + node.isEnd = true; // can set word to node as well, if needed + } +} + +/** + * Your AutocompleteSystem object will be instantiated and called as such: + * AutocompleteSystem obj = new AutocompleteSystem(sentences, times); + * List param_1 = obj.input(c); + */ + +``` \ No newline at end of file diff --git a/Java/Diameter of Binary Tree.java b/Java/Diameter of Binary Tree.java new file mode 100644 index 0000000..434b0a9 --- /dev/null +++ b/Java/Diameter of Binary Tree.java @@ -0,0 +1,57 @@ +E +1533515503 +tags: Tree + +找longest path (include or not include root) + +跟Binary Tree Maximum Path Sum 的想法一样: 处理single path, 或者combined path (do not include curr root) + +#### Singlepath, combined path +- `int[]{combinedPath, singlePath}`; +- pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; +- complete left/right child, or join curr root: `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`; + +``` +/* +Given a binary tree, you need to compute the length of the diameter of the tree. +The diameter of a binary tree is the length of the longest path between any two nodes in a tree. +This path may or may not pass through the root. + +Example: +Given a binary tree + 1 + / \ + 2 3 + / \ + 4 5 +Return 3, which is the length of the path [4,2,1,3] or [5,2,1,3]. + +Note: The length of path between two nodes is represented by the number of edges between them. + + +*/ + +/* +No special condition: left+root+right, or left, or right +recursive on itself. +end condition: if null -> 0, if leaf -> 1 +*/ +class Solution { + public int diameterOfBinaryTree(TreeNode root) { + if (root == null) return 0; + int[] rst = dfs(root); + + return Math.max(rst[0], rst[1]) - 1; + } + + private int[] dfs(TreeNode root) { + if (root == null) return new int[] {0, 0}; + + int[] left = dfs(root.left); + int[] right = dfs(root.right); + int singlePath = Math.max(left[1] , right[1]) + 1; // pick single path + 1 + int combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1); // complete left/right child, or join curr root. + return new int[]{combinedPath, singlePath}; + } +} +``` \ No newline at end of file diff --git a/Java/Distinct Subsequences.java b/Java/Distinct Subsequences.java old mode 100644 new mode 100755 index 35cf479..7f32d39 --- a/Java/Distinct Subsequences.java +++ b/Java/Distinct Subsequences.java @@ -1,8 +1,115 @@ +H +1519279629 +tags: String, DP + +Double Sequence DP: +0. DP size (n+1): 找前nth的结果, 那么dp array就需要开n+1, 因为结尾要return dp[n][m] +1. 在for loop 里面initialize dp[0][j] dp[i][0] +2. Rolling array 优化成O(N): 如果dp[i][j]在for loop里面, 就很好替换 curr/prev + +``` /* -Given a string S and a string T, count the number of distinct subsequences of T in S. +LeetCode +Given a string S and a string T, count the number of distinct subsequences of S which equals T. A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not). +Here is an example: +S = "rabbbit", T = "rabbit" + +Return 3. + +*/ +/* +Thoughts: +2 Sequence, count (adds up) DP. +dp[i][j]: # of distinct subsequence of B[0 ~ j - 1] that can occur in A[0 ~ i - 1] +Consider the last index of A and B. There can be two conditions: +1. A's last index is not part of B. +2. A's last index is part of B. + +dp[i][j] = dp[i - 2][j - 1] + dp[i - 2][j - 2]|A[i-1]==B[j-1]; + +dp[0][0] = 0; +dp[i][0] = 1; // ignore physical meaning, requirement for DP. + +Time,Space: O(MN) + +*/ +class Solution { + public int numDistinct(String s, String t) { + if (s == null || t == null || t.length() > s.length()) { + return 0; + } else if (s.equals(t)) { + return 1; + } + + int m = s.length(); + int n = t.length(); + int[][] dp = new int[m + 1][n + 1]; + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (j == 0) { + dp[i][j] = 1; + continue; + } + if (i == 0) { + dp[i][j] = 0; + continue; + } + dp[i][j] = dp[i - 1][j]; + if (s.charAt(i - 1) == t.charAt(j - 1)) { + dp[i][j] += dp[i - 1][j - 1]; + } + } + } + return dp[m][n]; + } +} + +// Optimize, rolling array: Space O(N) +class Solution { + public int numDistinct(String s, String t) { + if (s == null || t == null || t.length() > s.length()) { + return 0; + } else if (s.equals(t)) { + return 1; + } + + int m = s.length(); + int n = t.length(); + int[][] dp = new int[2][n + 1]; + int curr = 0; + int prev = 0; + for (int i = 0; i <= m; i++) { + prev = curr; + curr = 1 - prev; + for (int j = 0; j <= n; j++) { + if (j == 0) { + dp[curr][j] = 1; + continue; + } + if (i == 0) { + dp[curr][j] = 0; + continue; + } + dp[curr][j] = dp[prev][j]; + if (s.charAt(i - 1) == t.charAt(j - 1)) { + dp[curr][j] += dp[prev][j - 1]; + } + } + } + return dp[curr][n]; + } +} + +/* +Given a string S and a string T, count the number of distinct subsequences of T in S. + +A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) +of the characters without disturbing the relative positions of the remaining characters. +(ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not). + Example Given S = "rabbbit", T = "rabbit", return 3. @@ -18,29 +125,29 @@ Do it in O(n2) time and O(n) memory. Attempt2: Use DP. Okay, first I had no idea how to start, but here is a reference: http://blog.csdn.net/abcbc/article/details/8978146 First of all, Map out the number of existance of T in S in a 2D map: - 0 1 2 3 4 5 6 7 - --------------- - r a b b b i t -0| 1 1 1 1 1 1 1 1 -1| r 0 1 1 1 1 1 1 1 -2| a 0 0 1 1 1 1 1 1 -3| b 0 0 0 1 2 3 3 3 -4| b 0 0 0 0 1 3 3 3 -5| i 0 0 0 0 0 0 3 3 -6| t 0 0 0 0 0 0 0 3 + 0 1 2 3 4 5 6 7 + --------------- + r a b b b i t +0| 1 1 1 1 1 1 1 1 +1| r 0 1 1 1 1 1 1 1 +2| a 0 0 1 1 1 1 1 1 +3| b 0 0 0 1 2 3 3 3 +4| b 0 0 0 0 1 3 3 3 +5| i 0 0 0 0 0 0 3 3 +6| t 0 0 0 0 0 0 0 3 Use DP[T][S]. We realize: 1.DP[0][0] == 1; //Both null can be a match 2.DP[0][1 ~ S.length - 1] = 1;//First fow, when T=="", whatever S will have 1 subsequence: "" 3.DP[1 ~ T.length][0] = 0;// First column, when S=="", whatever T will not be subsequence of S == "" 4.When looking at each row and filling out the pixels, we realize when T exist in S[a~b], it will surely exist in S[a~b+1], taht is: - Step1: DP[i][j] is at least equal to DP[i][j - 1];//DP[i][j] is always based on DP[i][j-1], so DP[i][j] = DP[i][j+1] + something - Step2: So, what's that 'something' in step1? For example, look at T[3] == 'b' against S[0 ~ 3]: - S[0 ~ 3] has 1 'b' at S[3], and also, T[0~3] == S[0~3], that's a perfect match. SO DP[3][3] = 1 - S[0 ~ 4] has 2 'b' at S[3] and S[4]. Now imagine we pick either S[3] or S[4] to genreate T[0~3] out of S[0~4]: we have 2 possibilities.D[3][4] = 2 - Consider: D[i][j] means we picked S[j]; in our S[0 ~ 4] case, that means we picked S[4] but skipped S[3], though S[3] still counts towards another situation where we skipped S[4]. - After all, we will count whatever that we skipped into our current DP[i][j], that is DP[i][j] += T[i - 1] == S[j - 1] ? DP[i - 1][j - 1] : 0; - Conclusion: while we for-looping through each row, if we find out S[j] and S[j - 1] both equals to T[i - 1], we want to make sure we count D[i - 1][j -1]'s previous records in! + Step1: DP[i][j] is at least equal to DP[i][j - 1];//DP[i][j] is always based on DP[i][j-1], so DP[i][j] = DP[i][j+1] + something + Step2: So, what's that 'something' in step1? For example, look at T[3] == 'b' against S[0 ~ 3]: + S[0 ~ 3] has 1 'b' at S[3], and also, T[0~3] == S[0~3], that's a perfect match. SO DP[3][3] = 1 + S[0 ~ 4] has 2 'b' at S[3] and S[4]. Now imagine we pick either S[3] or S[4] to genreate T[0~3] out of S[0~4]: we have 2 possibilities.D[3][4] = 2 + Consider: D[i][j] means we picked S[j]; in our S[0 ~ 4] case, that means we picked S[4] but skipped S[3], though S[3] still counts towards another situation where we skipped S[4]. + After all, we will count whatever that we skipped into our current DP[i][j], that is DP[i][j] += T[i - 1] == S[j - 1] ? DP[i - 1][j - 1] : 0; + Conclusion: while we for-looping through each row, if we find out S[j] and S[j - 1] both equals to T[i - 1], we want to make sure we count D[i - 1][j -1]'s previous records in! Note: In double for loop, set i,j <= xxxx.length(), since we've increased the 2D array by 1 block on row and col. @@ -53,23 +160,23 @@ public class Solution { * @return: Count the number of distinct subsequences */ public int numDistinct(String S, String T) { - int[][] DP = new int[T.length() + 1][S.length() + 1]; - DP[0][0] = 1; - for(int i = 1; i < S.length(); i++) { - DP[0][i] = 1; - } - for (int i = 1; i < T.length(); i++) { - DP[i][0] = 0; - } - for (int i = 1; i <= T.length(); i++) { - for (int j = 1; j <= S.length(); j++){ - DP[i][j] = DP[i][j - 1]; - if (T.charAt(i - 1) == S.charAt(j - 1)) { - DP[i][j] += DP[i - 1][j - 1]; - } - } - } - return DP[T.length()][S.length()]; + int[][] DP = new int[T.length() + 1][S.length() + 1]; + DP[0][0] = 1; + for(int i = 1; i < S.length(); i++) { + DP[0][i] = 1; + } + for (int i = 1; i < T.length(); i++) { + DP[i][0] = 0; + } + for (int i = 1; i <= T.length(); i++) { + for (int j = 1; j <= S.length(); j++){ + DP[i][j] = DP[i][j - 1]; + if (T.charAt(i - 1) == S.charAt(j - 1)) { + DP[i][j] += DP[i - 1][j - 1]; + } + } + } + return DP[T.length()][S.length()]; } } @@ -81,30 +188,19 @@ public int numDistinct(String S, String T) { */ public class Solution { public int numDistinct(String S, String T) { - if (S.length() == 0) { - return T.length() == 0 ? 1 : 0; - } - if (T.length() == 0) { - return 1; - } - int count = 0; - for (int i = 0; i < S.length(); i++) { - if (S.charAt(i) == T.charAt(0)) { - count += numDistinct(S.substring(i + 1), T.substring(1)); - } - } - return count; + if (S.length() == 0) { + return T.length() == 0 ? 1 : 0; + } + if (T.length() == 0) { + return 1; + } + int count = 0; + for (int i = 0; i < S.length(); i++) { + if (S.charAt(i) == T.charAt(0)) { + count += numDistinct(S.substring(i + 1), T.substring(1)); + } + } + return count; } } - - - -/* -First Thought: -find the # of ways to get T from S, while having to follow the rules of 'subsequence' -How about: find what chars are missing in T based on S, then find the number of ways to insert the missing chars to make it back to S? -The missing chars: misChars = new ArrayList(); -However, time cost on this: -For example I have n missing chars from S.length == m. so I have (m + 1) places where i can insert the n chars. Then it's a mCn problem. This goes up to m!, too much. Not applicapable. - -*/ \ No newline at end of file +``` \ No newline at end of file diff --git a/Java/Edit Distance.java b/Java/Edit Distance.java old mode 100644 new mode 100755 index 916b78f..7aa8c1f --- a/Java/Edit Distance.java +++ b/Java/Edit Distance.java @@ -1,5 +1,38 @@ +H +1519272534 +tags: String, DP, Double Sequence DP, Sequence DP +time: O(MN) +Space: O(N) + +两个字符串, A要变成B, 可以 insert/delete/replace, 找最小变化operation count + +#### Double Sequence +- 考虑两个字符串的末尾index s[i], t[j]: 如果需要让这两个字符一样, 可能使用题目给出的三种operation: insert/delete/replace? +- 先calculate最坏的情况, 3种operation count + 1; 然后在比较match的情况. +- 注意, 在i或者j为0的时候, 变成另外一个数字的steps只能是全变. +- 第一步, 空间时间都是O(MN), O(MN) +- 滚动数组优化, 空间O(N) + +##### Detail analysis +- insert: assume insert on s, `#ofOperation = (s[0 ~ i] to t[0 ~ j-1]) + 1;` +- delete: assume delete on t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j]) + 1;` +- replace: replace both s and t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j - 1]) + 1;` +- dp[i][j]代表了两个 sequence 互相之间的性质: s[0 ~ i] 转换成 s[0~j] 所需要的最少 operation count +- init: 当i==0, dp[0][j] = j; 每次都要 + j 个character; 同理, 当j==0, dp[i][0] = i; +- 而dp[i][j]有两种情况处理: `s[i] == t[j]` or `s[i] != t[j]` + +##### 何时initialize +- 这种判断取决于经验: 如果知道initialization可以再 double for loop 里面一起做, 那么可以留着那么做 +- 这样属于 `需要什么, initialize什么` +- 事后在做space optimization的时候, 可以轻易在 1st dimension 上做rolling array + +#### Search +- 可以做, 但是不建议:这道题需要找 min count, 而不是search/find all solutions, 所以search会写的比较复杂, 牛刀杀鸡. + +``` /* -Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.) +Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. +(each operation is counted as 1 step.) You have the following 3 operations permitted on a word: @@ -12,8 +45,200 @@ Tags Expand String Dynamic Programming +*/ +/* Thoughts: +If certain character matches, no opeation needed. +If not matching, use the 3 operations and count of operations + 1 +dp[i][j] represents the # of changes from A[0 ~ i - 1] to B[0 ~ j - 1] + +init: for word1[0, 0] to change to words[0 ,j]: it has to be j add operations +*/ +class Solution { + public int minDistance(String word1, String word2) { + if (word1 == null && word2 == null) return 0; + if (word1 == null || word2 == null) return word1 == null ? word2.length() : word1.length(); + + int m = word1.length(); + int n = word2.length(); + int[][] dp = new int[m + 1][n + 1]; + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (i == 0) { + dp[i][j] = j; + continue; + } + if (j == 0) { + dp[i][j] = i; + continue; + } + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { // 3 ways to find replace, find min + dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])) + 1; + } + } + } + return dp[m][n]; + } +} + +// Rolling array +class Solution { + public int minDistance(String word1, String word2) { + if (word1 == null && word2 == null) return 0; + if (word1 == null || word2 == null) return word1 == null ? word2.length() : word1.length(); + + int m = word1.length(); + int n = word2.length(); + int[][] dp = new int[2][n + 1]; + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (i == 0) { + dp[i%2][j] = j; + continue; + } + if (j == 0) { + dp[i%2][j] = i; + continue; + } + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + dp[i%2][j] = dp[(i - 1)%2][j - 1]; + } else { // 3 ways to find replace, find min + dp[i%2][j] = Math.min(dp[(i - 1)%2][j - 1], Math.min(dp[i%2][j - 1], dp[(i - 1)%2][j])) + 1; + } + } + } + return dp[m%2][n]; + } +} + +// Init operation ahead of double for loop +// This approach is okay, but cannot be optimized for space since dp[i][j] has to be initialized completely +class Solution { + public int minDistance(String word1, String word2) { + if (word1 == null && word2 == null) return 0; + if (word1 == null || word2 == null) return word1 == null ? word2.length() : word1.length(); + + int m = word1.length(); + int n = word2.length(); + int[][] dp = new int[m + 1][n + 1]; + + // init + for (int i = 0; i <= m; i++) { + dp[i][0] = i; + } + for (int j = 0; j <= n; j++) { + dp[0][j] = j; + } + + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (word1.charAt(i - 1) == word2.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1]; + } else { // 3 ways to find replace, find min + dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])) + 1; + } + } + } + return dp[m][n]; + } +} + +/* +Thoughts: +Two sequences, looking for min # steps of changes to reach equivalence +DP[i][j] min # of steps to reach equivalence with word1[0 ~ i-1], and word2[0 ~ j-1]. +To compute DP[i][j], we rely on (i-1) index and (j-1) index. Which has 3 possible conditions: +1. word1[i-1] == word2[j-1] => dp[i -1][j - 1] +2. word1[i-1] != word2[j-1] => repalcement dp[i - 1][j - 1] + 1 +3. Insert/Delete => dp[i][j - 1] + 1 or dp[i - 1][j] + 1; where insert and delete has similar effect. + +dp[i][j] takese the min of all above cases + +dp[0][0] = 0; +dp[0][j] = j; +dp[i][0] = i; + +Space: O(MN) +Time: O(MN) +*/ +class Solution { + public int minDistance(String word1, String word2) { + if (word1 == null && word2 == null) { + return 0; + } + char[] words1 = word1 == null ? new char[0] : word1.toCharArray(); + char[] words2 = word2 == null ? new char[0] : word2.toCharArray(); + + int m = word1.length(); + int n = word2.length(); + int[][] dp = new int[m + 1][n + 1]; + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (i == 0) { + dp[i][j] = j; + continue; + } + if (j == 0) { + dp[i][j] = i; + continue; + } + dp[i][j] = Math.min(dp[i - 1][j - 1], Math.min(dp[i][j - 1], dp[i - 1][j])) + 1; + if (words1[i - 1] == words2[j - 1]) { + dp[i][j] = Math.min(dp[i][j], dp[i - 1][j - 1]); + } + } + } + return dp[m][n]; + } +} + + +// Optimize: Rolling array +// Space O(N) +class Solution { + public int minDistance(String word1, String word2) { + if (word1 == null && word2 == null) { + return 0; + } + char[] words1 = word1 == null ? new char[0] : word1.toCharArray(); + char[] words2 = word2 == null ? new char[0] : word2.toCharArray(); + + int m = word1.length(); + int n = word2.length(); + int[][] dp = new int[2][n + 1]; + int curr = 0; + int prev = 0; + + for (int i = 0; i <= m; i++) { + prev = curr; + curr = 1 - prev; + for (int j = 0; j <= n; j++) { + if (i == 0) { + dp[curr][j] = j; + continue; + } + if (j == 0) { + dp[curr][j] = i; + continue; + } + dp[curr][j] = Math.min(dp[prev][j - 1], Math.min(dp[curr][j - 1], dp[prev][j])) + 1; + if (words1[i - 1] == words2[j - 1]) { + dp[curr][j] = Math.min(dp[curr][j], dp[prev][j - 1]); + } + } + } + return dp[curr][n]; + } +} + +/** +Previous Notes: Draw a 2D array, consider rows as word1 and cols as word2. DP[i][j] means the steps (edit distance) to take to transfer word1[0 ~ i] to word2[0 ~ j] And, we have 3 different calculations for the 3 methods: @@ -22,15 +247,9 @@ DP[i][j] means the steps (edit distance) to take to transfer word1[0 ~ i] to wor 3. Delete: DP[i][j] = word1[i][j - 1] + 1; // extra char in word1 Note: just remember to start from i=1,j=1, because we are using DP[i-1][j-1], becareful with border case -*/ - - + */ public class Solution { - /** - * @param word1 & word2: Two string. - * @return: The minimum number of steps. - */ public int minDistance(String word1, String word2) { if (word1 == null && word2 != null) { return word2.length(); @@ -56,3 +275,5 @@ public int minDistance(String word1, String word2) { return DP[word1.length()][word2.length()]; } } + +``` \ No newline at end of file diff --git a/Java/Encode and Decode Strings.java b/Java/Encode and Decode Strings.java new file mode 100755 index 0000000..600768d --- /dev/null +++ b/Java/Encode and Decode Strings.java @@ -0,0 +1,151 @@ +M +1527968983 +tags: String + +如题. + +#### String +- 'word.length()#word' 这样encode, 可以避免遇到# +- 基于我们自己定的规律, 在decode的里面不需要过多地去check error input, assume所有input都是规范的. +- decode就是找"#",然后用"#"前的数字截取后面的string. + + +``` +/* +Design an algorithm to encode a list of strings to a string. +The encoded string is then sent over the network and is decoded back to the original list of strings. + +Machine 1 (sender) has the function: + +string encode(vector strs) { + // ... your code + return encoded_string; +} +Machine 2 (receiver) has the function: +vector decode(string s) { + //... your code + return strs; +} +So Machine 1 does: + +string encoded_string = encode(strs); +and Machine 2 does: + +vector strs2 = decode(encoded_string); +strs2 in Machine 2 should be the same as strs in Machine 1. + +Implement the encode and decode methods. + +Note: +The string may contain any possible characters out of 256 valid ascii characters. +Your algorithm should be generalized enough to work on any possible characters. + +Do not use class member/global/static variables to store states. +Your encode and decode algorithms should be stateless. + +Do not rely on any library method such as eval or serialize methods. +You should implement your own encode/decode algorithm. +*/ + + +/* +Use word.length() + "#" + word to mark a string. Append them all. +*/ +public class Codec { + + // Encodes a list of strings to a single string. + public String encode(List strs) { + if (strs.size() == 0) { + return ""; + } + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < strs.size(); i++) { + sb.append(strs.get(i).length() + "#" + strs.get(i)); + } + return sb.toString(); + } + + // Decodes a single string to a list of strings. + public List decode(String s) { + List strs = new ArrayList<>(); + if (s == null || s.length() == 0) { + return strs; + } + int start = 0; + while (start < s.length()) { + int ind = s.indexOf("#", start); + int leng = Integer.parseInt(s.substring(start, ind)); + + int end = ind + 1 + leng; + strs.add(s.substring(ind + 1, end)); + start = end; + } + return strs; + } +} + + +/* +Thoughts: +Break into integers +Use some special words to: 1. break line. 2. record null condition. +Note: "" empty string is also a string case, so don't treat that as null. Call null, "NULL" +Note2: As long as the list is not empty, though some string might be just "", make sure to encode it as 'LINE' just to remind in decoder: treat it as a "" +*/ +public class Codec { + // Encodes a list of strings to a single string. + public static String encode(List strs) { + if (strs == null || strs.size() == 0) { + return "NULL"; + } + StringBuffer sb = new StringBuffer(); + for (String str : strs) { + char[] arr = str.toCharArray(); + for (int i = 0; i < arr.length; i++) { + if (arr[i] >= 100) { + sb.append("" + (int)arr[i]); + } else if (arr[i] >= 10) { + sb.append("0" + (int)arr[i]); + } else { + sb.append("00" + (int)arr[i]); + } + } + sb.append("LINE"); + }//END for + if (sb.length() == 0) { + sb.append("LINE"); + } + return sb.toString(); + } + + // Decodes a single string to a list of strings. + public static List decode(String s) { + List rst = new ArrayList(); + if (s.equals("NULL")) { + return rst; + } + int index = s.indexOf("LINE"); + while (index != -1) { + String str = s.substring(0, index); + + StringBuffer sb = new StringBuffer(); + int i = 0; + while (i + 3 <= str.length()) { + int letter = Integer.parseInt(str.substring(i, i + 3)); + sb.append((char)letter); + i+=3; + } + rst.add(sb.toString()); + + s = s.substring(index + 4); + index = s.indexOf("LINE"); + } + + return rst; + } +} +// Your Codec object will be instantiated and called as such: +// Codec codec = new Codec(); +// codec.decode(codec.encode(strs)); + +``` \ No newline at end of file diff --git a/Java/Encode and Decode TinyURL.java b/Java/Encode and Decode TinyURL.java new file mode 100644 index 0000000..0a9770e --- /dev/null +++ b/Java/Encode and Decode TinyURL.java @@ -0,0 +1,38 @@ +M +1516430518 +tags: Hash Table, Math + +其实想到了切入点, 是个可难可简单的题目. 这里的encode就是想办法把url存起来, 然后给个 key. +那么具体怎么做这个key, 简单就可以用一个map, 然后counting. 复杂一点就可以做random letter/number组成key. + +``` +/* +TinyURL is a URL shortening service where you enter a URL such as https://leetcode.com/problems/design-tinyurl and it returns a short URL such as http://tinyurl.com/4e9iAk. + +Design the encode and decode methods for the TinyURL service. There is no restriction on how your encode/decode algorithm should work. You just need to ensure that a URL can be encoded to a tiny URL and the tiny URL can be decoded to the original URL. +*/ + +/* +Use a hashmap to store. Come up with a method to store keys. +Most easies way, use size of the map as keys +*/ +public class Codec { + final String PREFIX = "http://tinyurl.com/"; + final Map map = new HashMap<>(); + // Encodes a URL to a shortened URL. + public String encode(String longUrl) { + final String key = PREFIX + (map.size() + 1); + map.put(key, longUrl); + return key; + } + + // Decodes a shortened URL to its original URL. + public String decode(String shortUrl) { + return map.get(shortUrl); + } +} + +// Your Codec object will be instantiated and called as such: +// Codec codec = new Codec(); +// codec.decode(codec.encode(url)); +``` \ No newline at end of file diff --git a/Java/Evaluate Division.java b/Java/Evaluate Division.java new file mode 100644 index 0000000..3a9c43e --- /dev/null +++ b/Java/Evaluate Division.java @@ -0,0 +1,81 @@ +M +tags: Graph, DFS, Union Find, BFS + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + +``` +/* +Equations are given in the format A / B = k, where A and B are variables represented as strings, and k is a real number (floating point number). Given some queries, return the answers. If the answer does not exist, return -1.0. + +Example: +Given a / b = 2.0, b / c = 3.0. +queries are: a / c = ?, b / a = ?, a / e = ?, a / a = ?, x / x = ? . +return [6.0, 0.5, -1.0, 1.0, -1.0 ]. + +The input is: vector> equations, vector& values, vector> queries , where equations.size() == values.size(), and the values are positive. This represents the equations. Return vector. + +According to the example above: + +equations = [ ["a", "b"], ["b", "c"] ], +values = [2.0, 3.0], +queries = [ ["a", "c"], ["b", "a"], ["a", "e"], ["a", "a"], ["x", "x"] ]. +The input is always valid. You may assume that evaluating the queries will result in no division by zero and there is no contradiction. + + +*/ + +class Solution { + Map valueMap = new HashMap<>(); + Map> graph = new HashMap<>(); + public double[] calcEquation(String[][] equations, double[] values, String[][] queries) { + // build graph + for (int i = 0; i < values.length; i++) { + String x = equations[i][0], y = equations[i][1]; + valueMap.put(getKey(x, y), values[i]); + valueMap.put(getKey(y, x), 1.0 / values[i]); + graph.putIfAbsent(x, new ArrayList<>()); + graph.putIfAbsent(y, new ArrayList<>()); + graph.get(x).add(y); + graph.get(y).add(x); + } + + // dfs on each item + int n = queries.length; + double[] result = new double[n]; + for (int i = 0; i < n; i++) { + String[] query = queries[i]; + result[i] = dfs(new HashSet<>(), query[0], query[1], 1.0); + if (result[i] == 0.0) result[i] = -1.0; + } + + return result; + } + + private double dfs(Set visited, String start, String end, double value) { + if (visited.contains(start)) return 0.0; + if (!graph.containsKey(start)) return 0.0; + if (start.equals(end)) return value; + visited.add(start); + + double temp = 0.0; + for (String next : graph.get(start)) { + String nextKey = getKey(start, next); + temp = dfs(visited, next, end, value * valueMap.get(nextKey)); + if (temp != 0.0) break; + } + return temp; + } + + private String getKey(String a, String b) { + return a + "/" + b; + } +} +``` \ No newline at end of file diff --git a/Java/Evaluate Reverse Polish Notation.java b/Java/Evaluate Reverse Polish Notation.java new file mode 100644 index 0000000..688ab98 --- /dev/null +++ b/Java/Evaluate Reverse Polish Notation.java @@ -0,0 +1,97 @@ +M +1524553446 +tags: Stack + +给一个 RPN string list, 根据这个list, 计算结果. + +#### Stack +- stack 里面 存数字 +- 每次遇到operator, 都拿前2个数字计算 +- 计算结果存回到stack里面, 方便下一轮使用. +- Time,Space O(n) + + +``` +/** +Evaluate the value of an arithmetic expression in Reverse Polish Notation. + +Valid operators are +, -, *, /. Each operand may be an integer or another expression. + +Note: + +Division between two integers should truncate toward zero. +The given RPN expression is always valid. +That means the expression would always evaluate to a result +and there won't be any divide by zero operation. + +Example 1: + +Input: ["2", "1", "+", "3", "*"] +Output: 9 +Explanation: ((2 + 1) * 3) = 9 +Example 2: + +Input: ["4", "13", "5", "/", "+"] +Output: 6 +Explanation: (4 + (13 / 5)) = 6 +Example 3: + +Input: ["10", "6", "9", "3", "+", "-11", "*", "/", "*", "17", "+", "5", "+"] +Output: 22 +Explanation: + ((10 * (6 / ((9 + 3) * -11))) + 17) + 5 += ((10 * (6 / (12 * -11))) + 17) + 5 += ((10 * (6 / -132)) + 17) + 5 += ((10 * 0) + 17) + 5 += (0 + 17) + 5 += 17 + 5 += 22 + */ + + + class Solution { + public int evalRPN(String[] tokens) { + if (tokens == null || tokens.length == 0) { + return 0; + } + Stack stack = new Stack<>(); + for (String s : tokens) { + if (isOperator(s)) { + long numB = stack.pop(); + long numA = stack.pop(); + stack.push(eval(numA, numB, s)); + } else { + stack.push(Long.parseLong(s)); + } + } + return stack.pop().intValue(); + } + + public boolean isOperator(String s) { + if (s.length() != 1) { + return false; + } + char c = s.charAt(0); + return c == '+' || c == '-' || c == '/' || c == '*'; + } + + public long eval(long a, long b, String s) { + long rst = 0; + switch (s) { + case "*": + rst = a * b; + break; + case "/": + rst = a / b; + break; + case "+": + rst = a + b; + break; + case "-": + rst = a - b; + break; + } + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/Exam Room.java b/Java/Exam Room.java new file mode 100644 index 0000000..218e004 --- /dev/null +++ b/Java/Exam Room.java @@ -0,0 +1,132 @@ +M +1531359827 +tags: PriorityQueue, Sort + +#### PriorityQueue +- Use priority queue to sort by customized class interval{int dist; int x, y;}. +- Sort by larger distance and then sort by start index +- seat(): pq.poll() to find interval of largest distance. Split and add new intervals back to queue. +- leave(x): one seat will be in 2 intervals: remove both from pq, and merge to a new interval. +- 主方程写出来其实很好写, 就是 split + add interval, 然后 find + delete interval 而已. 最难的是构建data structure +- seat(): O(logn), leave(): O(n) + +##### Trick: 构建虚拟 boundary +- 如果是开头的seat, 或者是结尾的seat, 比较难handle: 一开始坐在seat=0的时候, 没有interval啊! +- Trick就是, 我们自己定义个虚拟的座位 `seat=-1`, `seat=N` +- 一开始有一个 interval[-1, N] 然后就建立了boundary. +- 从此以后, 每次split成小interval的时候: +- 遇到 `interval[-1, y]`, distance就是 `(y - 0)` +- 遇到 `interval[x, N]`, distance就是 `(N - 1 - x)` +- 当然正常的interval dist 就是 `(y - x) / 2` + +##### distance 中间点 +- Interval.dist 我们其实做的是 distance的中间点 `(y - x) / 2` +- 这里的dist是 `距离两边的距离` 而不是 x, y 之间的距离. 这里要特别注意. + +#### TreeSet +- https://leetcode.com/problems/exam-room/discuss/139885/Java-Solution-based-on-treeset/153588 + +#### Map +- how? +- TODO, not sure. + +``` +/* +In an exam room, there are N seats in a single row, numbered 0, 1, 2, ..., N-1. + +When a student enters the room, they must sit in the seat that maximizes the distance to the closest person. +If there are multiple such seats, they sit in the seat with the lowest number. +(Also, if no one is in the room, then the student sits at seat number 0.) + +Return a class ExamRoom(int N) that exposes two functions: +ExamRoom.seat() returning an int representing what seat the student sat in, +and ExamRoom.leave(int p) representing that the student in seat number p now leaves the room. +It is guaranteed that any calls to ExamRoom.leave(p) have a student sitting in seat p. + + + +Example 1: + +Input: ["ExamRoom","seat","seat","seat","seat","leave","seat"], [[10],[],[],[],[],[4],[]] +Output: [null,0,9,4,2,null,5] +Explanation: +ExamRoom(10) -> null +seat() -> 0, no one is in the room, then the student sits at seat number 0. +seat() -> 9, the student sits at the last seat number 9. +seat() -> 4, the student sits at the last seat number 4. +seat() -> 2, the student sits at the last seat number 2. +leave(4) -> null +seat() -> 5, the student​​​​​​​ sits at the last seat number 5. +​​​​​​​ + +Note: + +1 <= N <= 10^9 +ExamRoom.seat() and ExamRoom.leave() will be called at most 10^4 times across all test cases. +Calls to ExamRoom.leave(p) are guaranteed to have a student currently sitting in seat number p. + */ + +// PriorityQueue +class ExamRoom { + PriorityQueue pq; + int N; + + class Interval { + int x, y, dist; + public Interval(int x, int y) { + this.x = x; + this.y = y; + if (x == -1) { + this.dist = y; + } else if (y == N) { + this.dist = N - 1 - x; + } else { + this.dist = Math.abs(x - y) / 2; + } + } + } + + public ExamRoom(int N) { + this.pq = new PriorityQueue<>((a, b) -> a.dist != b.dist? b.dist - a.dist : a.x - b.x); + this.N = N; + pq.add(new Interval(-1, N)); + } + + // O(logn): poll top candidate, split into two new intervals + public int seat() { + int seat = 0; + Interval interval = pq.poll(); + if (interval.x == -1) seat = 0; + else if (interval.y == N) seat = N - 1; + else seat = (interval.x + interval.y) / 2; + + pq.offer(new Interval(interval.x, seat)); + pq.offer(new Interval(seat, interval.y)); + + return seat; + } + + // O(n)Find head and tail based on p. Delete and merge two ends + public void leave(int p) { + Interval head = null, tail = null; + List intervals = new ArrayList<>(pq); + for (Interval interval : intervals) { + if (interval.x == p) tail = interval; + if (interval.y == p) head = interval; + if (head != null && tail != null) break; + } + // Delete + pq.remove(head); + pq.remove(tail); + // Merge + pq.offer(new Interval(head.x, tail.y)); + } +} + +/** + * Your ExamRoom object will be instantiated and called as such: + * ExamRoom obj = new ExamRoom(N); + * int param_1 = obj.seat(); + * obj.leave(p); + */ +``` \ No newline at end of file diff --git a/Java/Excel Sheet Column Number.java b/Java/Excel Sheet Column Number.java new file mode 100755 index 0000000..3f822d9 --- /dev/null +++ b/Java/Excel Sheet Column Number.java @@ -0,0 +1,64 @@ +E +1524455965 +tags: Math + +#### Math +- 26位的运算, 根据10位运算去思考 +- 'A' - 'A' = 0. 所以 char - 'A' + 1 = 题目里的对应数位 +- 或者: 26位运算和10位一样:num += 每位的digit * Math.pow(26, 数位号) + + +``` +/* +Given a column title as appear in an Excel sheet, +return its corresponding column number. + +For example: + + A -> 1 + B -> 2 + C -> 3 + ... + Z -> 26 + AA -> 27 + AB -> 28 +*/ + +/* +Thoughts: +- 26-bits. +- Char array, head char is at most significant index. +- char - 'a' + 1 = number + +*/ +class Solution { + public int titleToNumber(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int count = 0; + for (char c : s.toCharArray()) { + int digit = c - 'A' + 1; + count = count * 26 + digit; + } + return count; + } +} + +//3.4.2016 recap +//digit * pow(26, digit position) +public class Solution { + public int titleToNumber(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int num = 0; + for (int i = s.length() - 1; i >= 0; i--) { + int digit = s.charAt(i) - 'A' + 1; + num += digit * Math.pow(26, s.length() - i - 1); + } + return num; + } +} + +``` \ No newline at end of file diff --git a/Java/Excel Sheet Column Title.java b/Java/Excel Sheet Column Title.java new file mode 100644 index 0000000..dee8c1f --- /dev/null +++ b/Java/Excel Sheet Column Title.java @@ -0,0 +1,52 @@ +E +1524457828 +tags: Math + +#### 基本换算 +- 26位 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + +``` +/* +Given a positive integer, return its corresponding column title +as appear in an Excel sheet. + +For example: + + 1 -> A + 2 -> B + 3 -> C + ... + 26 -> Z + 27 -> AA + 28 -> AB +*/ + +/* +Thoughts: +26 bits => num / 26 = 1 => 'A' on that digit +*/ +class Solution { + public String convertToTitle(int n) { + if (n <= 0) { + return null; + } + + StringBuilder sb = new StringBuilder(); + while (n > 0) { + int remain = n % 26; + n = n / 26; + if (remain == 0) { + sb.append("Z"); + n--; + } else { + sb.append((char)('A' + remain - 1)); + } + } + return sb.reverse().toString(); + } +} +``` \ No newline at end of file diff --git a/Java/Exclusive Time of Functions.java b/Java/Exclusive Time of Functions.java new file mode 100644 index 0000000..d175a09 --- /dev/null +++ b/Java/Exclusive Time of Functions.java @@ -0,0 +1,92 @@ +M +1531813417 +tags: Stack + +#### Stack +- 1. later function always appears after prior fn: 1 is called by 0 +- 2. `Not mentione in the question`: a function can be started multiple times +- 3. `Not mentione in the question`: a fn cannot start if children fn starts +- 4. Use stack to keep id +- TODO: what leads to the choice of stack? stacking fn id + +``` +/* +Given the running logs of n functions that are executed in a nonpreemptive single threaded CPU, +find the exclusive time of these functions. + +Each function has a unique id, start from 0 to n-1. A function may be called recursively or by another function. + +A log is a string has this format : function_id:start_or_end:timestamp. +For example, "0:start:0" means function 0 starts from the very beginning of time 0. +"0:end:0" means function 0 ends to the very end of time 0. + +Exclusive time of a function is defined as the time spent within this function, +the time spent by calling other functions should not be considered as this function's exclusive time. +You should return the exclusive time of each function sorted by their function id. + +Example 1: +Input: +n = 2 +logs = +["0:start:0", + "1:start:2", + "1:end:5", + "0:end:6"] +Output:[3, 4] +Explanation: +Function 0 starts at time 0, then it executes 2 units of time and reaches the end of time 1. +Now function 0 calls function 1, function 1 starts at time 2, executes 4 units of time and end at time 5. +Function 0 is running again at time 6, and also end at the time 6, thus executes 1 unit of time. +So function 0 totally execute 2 + 1 = 3 units of time, and function 1 totally execute 4 units of time. +Note: +Input logs will be sorted by timestamp, NOT log id. +Your output should be sorted by function id, which means +the 0th element of your output corresponds to the exclusive time of function 0. +Two functions won't start or end at the same time. +Functions could be called recursively, and will always end. +1 <= n <= 100 +*/ + +// Stack +class Solution { + class Record { + int id, time; + boolean start; + public Record (int id, boolean start, int time) { + this.id = id; + this.start = start; + this.time = time; + } + } + + public int[] exclusiveTime(int n, List logs) { + if (validate(n, logs)) return new int[]{}; + + // use int[] as data structure to track time of fn i + int[] rst = new int[n]; + Stack stack = new Stack<>(); + int prevTime = 0; + for (String log : logs) { + Record record = parse(log); + if (!stack.isEmpty()) rst[stack.peek()] += record.time - prevTime; + prevTime = record.time; + if (record.start) { + stack.push(record.id); + } else { + rst[stack.pop()]++; + prevTime++; + } + } + return rst; + } + + private boolean validate(int n, List logs) { + return n <= 0 || logs == null || logs.size() == 0; + } + + private Record parse(String s) { + String[] ss = s.split(":"); + return new Record(Integer.parseInt(ss[0]), ss[1].equals("start"), Integer.parseInt(ss[2])); + } +} +``` \ No newline at end of file diff --git a/Java/Expression Add Operators.java b/Java/Expression Add Operators.java new file mode 100644 index 0000000..143f01b --- /dev/null +++ b/Java/Expression Add Operators.java @@ -0,0 +1,181 @@ +H +1528348171 +tags: String, Divide and Conquer, DFS, Backtracking + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + +``` +/* + +Given a string that contains only digits 0-9 and a target value, +return all possibilities to add binary operators (not unary) +, -, or * +between the digits so they evaluate to the target value. + +Example 1: + +Input: num = "123", target = 6 +Output: ["1+2+3", "1*2*3"] +Example 2: + +Input: num = "232", target = 8 +Output: ["2*3+2", "2+3*2"] +Example 3: + +Input: num = "105", target = 5 +Output: ["1*0+5","10-5"] +Example 4: + +Input: num = "00", target = 0 +Output: ["0+0", "0-0", "0*0"] +Example 5: + +Input: num = "3456237490", target = 9191 +Output: [] + +*/ + +/* +Thoughts:: +- parse string, dfs on string index +- DFS: Choose integer as individual, or combo: [1,2,3], [12,3] +- Each choice launch 3 dfs : [+, -, *] +- handle * : dfs must pass in currNum and nextNumber for * to work. +- edge case: '0 + 00' +*/ +/* +Thoughts:: +- parse string, dfs on string index +- DFS: Choose integer as individual, or combo: [1,2,3], [12,3] +- Each choice launch 3 dfs : [+, -, *] +- handle * : dfs must pass in currNum and nextNumber for * to work. +- edge case: '0 + 00' +*/ +class Solution { + public List addOperators(String num, int target) { + List rst = new ArrayList<>(); + // edge case + if (num == null || num.length() == 0) { + return rst; + } + + // dfs1 from index = 0, sum = 0, preProduct = 0, + dfs(rst, new ArrayList<>(), num, 0, 0, 0, target); + return rst; + } + + private void dfs(List rst, List list, String s, + int index, long sum, long preProduct, int target) { + if (index >= s.length()) { + if (sum == target) { + StringBuffer sb = new StringBuffer(); + for (String part : list) { + sb.append(part); + } + rst.add(sb.toString()); + } + return; + } + + // for loop from index -> end + for (int i = index; i < s.length(); i++) { + String curr = s.substring(index, i + 1); + long currValue = Long.parseLong(curr); + if (s.charAt(index) == '0' && i > index) { // filter case: '01' + continue; + } + + if (index == 0) { + list.add(curr); + dfs(rst, list, s, i + 1, currValue, currValue, target); + list.remove(list.size() - 1); + continue; + } + + list.add("+"); + list.add(curr); + dfs(rst, list, s, i + 1, sum + currValue, currValue, target); + backtrack(list); + + list.add("-"); + list.add(curr); + dfs(rst, list, s, i + 1, sum - currValue, - currValue, target); + backtrack(list); + + list.add("*"); + list.add(curr); + long product = preProduct * currValue; + dfs(rst, list, s, i + 1, sum - preProduct + product, product, target); + backtrack(list); + } + } + + private void backtrack(List list) { + list.remove(list.size() - 1); + list.remove(list.size() - 1); + } +} + + +// Append string +class Solution { + public List addOperators(String num, int target) { + List rst = new ArrayList<>(); + // edge case + if (num == null || num.length() == 0) { + return rst; + } + + // dfs from index = 0, sum = 0, productFactor = 0, + dfs(rst, num, "", 0, 0, 0, target); + return rst; + } + + private void dfs(List rst, String s, String buffer, + int index, long sum, long productFactor, int target) { + if (index >= s.length()) { + if (sum == target) { + rst.add(buffer); + } + return; + } + + // for loop from index -> end + for (int i = index; i < s.length(); i++) { + String curr = s.substring(index, i + 1); + long currValue = Long.parseLong(curr); + if (s.charAt(index) == '0' && i > index) { // filter case: '01' + continue; + } + + if (index == 0) { + dfs(rst, s, curr, i + 1, currValue, currValue, target); + continue; + } + + dfs(rst, s, buffer + "+" + curr, i + 1, sum + currValue, currValue, target); + dfs(rst, s, buffer + "-" + curr, i + 1, sum - currValue, - currValue, target); + long product = productFactor * currValue; + dfs(rst, s, buffer + "*" + curr, i + 1, sum - productFactor + product, product, target); + } + } +} + +``` \ No newline at end of file diff --git a/Java/Expression Evaluation.java b/Java/Expression Evaluation.java old mode 100644 new mode 100755 index 47df6b5..adb5228 --- a/Java/Expression Evaluation.java +++ b/Java/Expression Evaluation.java @@ -1,10 +1,21 @@ -Build Expression Tree的另外一个变形。 -做的还是PostTraversal。先eval left, right, 然后eval符号。 +H +1524550668 +tags: Stack, Binary Tree, DFS, Expression Tree, Minimum Binary Tree + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. -注意Handle数字时,若左右Child全Null,那必定是我们weight最大的数字node了。 -若有个child是null,那就return另外一个node。 -还要注意: -过程中用个Long吧,最后结局在cast back to int. ``` /* Given an expression string array, return the final result of this expression diff --git a/Java/Expression Tree Build.java b/Java/Expression Tree Build.java old mode 100644 new mode 100755 index 795d3be..2a1c9eb --- a/Java/Expression Tree Build.java +++ b/Java/Expression Tree Build.java @@ -1,9 +1,70 @@ -和Max-tree一样,感谢http://blog.welkinlan.com/2015/06/29/max-tree-lintcode-java/ -这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙。 -注意虚拟的形态:treeNode,作用就是为了有个weight,好排序。 -要想想,Java这个strict mom,如果换做JavaScript, 直接在expressionTreeNode上面附加一个object就完了,哪还用什么另外一个TreeNode class. -O(n) +H +1524548535 +tags: Stack, Binary Tree, Expression Tree, Minimum Binary Tree + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + ``` +/* + +The structure of Expression Tree is a binary tree to evaluate certain expressions. +All leaves of the Expression Tree have an number string value. +All non-leaves of the Expression Tree have an operator string value. + +Now, given an expression array, build the expression tree of this expression, +return the root of this expression tree. + +Example +For the expression (2*6-(23+7)/(1+2)) +which can be represented by ["2" "*" "6" "-" "(" "23" "+" "7" ")" "/" "(" "1" "+" "2" ")"]. +The expression tree will be like + + [ - ] + / \ + [ * ] [ / ] + / \ / \ + [ 2 ] [ 6 ] [ + ] [ + ] + / \ / \ + [ 23 ][ 7 ] [ 1 ] [ 2 ] . +After building the tree, you just need to return root node [-]. + +Clarification +See wiki: +Expression Tree + +Tags Expand +LintCode Copyright Stack Binary Tree + + +*/ + +/** + * Definition of ExpressionTreeNode: + * public class ExpressionTreeNode { + * public String symbol; + * public ExpressionTreeNode left, right; + * public ExpressionTreeNode(String symbol) { + * this.symbol = symbol; + * this.left = this.right = null; + * } + * } + */ + public class Solution { class TreeNode { int val; @@ -27,7 +88,7 @@ public ExpressionTreeNode build(String[] expression) { for (int i = 0; i < expression.length; i++) { if (expression[i].equals("(")) { - base += 10; + base += 10 ; continue; } if (expression[i].equals(")")) { @@ -36,6 +97,8 @@ public ExpressionTreeNode build(String[] expression) { } val = getWeight(base, expression[i]); TreeNode node = new TreeNode(val, expression[i]); + + // Monotonous stack: building minimum binary tree while (!stack.isEmpty() && node.val <= stack.peek().val) { node.eNode.left = stack.pop().eNode; } @@ -47,6 +110,8 @@ public ExpressionTreeNode build(String[] expression) { if (stack.isEmpty()) { return null; } + + // Find the root, which will be the minimum value TreeNode rst = stack.pop(); while (!stack.isEmpty()) { rst = stack.pop(); diff --git a/Java/Fast Power.java b/Java/Fast Power.java old mode 100644 new mode 100755 index dca7b79..28ceca6 --- a/Java/Fast Power.java +++ b/Java/Fast Power.java @@ -1,3 +1,17 @@ +M +1527969371 +tags: DFS, Divide and Conquer + +如题: Calculate the a^n % b where a, b and n are all 32bit integers. + +#### Divide and Conquer +- a^n可以被拆解成(a*a*a*a....*a), 是乘机形式,而%是可以把每一项都mod一下的。所以就拆开来take mod. +- 这里用个二分的方法,recursively二分下去,直到n/2为0或者1,然后分别对待. +- 注意1: 二分后要conquer,乘积可能大于Integer.MAX_VALUE, 所以用个long. +- 注意2: 要处理n%2==1的情况,二分时候自动省掉了一份 a,要乘一下。 + + +``` /* Calculate the a^n % b where a, b and n are all 32bit integers. @@ -12,6 +26,10 @@ Tags Expand Divide and Conquer + +*/ + +/* Thoughts: Learn online: (a * b) % p = (a % p * b % p) % p @@ -19,27 +37,24 @@ Note: when n is odd number, it cannot be evenly divided into n/2 and n/2. This case needs special treatment: n = n/2 + n/2 + 1; */ - class Solution { - /* - * @param a, b, n: 32bit integers - * @return: An integer - */ public int fastPower(int a, int b, int n) { - if (n == 0) { - return 1 % b; - } - if (n == 1) { - return a % b; - } + if (n == 0) { + return 1 % b; + } + if (n == 1) { + return a % b; + } - long recurPow = fastPower(a, b, n / 2); - recurPow = (recurPow * recurPow) % b; + long recurPow = fastPower(a, b, n / 2); + recurPow = (recurPow * recurPow) % b; - if (n % 2 == 1) { - recurPow = recurPow * a % b; - } + if (n % 2 == 1) { + recurPow = recurPow * a % b; + } - return (int)recurPow; + return (int)recurPow; } }; + +``` \ No newline at end of file diff --git a/Java/Fibonacci.java b/Java/Fibonacci.java old mode 100644 new mode 100755 index adf73eb..18c1f40 --- a/Java/Fibonacci.java +++ b/Java/Fibonacci.java @@ -1,3 +1,18 @@ +E +1525416805 +tags: Math, DP, Memoization + +#### Memoization +- fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + +#### DP array. +- 滚动数组, 简化DP + +#### recursively calculate +- recursively calculate fib(n - 1) + fib(n - 2). 公式没问题, 但是时间太长, timeout. + + +``` /* Find the Nth number in Fibonacci sequence. @@ -23,27 +38,84 @@ Tags Expand Enumeration Mathematics Non Recursion + +*/ + +// Memoization +class Solution { + int[] fib = null; + public int fibonacci(int n) { + if (fib == null) { + fib = new int[n + 1]; + } + if (n <= 2) { + return n - 1; + } + if (fib[n] != 0) { + return fib[n]; + } + return fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + } +} + +/* + Recap 3.28.2016. + Rolling array, instead of initiating array. +*/ +class Solution { + public int fibonacci(int n) { + if (n <= 1) { + return 0; + } + int first = 0; + int second = 1; + for (int i = 2; i < n; i++) { + int temp = second; + second = first + second; + first = temp; + } + return second; + } +} + + +/* Thoughts: +Bottomup with for loop. 1. If non-recursion, do for loop for that n 2. Note: this specfiic problem is not 0-based. it's 1-based. 3. return fib[n] */ - class Solution { - /** - * @param n: an integer - * @return an integer f(n) - */ public int fibonacci(int n) { if (n <= 1) { - return 0; + return 0; + } + int[] fib = new int[n]; + fib[0] = 0; + fib[1] = 1; + for (int i = 2; i < n; i++) { + fib[i] = fib[i - 1] + fib[i - 2]; } - int[] fib = new int[n + 1]; - fib[1] = 0; - fib[2] = 1; - for (int i = 3; i <= n; i++) { - fib[i] = fib[i - 1] + fib[i - 2]; + return fib[n - 1]; + } +} + + +/* + Recursive. Long time complexity + Timeout +*/ +class Solution { + public int fibonacci(int n) { + if (n <= 1) { + return 0; + } + if (n == 2) { + return 1; } - return fib[n]; + return fibonacci(n - 1) + fibonacci(n - 2); } } + +``` \ No newline at end of file diff --git a/Java/Find All Anagrams in a String.java b/Java/Find All Anagrams in a String.java new file mode 100644 index 0000000..9f49410 --- /dev/null +++ b/Java/Find All Anagrams in a String.java @@ -0,0 +1,143 @@ +E +1524100532 +tags: Hash Table, Sliding Window + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### HashTable +- count character apperance 就想到hashtable +- 注意countS, countP 的技巧: 作比较只需要O(26) +- Overall timeO(n) +- 小心不要用一个int[] count 来技术 查0, 复杂度是O(n) + +``` +/** +Given a string s and a non-empty string p, find all the start indices of p's anagrams in s. + +Strings consists of lowercase English letters only and the length of both strings s and p will not be larger than 20,100. + +The order of output does not matter. + +Example 1: + +Input: +s: "cbaebabacd" p: "abc" + +Output: +[0, 6] + +Explanation: +The substring with start index = 0 is "cba", which is an anagram of "abc". +The substring with start index = 6 is "bac", which is an anagram of "abc". +Example 2: + +Input: +s: "abab" p: "ab" + +Output: +[0, 1, 2] + +Explanation: +The substring with start index = 0 is "ab", which is an anagram of "ab". +The substring with start index = 1 is "ba", which is an anagram of "ab". +The substring with start index = 2 is "ab", which is an anagram of "ab". +*/ +/* +Thoughts: +1. Two pointers with range of p.length(). O(n) +2. Use counter to count the character apperance in s and p. +3. For s, when moving the pointer, always -- on past index, and ++ on new index +4. Compare countS, countP => O(26) +Overall O(n) +*/ +class Solution { + public List findAnagrams(String s, String p) { + List rst = new ArrayList<>(); + if (s == null || s.length() == 0 || s.length() < p.length()) { + return rst; + } + + int n = p.length(); + int[] countS = new int[26]; + int[] countP = new int[26]; + for (int i = 0; i < n; i++) { + countS[s.charAt(i) - 'a']++; + countP[p.charAt(i) - 'a']++; + } + + if (compare(countS, countP)) { + rst.add(0); + } + + for (int i = n; i < s.length(); i++) { + countS[s.charAt(i) - 'a']++; + countS[s.charAt(i - n) - 'a']--; + if (compare(countS, countP)) { + rst.add(i - n + 1); + } + } + + return rst; + } + + private boolean compare(int[] countS, int[] countP) { + for (int i = 0; i < 26; i++) { + if (countS[i] != countP[i]) { + return false; + } + } + return true; + } +} + +/* +Thoughts: +1. Two pointers with range of p.length(). O(n) +2. isAnagram(a, b). O(n) +=> Overall O(n^2) +*/ +class Solution { + public List findAnagrams(String s, String p) { + List rst = new ArrayList<>(); + if (s == null || s.length() == 0 || s.length() < p.length()) { + return rst; + } + if (s.length() == p.length() && isAnagram(s, p)) { + rst.add(0); + return rst; + } + + int i = 0; + int j = p.length(); + while (j <= s.length()) { + if (isAnagram(s.substring(i, j), p)) { + rst.add(i); + } + i++; + j++; + } + + return rst; + } + + private boolean isAnagram(String a, String b) { + int[] arr = new int[26]; + for (int i = 0; i < a.length(); i++) { + arr[a.charAt(i) - 'a']++; + arr[b.charAt(i) - 'a']--; + } + for (int i = 0; i < 26; i++) { + if (arr[i] != 0) { + return false; + } + } + return true; + } +} + + + + +``` \ No newline at end of file diff --git a/Java/Find All Numbers Disappeared in an Array.java b/Java/Find All Numbers Disappeared in an Array.java new file mode 100644 index 0000000..47438cc --- /dev/null +++ b/Java/Find All Numbers Disappeared in an Array.java @@ -0,0 +1,141 @@ +E +1517550043 +tags: Array + +方法1: +换到正确的位置。 +需要小心handle i, 因为不知道换到nums[i]上的是什么,所以必须原地清理干净 方能move on. + +方法2: +做标记! +很巧妙地运用了标记的方法, 标记成负数,证明visit过。 +Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! + +方法3: +做标记! +跟方法2类似,也是做标记,这一次是加上一个大于n的数(因为题目给了n的border),最后check一下就好。为不超Integer.MAX_VALUE, 每次加n前,取余数。 + +做标记的方法固然快,但是相对来说比较hacky,在常规的代码中,估计不会用到. + + +``` +/* +Given an array of integers where 1 ≤ a[i] ≤ n (n = size of array), some elements appear twice and others appear once. + +Find all the elements of [1, n] inclusive that do not appear in this array. + +Could you do it without extra space and in O(n) runtime? You may assume the returned list does not count as extra space. + +Example: + +Input: +[4,3,2,7,8,2,3,1] + +Output: +[5,6] +*/ + +/* +Thought1: +Move it to where it should be at. +Important: if we keep traverse for loop, once it swap once and i + 1, the nums[i] may have a incorrect value. +Therefore, the solution should handle (i - 1) after each swap, until there is no swap to do on nums[i]. +In the background, the rumtime may accumulate up to 2n: n for all sways on index i = 0; and another n for the rest traverse. Overall, it's still O(n) + +*/ +class Solution { + public List findDisappearedNumbers(int[] nums) { + final List resultList = new ArrayList(); + if (nums == null || nums.length == 0) { + return resultList; + } + for (int i = 0; i < nums.length; i++) { + int desiredIndex = nums[i] - 1; + if (nums[desiredIndex] != nums[i]) { + int temp = nums[desiredIndex]; + nums[desiredIndex] = nums[i]; + nums[i] = temp; + i--; + } + } + + for (int i = 0; i < nums.length; i++) { + if (nums[i] != i + 1) { + resultList.add(i + 1); + } + } + return resultList; + } +} + +/* +Thougths: +1. traverse nums:switch each number to its correct location, and save existing number into list. +2. traverse list: treat the numbers as index, and save them back to nums +3. traverse nums again, some indexed will stil be incorrectly assigned, and they will be the missing values. +note: number 1 <-> index 0 +*/ +class Solution { + public List findDisappearedNumbers(int[] nums) { + final List list = new ArrayList<>(); + if (nums == null) { + return list; + } + // switch + for (int i = 0; i < nums.length; i++) { + if (nums[i] != i + 1) { + int correctIndex = nums[i] - 1; + int temp = nums[correctIndex]; + nums[correctIndex] = nums[i]; + list.add(temp); + } + } + // fill the rest + while (list.size() != 0) { + nums[list.get(0) - 1] = list.get(0); + list.remove(0); + } + + // validate + for (int i = 0; i < nums.length; i++) { + if (nums[i] != i + 1) { + list.add(i + 1); + } + } + return list; + } +} + + +/* +Thought2: +1. We want mark the num at it's desired index: num - 1. +2. The non-marked positions are missing the desired value: index + 1. + +One approach is to sort the the items into correct position and remove redundant, but that'll take nLogN. So, don't sort. + +A better approach is to mark the desired position to negative. +Note, we can't just set to -1, because the desired position might be further down in the traversed array, where we'd need its abs value again. +*/ +class Solution { + public List findDisappearedNumbers(int[] nums) { + final List resultList = new ArrayList(); + if (nums == null || nums.length == 0) { + return resultList; + } + + for (int i = 0; i < nums.length; i++) { + int desiredIndex = Math.abs(nums[i]) - 1; + nums[desiredIndex] = nums[desiredIndex] < 0 ? nums[desiredIndex] : -nums[desiredIndex]; + } + + for (int i = 0; i < nums.length; i++) { + if (nums[i] > 0) { + resultList.add(i + 1); + } + } + + return resultList; + } +} +``` \ No newline at end of file diff --git a/Java/Find Anagram Mappings.java b/Java/Find Anagram Mappings.java new file mode 100644 index 0000000..07b3357 --- /dev/null +++ b/Java/Find Anagram Mappings.java @@ -0,0 +1,67 @@ +E +1516255454 +tags: Hash Table + +比较简单, 用HashMap 存index list. 最后再遍历一遍数组A, 列举出所有元素. +O(n) + +``` +/* +Given two lists Aand B, and B is an anagram of A. B is an anagram of A means B is made by randomizing the order of the elements in A. + +We want to find an index mapping P, from A to B. A mapping P[i] = j means the ith element in A appears in B at index j. + +These lists A and B may contain duplicates. If there are multiple answers, output any of them. + +For example, given + +A = [12, 28, 46, 32, 50] +B = [50, 12, 32, 46, 28] +We should return +[1, 4, 3, 2, 0] +as P[0] = 1 because the 0th element of A appears at B[1], and P[1] = 4 because the 1st element of A appears at B[4], and so on. +Note: + +A, B have equal lengths in range [1, 100]. +A[i], B[i] are integers in range [0, 10^5]. + +*/ + +/* +Thoughts: +Brutle way: O(n^2) + +HashMap && O(n) solution +Loop over A to put all elements in map as key and list of indexs as value. +Loop over B to and pick up the item from map, and put B's index in the value list. +Loop over A again and use the map.value to build result. Note, we should not have to worry about outbound since A & B are anagram +*/ +class Solution { + public int[] anagramMappings(int[] A, int[] B) { + if (A == null || B == null || A.length == 0) { + return null; + } + + final int[] result = new int[A.length]; + final Map> map = new HashMap<>(); + + // Prepare the keys + for (int element : A) { + if (!map.containsKey(element)) { + map.put(element, new ArrayList()); + } + } + // Prepare the indexes + for (int i = 0; i < B.length; i++) { + map.get(B[i]).add(i); + } + + for (int i = 0; i < A.length; i++) { + final ArrayList list = map.get(A[i]); + result[i] = list.get(0); + list.remove(0); + } + return result; + } +} +``` \ No newline at end of file diff --git a/Java/Find Median from Data Stream.java b/Java/Find Median from Data Stream.java new file mode 100755 index 0000000..9940598 --- /dev/null +++ b/Java/Find Median from Data Stream.java @@ -0,0 +1,151 @@ +H +1533183415 +tags: Heap, Design, MinHeap, MaxHeap + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + +``` +/* +Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value. + +Examples: +[2,3,4] , the median is 3 + +[2,3], the median is (2 + 3) / 2 = 2.5 + +Design a data structure that supports the following two operations: + +void addNum(int num) - Add a integer number from the data stream to the data structure. +double findMedian() - Return the median of all elements so far. +For example: + +addNum(1) +addNum(2) +findMedian() -> 1.5 +addNum(3) +findMedian() -> 2 + */ + /* +Thoughts: +Median value needs to be maintained as we add new items into the structure. +MinHeap, MaxHeap. Maintain it during add. +*/ +class MedianFinder { + PriorityQueue minHeap;// later + PriorityQueue maxHeap;// former + /** initialize your data structure here. */ + public MedianFinder() { + minHeap = new PriorityQueue(); + maxHeap = new PriorityQueue(10, Collections.reverseOrder()); + } + + public void addNum(int num) { + // maxHeap.size() = minHeap.size() + 1 + if (maxHeap.isEmpty()) { + maxHeap.add(num); + return; + } + int currMedian = maxHeap.peek(); + if (num <= currMedian) { + maxHeap.offer(num); + } else { + minHeap.offer(num); + } + // make sure: maxHeap.size() = minHeap.size() + 1 + if (maxHeap.size() > minHeap.size() + 1) { + minHeap.offer(maxHeap.poll()); + } else if (maxHeap.size() < minHeap.size()) { + maxHeap.offer(minHeap.poll()); + } + } + + public double findMedian() { + if (minHeap.size() == maxHeap.size()) { + return (minHeap.peek() + maxHeap.peek()) / 2.0; + } else { + return maxHeap.peek(); + } + } +} + +/** + * Your MedianFinder object will be instantiated and called as such: + * MedianFinder obj = new MedianFinder(); + * obj.addNum(num); + * double param_2 = obj.findMedian(); + */ + + + +/* +Numbers keep coming, return the median of numbers at every time a new number added. + +Example +For numbers coming list: [1, 2, 3, 4, 5], return [1, 1, 2, 2, 3]. + +For numbers coming list: [4, 5, 1, 3, 2, 6, 0], return [4, 4, 4, 3, 3, 3, 3]. + +For numbers coming list: [2, 20, 100], return [2, 2, 20]. + +Challenge +Total run time in O(nlogn). + +Clarification +What's the definition of Median? - Median is the number that in the middle of a sorted array. +If there are n numbers in a sorted array A, the median is A[(n - 1) / 2]. +For example, if A=[1,2,3], median is 2. If A=[1,19], median is 1. + +Tags Expand +LintCode Copyright Heap Priority Queue +*/ + + + +public class Solution { + /** + * @param nums: A list of integers. + * @return: the median of numbers + */ + public int[] medianII(int[] nums) { + int[] rst = new int[nums.length]; + if (nums == null || nums.length == 0) { + return rst; + } + + PriorityQueue minHeap = new PriorityQueue(); + PriorityQueue maxHeap = new PriorityQueue(10, new Comparator() { + public int compare(Integer x, Integer y) { + return y - x; + } + }); + + rst[0] = nums[0]; + maxHeap.offer(rst[0]); + + for (int i = 1; i < rst.length; i++){ + int preMedian = maxHeap.peek(); + if (nums[i] > preMedian) { + minHeap.offer(nums[i]); + } else { + maxHeap.offer(nums[i]); + } + + if (maxHeap.size() > minHeap.size() + 1) { + minHeap.offer(maxHeap.poll()); + } else if (maxHeap.size() < minHeap.size()) { + maxHeap.offer(minHeap.poll()); + } + rst[i] = maxHeap.peek(); + } + return rst; + } +} + +``` \ No newline at end of file diff --git a/Java/Find Minimum in Rotated Sorted Array II.java b/Java/Find Minimum in Rotated Sorted Array II.java old mode 100644 new mode 100755 index 83aa7c8..446cceb --- a/Java/Find Minimum in Rotated Sorted Array II.java +++ b/Java/Find Minimum in Rotated Sorted Array II.java @@ -1,3 +1,59 @@ +H +1520311890 +tags: Array, Binary Search + +一个需要严谨思考的题目. 因为有duplicate, 会导致不断平移, 所以最终time complexity是O(n) +所以不如直接扫一遍, 给出答案. + +但是还是写一个Binary Search的样子, 只不过worst结果是O(n) + +``` +/* +Follow up for "Find Minimum in Rotated Sorted Array": +What if duplicates are allowed? + +Would this affect the run-time complexity? How and why? + +Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. + +(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). + +Find the minimum element. + +The array may contain duplicates. + + */ +/* +Thoughts: +Draw the graph, almost same with I. +If mid lands on duplicate && nums[mid]==nums[start], start++ until it does not equal. +Same for end, end-- untill it does not equal. +However: +Due to duplicates, it's possible for entire row to be same [2,2,2,2,2,2...2,2], which means the worst case is O(n) +Minght as well just find the min with O(n) +*/ +class Solution { + public int findMin(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int start = 0; + int end = n - 1; + while (start + 1 < end) { + int mid = (start + end) >> 1; + if (nums[mid] == nums[end]) { + end--; + } else if (nums[mid] > nums[end]) { + start = mid; + } else { + end = mid; + } + } + return nums[start] < nums[end] ? nums[start] : nums[end]; + } +} + /* Medium Find Minimum in Rotated Sorted Array II My Submissions @@ -15,11 +71,13 @@ Tags Expand Binary Search Divide and Conqueri +*/ +/* +Previous notes Thinking process: It seems using binary search will leads to O(n), so just use a for loop with O(n) */ - public class Solution { /** * @param num: a rotated sorted array @@ -39,3 +97,5 @@ public int findMin(int[] num) { } } + +``` \ No newline at end of file diff --git a/Java/Find Minimum in Rotated Sorted Array.java b/Java/Find Minimum in Rotated Sorted Array.java old mode 100644 new mode 100755 index a15ba64..a86abcc --- a/Java/Find Minimum in Rotated Sorted Array.java +++ b/Java/Find Minimum in Rotated Sorted Array.java @@ -1,3 +1,14 @@ +M +1520310613 +tags: Array, Binary Search + +画图, 最小值被rotate之后, 变成array中间的最低谷. +并且, 左边山坡的最小值, 大于右边山坡的最大值. +以此来给binary search做判断. + +O(nlogn) + +``` /* Suppose a sorted array is rotated at some pivot unknown to you beforehand. @@ -13,6 +24,39 @@ Tags Expand Binary Search +*/ + +/* +Thoughts: +O(n) could find it. To do it better, try binary search. +Find the mid point where nums[mid-1]>nums[mid] && nums[mid] nums[end] with given condition +*/ +class Solution { + public int findMin(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int start = 0; + int end = n - 1; + while (start + 1 < end) { + int mid = (start + end) >> 1; + if (mid - 1 >= 0 && mid + 1 < n && nums[mid - 1] > nums[mid] && nums[mid] < nums[mid + 1]) { + return nums[mid]; + } else if (nums[mid] > nums[end]) { // nums[end] is definitely < nums[start]; greater than nums[end]: at left slope + start = mid; + } else {// nums[mid] < nums[start] + end = mid; + } + } + return nums[start] < nums[end] ? nums[start] : nums[end]; + } +} + +/* +Previous notes Thinking process: Understand how to use binary in this problem: compare the mid point with end point. In this problem, because the sorted line is cut at one point then rotate, so one of the line is absolutely greater than the other line. @@ -20,8 +64,7 @@ if mid < end : that means minimum is on the end point's line. Move end to left. end = mid. Situation 2: if mid > end: that means there must be a mountain-jump somewhere after mid and before end, which is the minimum point. Now move start to mid. -*/ - + */ public class Solution { /** * @param num: a rotated sorted array @@ -50,3 +93,5 @@ public int findMin(int[] num) { } } + +``` \ No newline at end of file diff --git a/Java/Find Peak Element II.java b/Java/Find Peak Element II.java index 1629c93..f981ce5 100644 --- a/Java/Find Peak Element II.java +++ b/Java/Find Peak Element II.java @@ -1,3 +1,40 @@ +H +1520916753 +tags: Divide and Conquer, DFS, Binary Search + +2Dmatrix, 里面的value有一些递增, 递减的特点(细节比较长, 看原题). 目标是找到peak element + +peak: 比周围4个方向的点value大 + +#### DFS + +##### 基本原理 +- 我们不可能一口气准确定位(x,y), 但是我们可以再一个row/col里面, 找到1D array的 peak. +- 根据这个点, 再往剩下两个方向移动 +- 1. 在中间的一行i=midX, 找到peak所在的y. +- 2. 在中间的一列j=midY, 找到peak所在的x. (有可能强势override之前找到的y, 也就是放弃那一行的peak, 在midY上找peak) +- 3. 根据 (x,y) 的4个neighbor check (x,y)是不是 peak, 如果不是, 像更高的位置移动一格 +- 4. 根据之前算的 midX, midY 把board分成4个象限, 在每一份里面再继续找 +- 这个题目LintCode不给做了, 所以思路对的, 但是解答还没有再次验证. + +##### 剪枝/切分象限 +- 每次只是找到一个row/col里面的peak而已! +- 找到这个点, 就等于把board切成了两半. +- 然后, 再跟剩下的相邻的两个位置比较, 就知道了哪里更大, 就去哪里找peak, 也就是又切了第二刀. +- 切第二刀的时候, 也要把(x, y) 移到需要取的象限. 进行DFS +- 根据mid row 切割: +- http://www.jiuzhang.com/solution/find-peak-element-ii/#tag-highlight-lang-java +- http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf + +##### 时间复杂度 +- 每一个level都减一半 +- T(n) = n + T(n/2) = n + n/2 + n/4 + ... + 1 = n(1 + 1/2 + .... + 1/n) = 2n = O(n) + +#### Binary Search +- TODO +- O(nLogN) + +``` /* There is an integer matrix which has the following features: @@ -29,24 +66,170 @@ Challenge Solve it in O(n+m) time. -If you come up with an algorithm that you thought it is O(n log m) or O(m log n), can you prove it is actually O(n+m) or propose a similar but O(n+m) algorithm? +If you come up with an algorithm that you thought it is O(n log m) or O(m log n), +can you prove it is actually O(n+m) or propose a similar but O(n+m) algorithm? Tags Expand Binary Search LintCode Copyright Matrix */ -/* - NOT DONE. Will try if have time -*/ +// 简化版 +class Solution { + public List findPeakII(int[][] A) { + List rst = new ArrayList<>(); + if (A == null || A.length == 0 || A[0] == null || A[0].length == 0) { + return rst; + } + int m = A.length; + int n = A[0].length; + rst = find(1, m - 2, 1, n - 2, A); + + return rst; + } + + private List find(int x1, int x2, int y1, int y2, int[][] A) { + // Given coordinate boundary, find center point (midX, midY) + int midX = x1 + ((x2 - x1) >> 1); // or just (x2 - x1)/2 + int midY = y1 + ((y2 - y1) >> 1); + int x = midX, y = midY; // 从中间点开始寻找 x,y + int max = A[x][y]; + // With i = midX, find peak on the center row. + // 固定y: 这里顾不到curr row上下的大小 + for (int j = y1; j <= y2; j++) { + if (A[midX][j] > max) { + max = A[midX][j]; + x = midX; + y = j; + } + } + // With j = midY, find peak on the center col. + // 固定x: 这里顾不到curr col左右的大小 + // 这里如果找到A[i][midY]大于max, 强行override 之前写到的y. + // 放弃找之前的peak, 找这个新peak, 肯定找的到. + for (int i = x1; i <= x2; i++) { + if (A[i][midY] > max) { + max = A[i][midY]; + y = midY; + x = i; + } + } + + // If (x,y) not at the peak, move towards the peak for 1 step, then DFS + // 剪枝/矫正. + if (A[x - 1][y] > max) { // UP-LEFT + return find(x1, midX - 1, y1, y2, A); + } else if (A[x + 1][y] > max) { // UP-RIGHT + return find(x1 + 1, midX, y1, y2, A); + } else if (A[x][y - 1] > max) { // DOWN-LEFT + return find(x1, x2, y1, midY - 1, A); + } else if (A[x][y + 1] > max) { // DOWN-RIGHT + return find(x1, x2, midY + 1, y2, A); + } + + return new ArrayList<>(Arrays.asList(x, y)); + } +} + +/* +Not on LeetCode. +*/ class Solution { /** * @param A: An integer matrix * @return: The index of the peak */ public List findPeakII(int[][] A) { - // write your code here + List rst = new ArrayList<>(); + if (A == null || A.length == 0 || A[0] == null || A[0].length == 0) { + return rst; + } + int m = A.length; + int n = A[0].length; + rst = find(1, m - 2, 1, n - 2, A); + + return rst; + } + + private List find(int x1, int x2, int y1, int y2, int[][] A) { + // Given coordinate boundary, find center point (midX, midY) + int midX = x1 + ((x2 - x1) >> 1); // or just (x2 - x1)/2 + int midY = y1 + ((y2 - y1) >> 1); + + int x = midX, y = midY; // 从中间点开始寻找 x,y + int max = A[x][y]; + // With i = midX, find peak on the center row. + // 固定y: 这里顾不到curr row上下的大小 + for (int j = y1; j <= y2; j++) { + if (A[midX][j] > max) { + max = A[midX][j]; + x = midX; + y = j; + } + } + + // With j = midY, find peak on the center col. + // 固定x: 这里顾不到curr col左右的大小 + // 这里如果找到A[i][midY]大于max, 强行override 之前写到的y. + // 放弃找之前的peak, 找这个新peak, 肯定找的到. + for (int i = x1; i <= x2; i++) { + if (A[i][midY] > max) { + max = A[i][midY]; + y = midY; + x = i; + } + } + + // If (x,y) not at the peak, move towards the peak for 1 step, then DFS + // 剪枝/矫正. + boolean isPeak = true; + if (A[x - 1][y] > A[x][y]) { + isPeak = false; + x -= 1; + } else if (A[x + 1][y] > A[x][y]) { + isPeak = false; + x += 1; + } else if (A[x][y - 1] > A[x][y]) { + isPeak = false; + y -= 1; + } else if (A[x][y + 1] > A[x][y]) { + isPeak = false; + y += 1; + } + + if (isPeak) { + return new ArrayList(Arrays.asList(x, y)); + } + + // Depending which quadrant (x,y) is at. DFS into one of the 4 quadrants. + // UP-LEFT + if (x >= x1 && x < midX && y >= y1 && y < midY) { + return find(x1, midX - 1, y1, midY - 1, A); + } + + // UP-RIGHT + if (x >= x1 && x < midX && y > midY && y <= y2) { + return find(x1, midX - 1, midY + 1, y2, A); + } + + // DOWN-LEFT + if (x > midX && x <= x2 && y >= y1 && y < midY) { + return find(midX + 1, x2, y1, midY - 1, A); + } + + // DOWN-RIGHT + if (x >= midX && x <= x2 && y > midY && y <= y2) { + return find(midX + 1, x2, midY + 1, y2, A); + } + + // Failed, return empty corridate. + return new ArrayList<>(); } } + + + + +``` \ No newline at end of file diff --git a/Java/Find Peak Element.java b/Java/Find Peak Element.java old mode 100644 new mode 100755 index 0717014..e450dce --- a/Java/Find Peak Element.java +++ b/Java/Find Peak Element.java @@ -1,6 +1,132 @@ -还是binary search. -一个特别的check condition, 和特别的move left, move right的case罢了。 +M +1518761203 +tags: Array, Binary Search + +binary search. +Goal: find peak, where both sides are descending +最左边, 最右边是Integer.MIN_VALUE时候, 也能构成中间数mid是peak的条件. + +Note: +没有必要特别check (mid-1)<0或者(mid+1)>=n. +证明: +1. 最左端: 当start=0, end = 2 => mid = 1, mid-1 = 0; +2. 最右端: 当end = n - 1, start = n - 3; mid = (start+end)/2 = n - 2; +那么mid + 1 = n - 2 + 1 = n - 1 < n 是理所当然的 + ``` + +/** +LeetCode +A peak element is an element that is greater than its neighbors. + +Given an input array where num[i] ≠ num[i+1], find a peak element and return its index. + +The array may contain multiple peaks, in that case return the index to any one of the peaks is fine. + +You may imagine that num[-1] = num[n] = -∞. + +For example, in array [1, 2, 3, 1], 3 is a peak element and your function should return the index number 2. + +click to show spoilers. + +Note: +Your solution should be in logarithmic complexity. + + */ + + /* +Thoughts: +Binary serach, find the one nums[mid] > nums[mid-1] && nums[mid] < nums[mid - 1] +*/ +class Solution { + public int findPeakElement(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int start = 0; + int end = n - 1; + while (start + 1 < end) { + int mid = (start + end) >> 1; + if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) { // match + return mid; + } else if (nums[mid] > nums[mid - 1]) { // ascending slope + start = mid; + } else { // descending slope + end = mid; + } + } + return nums[start] > nums[end] ? start : end; + } +} + +/* +Thoughts: +Binary serach, find the one nums[mid] > nums[mid-1] && nums[mid] < nums[mid - 1] +*/ +class Solution { + public int findPeakElement(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int start = 0; + int end = n - 1; + while (start + 1 < end) { + int mid = (start + end) >> 1; + if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) { // match + return mid; + } else if (nums[mid] > nums[mid - 1]) { // ascending slope + start = mid; + } else { // descending slope + end = mid; + } + } + return nums[start] > nums[end] ? start : end; + } +} + +/* +Thoughts: +Of course can O(n) go through all and find point B where AC. +Goal: find such point with less than O(n) => O(logn)? => binary serach + +if nums[mid - 1] < nums[mid] && nums[mid] > nums[mid + 1], return mid +That is: mid is at peak. +Note: +if mid - 1 < 0, we can say that from (mid-1) to (mid), it's ascending: potentially mid is at peak +if mid + 1 >= n, we can say that from (mid) to (mid + 1), it's ascending: potentially mid is at peak + +Binary Search: +start <= end +start = mid + 1 +end = mid - 1 +*/ +class Solution { + public int findPeakElement(int[] nums) { + if (nums == null || nums.length <= 1) { + return 0; + } + + int n = nums.length; + int start = 0; + int end = nums.length - 1; + int mid = 0; + while (start <= end) { + mid = start + (end - start) / 2; + if ((mid - 1 < 0 || nums[mid] > nums[mid - 1]) && (mid + 1 >= n || nums[mid] > nums[mid + 1])) { + return mid; + } else if (mid - 1 >= 0 && nums[mid] < nums[mid - 1]) { + end = mid - 1; + } else { + start = mid + 1; + } + } + return mid; + } +} + +// Previous notes. Previous solution is incorrect now. /*There is an integer array which has the following features: * The numbers in adjacent positions are different. diff --git a/Java/Find the Celebrity.java b/Java/Find the Celebrity.java new file mode 100644 index 0000000..d582141 --- /dev/null +++ b/Java/Find the Celebrity.java @@ -0,0 +1,67 @@ +M +1531810500 +tags: Array, Greedy +time: O(n) +space: O(1) + +有n个人, 其中有个人是celebrity, 满足条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +#### Understand the property +- If brutly find celeb by comparing all possible pair: take complete O(n^2) handshakes. +- Instead, we can perform pruning, or like survival mode: +- 1. Assume a celeb = 0, and compare with all i = [1~ n-1] +- 2. If `celeb candidate know i, set celeb = i` as the next candidate (ex: prev canddiate invalid when he knows i) +- 3. For last standing celeb candidate: compare with all for validation +- Why performing the last run of validation? There could be someone dropped out before we execute `know(celeb, i)`. + +##### 思考逻辑 +- 先写出来[0 ~ n - 1], 最简单的方式 O(n^2) 检查, 记录每个人的状态. +- 逐渐发现, 因为 celeb 谁都不会认识, 那么当任何candidate knows anyone, 他自身就不是celeb. +- 我们可以greedy地, 一旦fail一个, 就立刻假设下一个是celeb candidate +- 最终还是要检查一遍, 避免错漏. +- 想一下happy case: 如果 celeb=0, 那么 know(celeb, i) 永远都是false, 然后 celeb一直保持0, 坚持到verify所有人. + +``` +/* +Suppose you are at a party with n people (labeled from 0 to n - 1) and among them, there may exist one celebrity. +The definition of a celebrity is that all the other n - 1 people know him/her but he/she does not know any of them. + +Now you want to find out who the celebrity is or verify that there is not one. +The only thing you are allowed to do is to ask questions like: "Hi, A. Do you know B?" +to get information of whether A knows B. You need to find out the celebrity (or verify there is not one) +by asking as few questions as possible (in the asymptotic sense). + +You are given a helper function bool knows(a, b) which tells you whether A knows B. +Implement a function int findCelebrity(n), your function should minimize the number of calls to knows. + +Note: There will be exactly one celebrity if he/she is in the party. +Return the celebrity's label if there is a celebrity in the party. +If there is no celebrity, return -1. + +*/ +/* The knows API is defined in the parent class Relation. + boolean knows(int a, int b); */ + +public class Solution extends Relation { + public int findCelebrity(int n) { + if (n <= 1) return -1; + + int celeb = 0; + // pick candidate + for (int i = 0; i < n; i++) { + if (celeb != i && knows(celeb, i)) { + celeb = i; + } + } + + // final check and return + for (int i = 0; i < n; i++) { + if (celeb != i && !(knows(i, celeb) && !knows(celeb, i))) { + return -1; + } + } + + return celeb; + } +} +``` \ No newline at end of file diff --git a/Java/Find the Connected Component in the Undirected Graph.java b/Java/Find the Connected Component in the Undirected Graph.java old mode 100644 new mode 100755 index 694d25c..320a834 --- a/Java/Find the Connected Component in the Undirected Graph.java +++ b/Java/Find the Connected Component in the Undirected Graph.java @@ -1,18 +1,25 @@ -BFS遍历,把每个node的neighbor都加进来。 +M +1527969793 +tags: BFS, DFS -一定注意要把visit过的node Mark一下。因为curr node也会是别人的neighbor,会无限循环。 +给一个undirected graph, return 所有的component. (这道题找不到了) -Component的定义:所有Component内的node必须被串联起来via path (反正这里是undirected, 只要链接上就好) +#### BFS +- BFS遍历,把每个node的neighbor都加进来. +- 一定注意要把visit过的node Mark一下。因为curr node也会是别人的neighbor,会无限循环。 +- Component的定义:所有Component内的node必须被串联起来via path (反正这里是undirected, 只要链接上就好) +- 这道题:其实component在input里面都已经给好了,所有能一口气visit到的,全部加进queue里面,他们就是一个component里面的了。 +- 而我们这里不需要判断他们是不是Component -这道题:其实component在input里面都已经给好了,所有能一口气visit到的,全部加进queue里面,他们就是一个component里面的了。 +#### DFS +- DFS 应该也可以 visit all nodes, mark visited. -而我们这里不需要判断他们是不是Component。 ``` - /* +/* Find the number connected component in the undirected graph. Each node in the graph contains a label and a list of its neighbors. -(a connected component (or just component) of an undirected graph is a subgraph in which - any two vertices are connected to each other by paths, +(a connected component (or just component) of an undirected graph is a subgraph +in which any two vertices are connected to each other by paths, and which is connected to no additional vertices in the supergraph.) Example @@ -60,7 +67,7 @@ public List> connectedSet(ArrayList nodes) { return rst; } - HashMap map = new HashMap(); + HashMap map = new HashMap<>(); for (UndirectedGraphNode node : nodes) { map.put(node.label, false); } diff --git a/Java/Find the Duplicate Number.java b/Java/Find the Duplicate Number.java new file mode 100644 index 0000000..36e9db0 --- /dev/null +++ b/Java/Find the Duplicate Number.java @@ -0,0 +1,68 @@ +M +1520924985 +tags: Array, Two Pointers, Binary Search + +- 注意不要思维定式: 以为mid是index +- 这里mid其实是binary search on value [1, n] 的一个value. +- 再次用到validate() function + +Time: O(nLogN) + +``` +/* +Given an array nums containing n + 1 integers, +where each integer is between 1 and n (inclusive), +prove that at least one duplicate number must exist. +Assume that there is only one duplicate number, find the duplicate one. + +Note: +You must not modify the array (assume the array is read only). +You must use only constant, O(1) extra space. +Your runtime complexity should be less than O(n2). +There is only one duplicate number in the array, but it could be repeated more than once. +*/ + + +/* +Thoughts: +1. Cannot change array: can't sort. The array is unsorted. +2. Limited space +3. time < O(n^2), which should be O(nLogN) + +n+1 numbers, each num is in [1,n] => the extra 1 number is the duplicate. +If we count # of numbers that's <= numCandidate, the unique ones should always have count <= numCandidate +However, the duplicate will have: count >= numCandidate! + +Use this validate() function to binary search on number [1 ~ n]. +In validate function, loop over nums to count. +*/ +class Solution { + public int findDuplicate(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int start = 1; // as given, the value is in [1, n] + int end = n; + + while (start + 1 < end) { + int mid = start + (end - start) / 2; + if (validate(mid, nums)) { + start = mid; + } else { + end = mid; + } + } + + return validate(start, nums) ? end : start; + } + + private boolean validate(int candidate, int[] nums) { + int count = 0; + for (int num: nums) { + count += num <= candidate ? 1 : 0; + } + return count <= candidate; + } +} +``` \ No newline at end of file diff --git a/Java/Find the Weak Connected Component in the Directed Graph.java b/Java/Find the Weak Connected Component in the Directed Graph.java old mode 100644 new mode 100755 index 34f0384..28d0583 --- a/Java/Find the Weak Connected Component in the Directed Graph.java +++ b/Java/Find the Weak Connected Component in the Directed Graph.java @@ -1,18 +1,29 @@ -Identify这是个union-find问题还挺巧妙。 -看到了weak component的形式: 一个点指向所有,那么所有的点都有一个公共的parent,然后就是要找出这些点。 +M +1532582750 +tags: Union Find + +遍历 weak connected graph, 将结果存在 List>种. + +#### Union Find +- 跟传统的UnionFind有两点不同: +- 1. 用 Map 代替 int[], 因为没有给出 graph node label的 boundary. +- 2. find(x)时候, 没有去update `parent[x]/map.put(x, ..)`. 因为我们最终需要找到这个path. +- 无法用传统dfs: directed node 无法point到上一个点; 必须用`存parent的方式把所有node遍历掉` + +#### Identify这是个union-find问题 +- 看到了weak component的形式: 一个点指向所有,那么所有的点都有一个公共的parent,然后就是要找出这些点。 +- 为何不能从一个点出发,比如A,直接print它所有的neighbors呢: +- 如果轮到了B点,那因为是directed,它也不知道A的情况,也不知道改如何继续加,或者下手。 +- 所以,要把所有跟A有关系的点,或者接下去和A的neighbor有关系的点,都放进union-find里面,让这些点有Common parents. +- 最后output的想法: +- 做一个 map 。 +- 之前我们不是给每个num都存好了parent了嘛。 +- 每个num都有个parent, 然后不同的parent就创造一个不同的list。 +- 最后,把Map里面所有的list拿出来就好了。 -为何不能从一个点出发,比如A,直接print它所有的neighbors呢? - 不行,如果轮到了B点,那因为是directed,它也不知道A的情况,也不知道改如何继续加,或者下手。 - -所以,要把所有跟A有关系的点,或者接下去和A的neighbor有关系的点,都放进union-find里面,让这些点有Common parents. - -最后output的想法: -做一个 map 。 -之前我们不是给每个num都存好了parent了嘛。 -每个num都有个parent, 然后不同的parent就创造一个不同的list。 -最后,把Map里面所有的list拿出来就好了。 ``` /* +LintCode Find the number Weak Connected Component in the directed graph. Each node in the graph contains a label and a list of its neighbors. (a connected set of a directed graph is a subgraph in which @@ -76,43 +87,31 @@ * DirectedGraphNode(int x) { label = x; neighbors = new ArrayList(); } * }; */ + public class Solution { - class UnionFind { - HashMap map; - //Constructor: - UnionFind(HashSet set) { - map = new HashMap(); - for (int num : set) { - map.put(num, num); - } - } - //Find: - //Root parent should have itself as child in map - int find(int x) { - int parent = map.get(x); - while (parent != map.get(parent)) { - parent = map.get(parent); - } - return parent; - } - void union(int x, int y) { - int findX = find(x); - int findY = find(y); - if (findX != findY) { - map.put(findX, findY); - } - } - } - public List> generateRst (List> rst, UnionFind uf, HashSet set) { - - HashMap> listMap = new HashMap>(); - for (int num : set) { - int rootParent = uf.find(num);//uf.map.get(num); - if (!listMap.containsKey(rootParent)) { - listMap.put(rootParent, new ArrayList()); - } - //Add num into its correct set (meaning its root ancestor) - listMap.get(rootParent).add(num); + HashMap map; + + public List> connectedSet2(ArrayList nodes) { + List> rst = new ArrayList<>(); + if (nodes == null || nodes.size() == 0) return rst; + + buildUnionFind(nodes); + + //find and union: construct the map structure + for (DirectedGraphNode node : nodes) { + for (DirectedGraphNode neighbor : node.neighbors) { + union(node.label, neighbor.label); + } + } + return generateRst(rst); + } + + private List> generateRst (List> rst) { + HashMap> listMap = new HashMap<>(); + for (int num : map.keySet()) { + int rootParent = find(num); + listMap.putIfAbsent(rootParent, new ArrayList<>()); + listMap.get(rootParent).add(num); //Add num into its correct set (meaning its root ancestor) } for (List list: listMap.values()) { @@ -121,43 +120,38 @@ public List> generateRst (List> rst, UnionFind uf, H } return rst; } - - public List> connectedSet2(ArrayList nodes) { - List> rst = new ArrayList>(); - if (nodes == null || nodes.size() == 0) { - return rst; - } - HashSet set = new HashSet(); - for (DirectedGraphNode node : nodes) { - set.add(node.label); + + //Union Find Constructor: + private void buildUnionFind (ArrayList nodes) { + map = new HashMap<>(); + for (DirectedGraphNode node : nodes) { + map.put(node.label, node.label); for (DirectedGraphNode neighbor : node.neighbors) { - set.add(neighbor.label); + map.put(neighbor.label, neighbor.label); } } - UnionFind uf = new UnionFind(set); + } - //find and union: construct the map structure - for (DirectedGraphNode node : nodes) { - for (DirectedGraphNode neighbor : node.neighbors) { - uf.union(node.label, neighbor.label); - } - } - return generateRst(rst, uf, set); - } + //Find: + //Root parent should have itself as child in map + private int find(int x) { + int parent = map.get(x); + while (parent != map.get(parent)) { + parent = map.get(parent); + } + return parent; + } - + private void union(int x, int y) { + int findX = find(x); + int findY = find(y); + if (findX != findY) { + map.put(findX, findY); + } + } } - - - - - - - - - /* Can we do the following for find() ? Inspired by the union-find implemented with int[] Sort of recurisvely trying to get the parent orign, instead of using a while loop? diff --git a/Java/First Bad Version.java b/Java/First Bad Version.java old mode 100644 new mode 100755 index 3a13b36..4faf47c --- a/Java/First Bad Version.java +++ b/Java/First Bad Version.java @@ -1,5 +1,12 @@ -根据isBadVersion的性质,判断还如何end=mid or start=mid. +E +1520920514 +tags: Binary Search + +Binary Search + +根据isBadVersion的性质,判断还如何end=mid or start=mid. isBadVersion 是有方向的嘛,一个点错了,后面全错。 + ``` /* The code base version is an integer start from 1 to n. @@ -10,7 +17,7 @@ You can call isBadVersion to help you determine which version is the first bad one. The details interface can be found in the code's annotation part. -Have you met this question in a real interview? Yes + Example Given n = 5: @@ -31,6 +38,57 @@ */ +/* +Thoughts: +Binary search on [1 .... n]. If prev == good, curr == bad -> return curr index. +*/ +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + if (n <= 0) { + return n; + } + int start = 0; + int end = n; + while (start + 1 < end) { + int mid = start + (end - start) / 2; + if (isBadVersion(mid) && mid - 1 >= 1 && !isBadVersion(mid - 1)) { + return mid; + } else if (isBadVersion(mid)) { + end = mid; + } else { + start = mid; + } + } + return isBadVersion(start) ? start : end; + } +} + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + if (n <= 0) { + return n; + } + int start = 0; + int end = n; + while (start <= end) { + int mid = start + (end - start) / 2; + if (isBadVersion(mid)) { + if (mid - 1 >= 1 && !isBadVersion(mid - 1)) { + return mid; + } + end = mid - 1; + } else { + start = mid + 1; + } + } + return isBadVersion(start) ? start : end; + } +} + + /* Recap: 12.07.2015. Feels like to find the 1st occurance of the match. going left all the way. @@ -43,10 +101,6 @@ * the kth code version is bad or not. */ class Solution { - /** - * @param n: An integers. - * @return: An integer which is the first bad version. - */ public int findFirstBadVersion(int n) { if (n < 1) { return 0; @@ -74,11 +128,6 @@ public int findFirstBadVersion(int n) { } - - - - - class Solution { /** * @param n: An integers. diff --git a/Java/First Missing Positive.java b/Java/First Missing Positive.java old mode 100644 new mode 100755 index b2dc9c2..16dd83a --- a/Java/First Missing Positive.java +++ b/Java/First Missing Positive.java @@ -1,3 +1,24 @@ +H +1525157745 +tags: Array + +给一串无序数字, 有负数: 找这个array里面第一个 missing的 positive integer + +missing positive integer 其实是以 [1, n] 来做比较的. + +#### Array分析, index 技巧 +- 用while loop, 不断地尝试把 number 送到该放的地方 +- 如果 index = nums[i] 超过了nums.length, 当然就不移动了 +- 注意: 检查 val != nums[val], avoid infinitely loop +- 检验: nums[i] 是否等于 i, 如果不对, 就找到了结果 + +#### Edge Case +- 如果nums==null, 其实missing positive integer 自然而然是 1 +- validation时, 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) +- 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +- 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + +``` /* Given an unsorted integer array, find the first missing positive integer. @@ -10,6 +31,52 @@ Your algorithm should run in O(n) time and uses constant space. Tags Expand Array +*/ + + +class Solution { + public int firstMissingPositive(int[] nums) { + if (nums == null || nums.length == 0) { + return 1; + } + if (nums.length == 1) { + return nums[0] == 1 ? 2 : 1; + } + int n = nums.length; + // Move value to correct position with best efforts. O(n) + int i = 0; + while (i < n) { + int val = nums[i]; + if (val != i && val >= 0 && val < n && val != nums[val]) { // val != nums[val], avoid infinitely loop + int temp = nums[val]; + nums[val] = nums[i]; + nums[i] = temp; + } else { + i++; + } + } + + // Check, O(n) + // Skip index 0, only care about positive digits + for (i = 1; i < n; i++) { + if (nums[i] != i) { + return i; + } + } + + // max value is n and stored at index 0 + if (nums[0] == n) { + return n + 1; + } + + // max value if (n - 1) and n will be the next positive number (outside of array) + return n; + } +} + +// Wrong, Using O(nLogN) time +/** + Thoughts: It means: after it's sorted, what's the first missing postive int counted from 1 ---> more @@ -20,14 +87,8 @@ Your algorithm should run in O(n) time and uses constant space. NOTE: Deal with negative and positive number separately Watch out for redundant number: ask if the list has duplicated elements -*/ - - + */ public class Solution { - /** - * @param A: an array of integers - * @return: an integer - */ public int firstMissingPositive(int[] A) { if (A == null || A.length == 0) { return 1; @@ -56,4 +117,5 @@ else if(A[i] != count) {//if not match, kick out } return count; } -} \ No newline at end of file +} +``` \ No newline at end of file diff --git a/Java/First Unique Character in a String.java b/Java/First Unique Character in a String.java new file mode 100644 index 0000000..8d450bd --- /dev/null +++ b/Java/First Unique Character in a String.java @@ -0,0 +1,86 @@ +E +1533602948 +tags: Hash Table, String + +方法1: 按照题意, 找到第一个 first index == last index的字母. + +方法2: 用hashmap存字母的index, 有些重复字母的index就会是个list. 找到单一index, 结合成list, sort, return list.get(0) + +``` +/* +Given a string, find the first non-repeating character in it and return it's index. If it doesn't exist, return -1. + +Examples: + +s = "leetcode" +return 0. + +s = "loveleetcode", +return 2. +Note: You may assume the string contain only lowercase letters. +*/ +class Solution { + public int firstUniqChar(String s) { + int[] freq = new int[256]; + for (char c : s.toCharArray()) { + freq[c - 'a']++; + } + for (int i = 0; i < s.length(); i++) { + if (freq[s.charAt(i) - 'a'] == 1) return i; + } + return -1; + } +} + +/* + Direclty compare first occurance of a character && last occurance, see if at same spot + */ +class Solution { + public int firstUniqChar(String s) { + if (s == null || s.length() == 0) { + return -1; + } + for (int i = 0; i < s.length(); i++) { + if (s.indexOf(s.charAt(i)) == s.lastIndexOf(s.charAt(i))) { + return i; + } + } + return -1; + } +} + + +/* +Thoughts: +1. put all letter into map +2. If more than 1 occurs, remove it from +*/ +class Solution { + public int firstUniqChar(String s) { + if (s == null || s.length() == 0) { + return -1; + } + final Map> map = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + final char letter = s.charAt(i); + if (!map.containsKey(letter)) { + map.put(letter, new ArrayList()); + } + map.get(letter).add(i); + } + final ArrayList indexes = new ArrayList<>(); + for (Map.Entry> entry : map.entrySet()) { + if (entry.getValue().size() == 1) { + indexes.addAll(entry.getValue()); + } + } + + if (indexes.size() == 0) { + return -1; + } + Collections.sort(indexes); + return indexes.get(0); + } +} + +``` diff --git a/Java/Flatten 2D Vector.java b/Java/Flatten 2D Vector.java new file mode 100755 index 0000000..dcf865e --- /dev/null +++ b/Java/Flatten 2D Vector.java @@ -0,0 +1,99 @@ +M +1534317715 +tags: Design + +Implement an iterator to flatten a 2d vector. + +Just move pointers carefully with next(), hashNext() + +#### Basic Implementation using x, y corrdinate +- 就是把2D list里面的element全部遍历一遍。 +- 跟一个nxn的matrix遍历,是没区别的拉; 所有来个x,y,把2d list跑一变。 + +#### Always return item at index 0, and remove from list? +- list 方便remove, 考虑吧reduce input vector (就像给的是linked list 一样) + +``` +/* +Implement an iterator to flatten a 2d vector. + +For example, +Given 2d vector = + +[ + [1,2], + [3], + [4,5,6] +] +By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,2,3,4,5,6]. + +Hint: + +How many variables do you need to keep track? +Two variables is all you need. Try with x and y. +Beware of empty rows. It could be the first few rows. +To write correct code, think about the invariant to maintain. What is it? +The invariant is x and y must always point to a valid point in the 2d vector. Should you maintain your invariant ahead of time or right when you need it? +Not sure? Think about how you would implement hasNext(). Which is more complex? +Common logic in two different places should be refactored into a common method. + + +Tags: Design +Similar Problems: (M) Binary Search Tree Iterator, (M) Zigzag Iterator, (M) Peeking Iterator + +*/ + +/* +Thoughts: +As hint indicates: use 2 pointers to hold position. +Use hasNext to validate (x,y) and move x. +Use next() to return (x,y) and move it(regardless of correctness, which is determined by hasNext()) +*/ +public class Vector2D { + private int x; + private int y; + private List> list; + public Vector2D(List> vec2d) { + if (vec2d == null) { + return; + } + this.x = 0; + this.y = 0; + this.list = vec2d; + } + + public int next() { + int rst = list.get(x).get(y); + if (y + 1 >= list.get(x).size()) { + y = 0; + x++; + } else { + y++; + } + return rst; + } + + public boolean hasNext() { + if (list == null) { + return false; + } + while (x < list.size() && list.get(x).size() == 0) { + x++; + y = 0; + } + if (x >= list.size()) { + return false; + } + if (y >= list.get(x).size()) { + return false; + } + return true; + } +} + +/** + * Your Vector2D object will be instantiated and called as such: + * Vector2D i = new Vector2D(vec2d); + * while (i.hasNext()) v[f()] = i.next(); + */ +``` \ No newline at end of file diff --git a/Java/Flatten Binary Tree to Linked List.java b/Java/Flatten Binary Tree to Linked List.java old mode 100644 new mode 100755 index 6ff61e1..134092e --- a/Java/Flatten Binary Tree to Linked List.java +++ b/Java/Flatten Binary Tree to Linked List.java @@ -1,82 +1,98 @@ -/* -Flatten Binary Tree to Linked List +M +1519794111 +tags: Binary Tree, DFS + +给一个binary tree, 把tree做成 linked list的形式, in-place. -Flatten a binary tree to a fake "linked list" in pre-order traversal. +#### DFS +- 分析题意后, 按照题意: Flatten the tree, no extra space. +- 1. reserve right child: `reservedRightNode` +- 2. Connect `root.right = root.left`, DFS flatten(root.right) +- 3. 移花接木, coneect end of list -> reservedRightNode +- 4. flatten 剩下的. root.right... -Here we use the right pointer in TreeNode as the next pointer in ListNode. +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 -Example - 1 - \ - 1 2 - / \ \ - 2 5 => 3 - / \ \ \ - 3 4 6 4 - \ - 5 - \ - 6 -Note -Don't forget to mark the left child of each node to null. -Or you will get Time Limit Exceeded or Memory Limit Exceeded. +``` +/* +Given a binary tree, flatten it to a linked list in-place. + +For example, +Given + + 1 + / \ + 2 5 + / \ \ + 3 4 6 +The flattened tree should look like: + 1 + \ + 2 + \ + 3 + \ + 4 + \ + 5 + \ + 6 +click to show hints. + +Hints: +If you notice carefully in the flattened tree, each node's right child points to +the next node of a pre-order traversal. Challenge Do it in-place without any extra memory. -Tags Expand -Binary Tree Depth First Search */ - - /** - * Definition of TreeNode: + * Definition for a binary tree node. * public class TreeNode { - * public int val; - * public TreeNode left, right; - * public TreeNode(int val) { - * this.val = val; - * this.left = this.right = null; - * } + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } * } */ -public class Solution { - /** - * @param root: a TreeNode, the root of the binary tree - * @return: nothing - */ - public TreeNode parentNode = null; +/* +Thoughts: +1. Move root.left to root.rigthMost child (while loop find leaf) +2. Take notion of the first root.right, and flatten(xx) on it. +3. On each level, if not left child, flatten right child. +*/ +class Solution { public void flatten(TreeNode root) { - if (root == null) { - return; - } - - if (parentNode != null) { - parentNode.left = null; - parentNode.right = root; - } - - parentNode = root; - TreeNode right = root.right; - flatten(root.left); - flatten(right); + if (root == null) { + return; + } + if (root.left == null) { + flatten(root.right); + } else { + // Reserve right sub tree + TreeNode rightNode = root.right; + + // Move left child to right side, cut off original left child + root.right = root.left; + root.left = null; + + // Flatten the new right child + flatten(root.right); + + // Append previous right child to end of flatten tree + TreeNode node = root; + while (node.right != null) { + node = node.right; + } + node.right = rightNode; // connect + flatten(root.right); + } } } - - - - - - - - - - - - - - - +``` \ No newline at end of file diff --git a/Java/Flatten Nested List Iterator.java b/Java/Flatten Nested List Iterator.java new file mode 100644 index 0000000..3a05e55 --- /dev/null +++ b/Java/Flatten Nested List Iterator.java @@ -0,0 +1,138 @@ +M +1518666697 +tags: Stack, Design + +方法1: 用queue, 把需要的item全部打出来 +方法2: 用stack, 把需要的item先存一行, 每次打开子序列时候, 全部加回stack. + +``` +/* +Given a nested list of integers, implement an iterator to flatten it. + +Each element is either an integer, or a list -- whose elements may also be integers or other lists. + +Example 1: +Given the list [[1,1],2,[1,1]], + +By calling next repeatedly until hasNext returns false, +the order of elements returned by next should be: [1,1,2,1,1]. + +Example 2: +Given the list [1,[4,[6]]], + +By calling next repeatedly until hasNext returns false, +the order of elements returned by next should be: [1,4,6]. +*/ + +/** + * // This is the interface that allows for creating nested lists. + * // You should not implement it, or speculate about its implementation + * public interface NestedInteger { + * + * // @return true if this NestedInteger holds a single integer, rather than a nested list. + * public boolean isInteger(); + * + * // @return the single integer that this NestedInteger holds, if it holds a single integer + * // Return null if this NestedInteger holds a nested list + * public Integer getInteger(); + * + * // @return the nested list that this NestedInteger holds, if it holds a nested list + * // Return null if this NestedInteger holds a single integer + * public List getList(); + * } + */ + + /* +Thoughts: +Store the NestedInteger object in stack. +Note: hasNext() should make sure the next value is integer, instead of array, or empty array. +Therefore, hasNext() will maintain the stack + +*/ +public class NestedIterator implements Iterator { + final Stack stack = new Stack<>(); + + public NestedIterator(List nestedList) { + if (nestedList == null || nestedList.size() == 0) { + return; + } + for (int i = nestedList.size() - 1; i >= 0; i--) { + stack.push(nestedList.get(i)); + } + } + + @Override + public Integer next() { + return hasNext() ? stack.pop().getInteger() : null; + } + + @Override + public boolean hasNext() { + while (!stack.isEmpty()) { + NestedInteger nestedInteger = stack.peek(); + if (nestedInteger.isInteger()) { + return true; + } + + // nestedInteger is list + stack.pop(); + // push back into stack + for (int i = nestedInteger.getList().size() - 1; i >= 0; i--) { + stack.push(nestedInteger.getList().get(i)); + } + } + return false; + } +} + + + +/* +Thoughts: +Use queue to hold all items from the nested list. +There can be list of list at many levels, so we should construct DFS to hold all the values +Space: O(n), +Time: +O(n) initialization +O(1) next(), hasNext(); +*/ +public class NestedIterator implements Iterator { + final Queue queue = new LinkedList(); + + public NestedIterator(List nestedList) { + if (nestedList == null || nestedList.size() == 0) { + return; + } + dfs(nestedList); + } + + private void dfs(List list) { + for (NestedInteger nestedInteger : list) { + if (nestedInteger.isInteger()) { + queue.offer(nestedInteger.getInteger()); + } else { + dfs(nestedInteger.getList()); + } + } + } + + @Override + public Integer next() { + if (!queue.isEmpty()) { + return queue.poll(); + } + return null; + } + + @Override + public boolean hasNext() { + return !queue.isEmpty(); + } +} + +/** + * Your NestedIterator object will be instantiated and called as such: + * NestedIterator i = new NestedIterator(nestedList); + * while (i.hasNext()) v[f()] = i.next(); + */ +``` \ No newline at end of file diff --git a/Java/Flip Game II.java b/Java/Flip Game II.java new file mode 100755 index 0000000..a286852 --- /dev/null +++ b/Java/Flip Game II.java @@ -0,0 +1,257 @@ +M +1528875498 +tags: DFS, Backtracking, DP + +String 只包含 + , - 两个符号. 两个人轮流把consecutive连续的`++`, 翻转成 `--`. + +如果其中一个人再无法翻转了, 另一个人就赢. 求: 给出string, 先手是否能赢. + +#### Backtracking +- curr player 每走一步, 就生成一种新的局面, dfs on this +- 等到dfs结束, 不论成功与否, 都要backtracking +- curr level: 把"++" 改成 "--"; backtrack的时候, 改回 '--' +- 换成boolean[] 比 string/stringBuilder要快很多, 因为不需要重新生成string. +- ++ 可以走 (n - 1)个位置: +- T(N) = (N - 2) * T(N - 2) = (N - 4) * (N - 2) * T(N - 4) ... = O(N!) + +##### iterate based on "++" +- 做一个String s的 replica: string or stringBuilder +- 每次dfs后, 然后更替里面的字符 "+" => "-" +- 目的只是Mark已经用过的index +- 真正的dfs 还是在 original input string s 身上展开 +- 每次都重新生成substring, 并不是很efficient + +##### Game theory +- 保证p1能胜利,就必须保持所有p2的move都不能赢 +- 或者说, 在知道棋的所有情况时, 只要p2有一种路子会输, p1就一定能走对路能赢. +- 同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +- p1: player1, p2: player2 + +#### O(N^2) 的 DP +- 需要Game Theory的功底, Nim game. https://www.jiuzhang.com/qa/941/ +- http://www.1point3acres.com/bbs/thread-137953-1-1.html +- TODO: https://leetcode.com/problems/flip-game-ii/discuss/73954/Theory-matters-from-Backtracking(128ms)-to-DP-(0ms) + +``` +/* +You are playing the following Flip Game with your friend: +Given a string that contains only these two characters: + and -, +you and your friend take turns to flip two consecutive "++" into "--". +The game ends when a person can no longer make a move and +therefore the other person will be the winner. + +Write a function to determine if the starting player can guarantee a win. + +For example, given s = "++++", return true. +The starting player can guarantee a win by flipping the middle "++" to become "+--+". + +Follow up: +Derive your algorithm's runtime complexity. + +Tags: Backtracking +Similar Problems: (E) Nim Game, (E) Flip Game +*/ +// DFS, over 65% +public class Solution { + public boolean canWin(String s) { + if (s == null || s.length() < 2) { + return false; + } + boolean[] sign = new boolean[s.length()]; + for (int i = 0; i < sign.length; i++) sign[i] = s.charAt(i) == '+'; + return dfs(sign); + } + + public boolean dfs(boolean[] sign) { + for (int i = 0; i < sign.length - 1; i++) { + if (sign[i] && sign[i + 1]) { + setSign(sign, i, false); + // When opponent is possible to lose, return true for curr player + if (!dfs(sign)) { + setSign(sign, i, true); + return true; + } + setSign(sign, i, true); + } + } + return false; + } + + private void setSign(boolean[] sign, int i, boolean value) { + sign[i] = value; + sign[i + 1] = value; + } +} + + + +/* +Thoughts: +DFS, search by replace '++' with "--" at each possible spot. +*/ + +class Solution { + public boolean canWin(String s) { + if (s == null || s.length() < 2) { + return false; + } + return search(new StringBuilder(s)); + } + + public boolean search(StringBuilder sb) { + for (int i = 0; i < sb.length() - 1; i++) { + if (sb.substring(i, i + 2).equals("++")) { + sb.replace(i, i + 2, "--"); + if (!canWin(sb.toString())) { + sb.replace(i, i + 2, "++"); + return true; + } + sb.replace(i, i + 2, "++"); + + } + } + return false; + } +} + + +/* + SLOW + Make sure to use a new string, and do not alter the original input s + when calling recursively on canWin. + Much slower, since it's creating string in every dfs +*/ +public class Solution { + public static boolean canWin(String s) { + if (s == null || s.length() <= 1) { + return false; + } + StringBuilder sb = new StringBuilder(s); + while (sb.indexOf("++") != -1) { + int index = sb.indexOf("++"); + if(!canWin(s.substring(0, index) + "--" + s.substring(index + 2))) { + return true; + } + sb.replace(index, index + 1, "-"); + } + return false; + } +} + + + +/* +Attemp2, from:http://www.cnblogs.com/jcliBlogger/p/4886741.html +Similar to my idea, but much more clear: no need of the isP1 flag. +Iterative idea:p1 can win, and p2 must not win at all. +Therefore, if p2's move can't win, we return true on p1's state. +For loop and the if statement works as 'OR': just need one of the p1's movement win. +*/ + +public class Solution { + public boolean canWin(String s) { + if (s == null || s.length() <= 1) { + return false; + } + String str = new String(s); + for (int i = str.indexOf("++"); i >= 0 && i < str.length() - 1; i = str.indexOf("++")) { + if (!canWin( s.substring(0, i) + "--" + s.substring(i + 2))) {//Just pick one way of p1's move + return true; + } + str = str.substring(0, i) + "-" + str.substring(i + 1);//Help to move certain spot. + } + return false; + } + +} +//let k = n/2 +//O(k * (k - 1) * (k - 2) ... k) = O(k!) = O((n/2)!) = O(n!) + +/* +Attempt1, Failed. +Thoughts: +method checkP1Win(), inside of it: +OR all p1's win state, if one of the move wins, return true; +However, a bit of code redundancy, does not feel good about this. +Fails on "+++++++++" +*/ +public class Solution { + public static boolean canWin(String s) { + if (s == null || s.length() <= 1) { + return false; + } + boolean rst = false; + String str = new String(s); + for (int i = str.indexOf("++"); i >= 0 && i < str.length() - 1; i = str.indexOf("++")) { + if (checkP1Win(s, i, true)) { + rst = true; + break; + } + str = str.substring(0, i) + "-" + str.substring(i + 1); + } + return rst; + + } + public static boolean checkP1Win(String str, int x, boolean isP1) { + String s = str.substring(0,x) + "--" + str.substring(x + 2); + if (s.indexOf("++") == -1) { + return isP1; + } + for (int i = s.indexOf("++"); i >= 0 && i < s.length() - 1; i = s.indexOf("++")) { + if (checkP1Win(s, i, !isP1)) { + return true; + } + s = s.substring(0, i) + "-" + s.substring(i + 1); + } + return false; + } + + +} + + + +public class Solution { + public static boolean canWin(String s) { + if (s == null || s.length() <= 1) { + return false; + } + boolean rst = false; + String str = new String(s); + for (int i = str.indexOf("++"); i >= 0 && i < str.length() - 1; i = str.indexOf("++")) { + if (checkP1Win(s, i, true)) { + rst = true; + break; + } + str = str.substring(0, i) + "-" + str.substring(i + 1); + } + return rst; + + } + public static boolean checkP1Win(String str, int x, boolean isP1) { + String s = str.substring(0,x) + "--" + str.substring(x + 2); + if (s.indexOf("++") == -1) { + return isP1; + } + if (isP1) { + String temp = s; + for (int i = temp.indexOf("++"); i >= 0 && i < temp.length() - 1; i = temp.indexOf("++")) { + if (checkP1Win(s, i, !isP1)) { + return true; + } + temp = temp.substring(0, i) + "-" + temp.substring(i + 1); + } + return false; + } else { + String temp = s; + for (int i = temp.indexOf("++"); i >= 0 && i < temp.length() - 1; i = temp.indexOf("++")) { + if (!checkP1Win(s, i, !isP1)) { + return false; + } + temp = temp.substring(0, i) + "-" + temp.substring(i + 1); + } + return true; + } + } +} +``` \ No newline at end of file diff --git a/Java/Flip Game.java b/Java/Flip Game.java new file mode 100755 index 0000000..e641786 --- /dev/null +++ b/Java/Flip Game.java @@ -0,0 +1,111 @@ +E +1524458579 +tags: String + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + +``` +/* +You are playing the following Flip Game with your friend: +Given a string that contains only these two characters: + and -, +you and your friend take turns to flip two consecutive "++" into "--". +The game ends when a person can no longer make a move and +therefore the other person will be the winner. + +Write a function to compute all possible states of the string after one valid move. + +For example, given s = "++++", after one move, +it may become one of the following states: + +[ + "--++", + "+--+", + "++--" +] +*/ + +// 99.83 +class Solution { + public List generatePossibleNextMoves(String s) { + List rst = new ArrayList<>(); + if (s == null || s.length() < 2) { + return rst; + } + StringBuffer sb = new StringBuffer(s); + for (int i = 0; i < s.length() - 1; i++) { + String str = s.substring(i, i + 2); + if (str.equals("++")) { + sb.replace(i, i + 2, "--"); + rst.add(sb.toString()); + sb.replace(i, i + 2, "++"); + } + } + return rst; + } +} + +// 12.06.2015, slower than the previous one +public class Solution { + public List generatePossibleNextMoves(String s) { + List rst = new ArrayList(); + if (s == null || s.length() == 0) { + return rst; + } + ArrayList list = new ArrayList(); + StringBuffer sb = new StringBuffer(s); + while (sb.indexOf("++") != -1) { + int index = sb.indexOf("++"); + list.add(index); + sb.replace(index, index + 1, "*"); + } + for (int index : list) { + rst.add(s.substring(0, index) + "--" + s.substring(index + 2)); + } + return rst; + } +} + + +/* +Thoughts: +Two pointers to check if p1 and p2 match target patern. If so, add. + +Need to ask: are we only looking to change to '--' from '++'? +*/ +public class Solution { + public static List generatePossibleNextMoves(String s) { + List rst = new ArrayList(); + if (s == null || s.length() < 1) { + return rst; + } + char[] arr = s.toCharArray(); + search('+','-',arr,rst); + return rst; + } + + public static void search(char target, char replace, char[] arr, List rst) { + int p1 = 0; + int p2 = 1; + while (p2 <= arr.length - 1) { + if (arr[p1] == target && arr[p2] == target) { + arr[p1] = replace; + arr[p2] = replace; + rst.add(new String(arr)); + arr[p1] = target; + arr[p2] = target; + } + p1++; + p2++; + } + } +} + + + + + +``` \ No newline at end of file diff --git a/Java/Flood Fill.java b/Java/Flood Fill.java new file mode 100644 index 0000000..211e16c --- /dev/null +++ b/Java/Flood Fill.java @@ -0,0 +1,75 @@ +E +1533512047 +tags: DFS + +Same as MS Paint + +#### DFS +- track `boolean[][] visited`, validate before dfs + +``` +/* +An image is represented by a 2-D array of integers, +each integer representing the pixel value of the image (from 0 to 65535). + +Given a coordinate (sr, sc) representing the starting pixel (row and column) of the flood fill, +and a pixel value newColor, "flood fill" the image. + +To perform a "flood fill", consider the starting pixel, +plus any pixels connected 4-directionally to the starting pixel of the same color as the starting pixel, +plus any pixels connected 4-directionally to those pixels (also with the same color as the starting pixel), +and so on. Replace the color of all of the aforementioned pixels with the newColor. + +At the end, return the modified image. + +Example 1: +Input: +image = [[1,1,1],[1,1,0],[1,0,1]] +sr = 1, sc = 1, newColor = 2 +Output: [[2,2,2],[2,2,0],[2,0,1]] +Explanation: +From the center of the image (with position (sr, sc) = (1, 1)), all pixels connected +by a path of the same color as the starting pixel are colored with the new color. +Note the bottom corner is not colored 2, because it is not 4-directionally connected +to the starting pixel. +Note: + +The length of image and image[0] will be in the range [1, 50]. +The given starting pixel will satisfy 0 <= sr < image.length and 0 <= sc < image[0].length. +The value of each color in image[i][j] and newColor will be an integer in [0, 65535]. + +*/ + +/* +dfs, then update curr color. +mark boolean visited[][] +*/ +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + boolean[][] visited; + public int[][] floodFill(int[][] image, int sr, int sc, int newColor) { + int m = image.length, n = image[0].length; + visited = new boolean[m][n]; + dfs(image, sr, sc, image[sr][sc], newColor); + return image; + } + + private void dfs(int[][] image, int sr, int sc, int original, int newColor) { + image[sr][sc] = newColor; + visited[sr][sc] = true; + + for (int i = 0; i < 4; i++) { + int x = sr + dx[i], y = sc + dy[i]; + if (validate(image, x, y, original)) { + dfs(image, x, y, original, newColor); + } + } + } + + private boolean validate(int[][] image, int x, int y, int original) { + int m = image.length, n = image[0].length; + return x >= 0 && x < m && y >= 0 && y < n && image[x][y] == original && !visited[x][y]; + } +} +``` \ No newline at end of file diff --git a/Java/Fraction to Recurring Decimal.java b/Java/Fraction to Recurring Decimal.java new file mode 100755 index 0000000..c3f503f --- /dev/null +++ b/Java/Fraction to Recurring Decimal.java @@ -0,0 +1,126 @@ +M +tags: Hash Table, Math + +TODO: no need of hashMap, just use set to store the existing + +不难想到处理除法:考虑正负,考虑小数点前后。主要是小数点以后的要着重考虑。 +很容易忽略的是integer的益处。 +``` +/* +Given two integers representing the numerator and denominator of a fraction, return the fraction in string format. + +If the fractional part is repeating, enclose the repeating part in parentheses. + +For example, + +Given numerator = 1, denominator = 2, return "0.5". +Given numerator = 2, denominator = 1, return "2". +Given numerator = 2, denominator = 3, return "0.(6)". + +Hide Company Tags Google +Show Tags +Hash Table Math + + +*/ + +/* + Thoughts: + Divide it into small pieces: + 1. d = 0, return null; + 2. n = 0 -> 0 + 3. mark negative. let n = abs(numerator), d = abs(denominator) + 4. consider front and end: + front = (int)sharp divide + end: build hashmap to track if the numerator*10 occurs. Once occurs again, return the remianing. + 5. based on sign, return results. + +Note: +Have to take int overflow INT_MAX, INT_MIN.... +*/ +//Optimized code: +public class Solution { + public String fractionToDecimal(int numerator, int denominator) { + long nume = Math.abs((long)numerator); + long deno = Math.abs((long)denominator); + String sign = (numerator < 0) ^ (denominator < 0) ? "-" : ""; + if (deno == 0) { + return ""; + } else if (nume == 0) { + return "0"; + } else if (nume % deno == 0) { + return sign + nume/deno + ""; + } + + HashMap map = new HashMap(); + StringBuffer rst = new StringBuffer(sign + nume/deno + "."); + long end = nume%deno * 10;//The decimal portion of the value, after decimal point + int i = 0; + while (end != 0){ + if (map.containsKey(end)) { + rst.insert(rst.indexOf(".") + map.get(end) + 1, "("); + rst.append(")"); + return rst.toString(); + } + rst.append(end/deno); + map.put(end, i++); + end = (end % deno) * 10; + } + return rst.toString(); + } +} + + + +//Original working version +public class Solution { + public String fractionToDecimal(int numerator, int denominator) { + long nume = Math.abs((long)numerator); + long deno = Math.abs((long)denominator); + String sign = (numerator < 0) ^ (denominator < 0) ? "-" : ""; + if (deno == 0) { + return ""; + } else if (nume == 0) { + return "0"; + } else if (nume % deno == 0) { + return sign + nume/deno + ""; + } + + String rst = sign + nume/deno + "."; + long end = nume%deno * 10; + + HashMap map = new HashMap(); + boolean repeat = false; + String endRepeat = ""; + int n = 0; + while (true){ + if (end == 0) { + break; + } else if (map.containsValue(end)) { + repeat = true; + break; + } + map.put(n++, end); + end = (end % deno) * 10; + } + + for (int i = 0; i < n; i++) { + if (repeat && map.get(i) == end) { + rst += "(" + map.get(i)/deno; + endRepeat = ")"; + repeat = false; + } else { + rst += map.get(i)/deno; + } + } + + return rst + endRepeat; + } +} + + + + + + +``` \ No newline at end of file diff --git a/Java/Friends Of Appropriate Ages.java b/Java/Friends Of Appropriate Ages.java new file mode 100644 index 0000000..44d0945 --- /dev/null +++ b/Java/Friends Of Appropriate Ages.java @@ -0,0 +1,80 @@ +M +1531813653 +tags: Array, Math + +#### Array, Math +- 这个问题更在于问题本身的分析 (而且还有多余条件); 最终的for loop 也比较不standard. +- People younger than 15 cannot make requests due to the first rule. +- From the age of 15, people can make requests to the same age: a[i] * (a[i] - 1) requests. +- People can make requests to younger people older than 0.5 * i + 7: a[j] * a[i] requests. +- The third rule is redundant as the condition is already covered by the second rule. +- TODO: the approach. + +``` +/* +Some people will make friend requests. The list of their ages is given and ages[i] is the age of the ith person. + +Person A will NOT friend request person B (B != A) if any of the following conditions are true: + +age[B] <= 0.5 * age[A] + 7 +age[B] > age[A] +age[B] > 100 && age[A] < 100 +Otherwise, A will friend request B. + +Note that if A requests B, B does not necessarily request A. Also, people will not friend request themselves. + +How many total friend requests are made? + +Example 1: + +Input: [16,16] +Output: 2 +Explanation: 2 people friend request each other. +Example 2: + +Input: [16,17,18] +Output: 2 +Explanation: Friend requests are made 17 -> 16, 18 -> 17. +Example 3: + +Input: [20,30,100,110,120] +Output: +Explanation: Friend requests are made 110 -> 100, 120 -> 110, 120 -> 100. + + +Notes: + +1 <= ages.length <= 20000. +1 <= ages[i] <= 120. +*/ + +/* +1. bound +age[B] > 0.5 * age[A] + 7 +age[B] <= age[A] + +2.once found the boundary above, apply these: +age[A] >= 100 : all B candidates +age[A] < 100 : only age[B] <= 100 + +sort ages, and then binary search? + +*/ + +// wut? +class Solution { + public int numFriendRequests(int[] ages) { + int[] count = new int[121]; + for (int age: ages) { + count[age]++; + } + int rst = 0; + for (int i = 15; i < 121; i++) { + for (int j = (int)(i * 0.5 + 8); j <= i; j++) { + rst += i == j ? count[j] * (count[i] - 1) : count[j] * count[i]; + } + } + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/Frog Jump.java b/Java/Frog Jump.java new file mode 100644 index 0000000..368acb7 --- /dev/null +++ b/Java/Frog Jump.java @@ -0,0 +1,117 @@ +H +1533535937 +tags: DP, Hash Table + +Frog jump 的题目稍微需要理解: 每个格子可以 jump k-1, k, k+1 steps, 而k取决于上一步所跳的步数. 默认 0->1 一定是跳了1步. + +注意: int[] stones 里面是stone所在的unit (不是可以跳的步数, 不要理解错). + +#### DP +- 原本想按照corrdiante dp 来做, 但是发现很多问题, 需要track 不同的 possible previous starting spot. +- 根据jiuzhang答案: 按照定义, 用一个 map of > +- 每次在处理一个stone的时候, 都根据他自己的 set of , 来走下三步: k-1, k, or k+1 steps. +- 每次走一步, 查看 stone + step 是否存在; 如果存在, 就加进 next position: `stone+step`的 hash set 里面 + +##### 注意init +- `dp.put(stone, new HashSet<>())` mark 每个stone的存在 +- `dp.get(0).add(0)` init condition, 用来做 dp.put(1, 1) + +##### 思想 +- 最终做下来思考模式, 更像是BFS的模式: starting from (0,0), add all possible ways +- 然后again, try next stone with all possible future ways ... etc + +``` + +/* +A frog is crossing a river. The river is divided into x units and at each unit +there may or may not exist a stone. The frog can jump on a stone, but it must not jump into the water. + +Given a list of stones' positions (in units) in sorted ascending order, +determine if the frog is able to cross the river by landing on the last stone. +Initially, the frog is on the first stone and assume the first jump must be 1 unit. + +If the frog's last jump was k units, then its next jump must be either k - 1, k, or k + 1 units. +Note that the frog can only jump in the forward direction. + +Note: + +The number of stones is ≥ 2 and is < 1,100. +Each stone's position will be a non-negative integer < 231. +The first stone's position is always 0. +Example 1: + +[0,1,3,5,6,8,12,17] + +There are a total of 8 stones. +The first stone at the 0th unit, second stone at the 1st unit, +third stone at the 3rd unit, and so on... +The last stone at the 17th unit. + +Return true. The frog can jump to the last stone by jumping +1 unit to the 2nd stone, then 2 units to the 3rd stone, then +2 units to the 4th stone, then 3 units to the 6th stone, +4 units to the 7th stone, and 5 units to the 8th stone. +Example 2: + +[0,1,2,3,4,8,9,11] + +Return false. There is no way to jump to the last stone as +the gap between the 5th and 6th stone is too large. +*/ + +// simplified with helper function +class Solution { + public boolean canCross(int[] stones) { + if (stones == null || stones.length == 0) return false; + Map> dp = new HashMap<>(); + // Init: all stone slots has nothing on them + for (int stone : stones) { + dp.put(stone, new HashSet<>()); + } + dp.get(0).add(0); // such that dp.get(0) will move (dp, 0-stone, 1-step) on index 0 + for (int stone : stones) { + for (int k : dp.get(stone)) { + move(dp, stone, k); + move(dp, stone, k + 1); + move(dp, stone, k - 1); + } + } + int lastStone = stones[stones.length - 1]; + return !dp.get(lastStone).isEmpty(); // able to reach + } + + private void move(Map> dp, int stone, int step) { + if (step > 0 && dp.containsKey(stone + step)) { + dp.get(stone + step).add(step); + } + } +} + +// original +class Solution { + public boolean canCross(int[] stones) { + if (stones == null || stones.length == 0) return false; + int n = stones.length; + Map> dp = new HashMap<>(); + for (int stone : stones) { + dp.put(stone, new HashSet<>()); + } + dp.get(0).add(0); + for (int stone : stones) { + for (int k : dp.get(stone)) { + if (k - 1 > 0 && dp.containsKey(stone + k - 1)) { // k - 1 + dp.get(stone + k - 1).add(k - 1); + } + if (k > 0 && dp.containsKey(stone + k)) {// k + dp.get(stone + k).add(k); + } + if (k + 1 > 0 && dp.containsKey(stone + k + 1)) { // k + 1 + dp.get(stone + k + 1).add(k + 1); + } + } + } + int lastStone = stones[n - 1]; + return !dp.get(lastStone).isEmpty(); // able to reach + } +} +``` \ No newline at end of file diff --git a/Java/Game of Life.java b/Java/Game of Life.java new file mode 100755 index 0000000..5542af8 --- /dev/null +++ b/Java/Game of Life.java @@ -0,0 +1,194 @@ +M +1521012714 +tags: Array + +#### basic +- 简单的implementation, 把count function写清楚就好. +- time: O(mn), extra space: O(mn) +- 注意结尾要一个个board[i][j]copy + +#### follow up +unlimited border? 可能需要分割board. 用大框分割, 每次换行的时候, 重复计算2行就好了. see details below. + +#### improvement: do it in place! +- time: O(mn), extra space: O(1) +- bit manipulation: 用第二个bit来存previous value. +- 因为我们只考虑 0 和 1 而已, 所以可以这样取巧. 但是并不scalable. +- 如果需要存multiple state, 可能需要移动更多位, 或者用一个 state map +- 注意 bit manipulation 的细节: <<1, >>1, 还有 mast的用法: |, & + + +``` +/* +According to the Wikipedia's article: "The Game of Life, also known simply as Life, +is a cellular automaton devised by the British mathematician John Horton Conway in 1970." + +Given a board with m by n cells, each cell has an initial state live (1) or dead (0). +Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) +using the following four rules (taken from the above Wikipedia article): + +Any live cell with fewer than two live neighbors dies, as if caused by under-population. +Any live cell with two or three live neighbors lives on to the next generation. +Any live cell with more than three live neighbors dies, as if by over-population.. +Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction. +Write a function to compute the next state (after one update) of the board given its current state. + +Follow up: +Could you solve it in-place? Remember that the board needs to be updated at the same time: +You cannot update some cells first and then use their updated values to update other cells. +In this question, we represent the board using a 2D array. +In principle, the board is infinite, which would cause problems +when the active area encroaches the border of the array. How would you address these problems? +Credits: + +*/ + +/* +Thoughts +1: +< 2 => 0 => STATE = 2 +> 3 => 0 => STATE = 2 +==2, ==3 => 1 => 1 + +O(mn) +*/ + +// time O(mn * 8) = O(mn), Space(mn) +class Solution { + public void gameOfLife(int[][] board) { + if (board == null || board.length == 0 || board[0] == null || board[0].length ==0) { + return; + } + int m = board.length; + int n = board[0].length; + int[][] newBoard = new int[m][n]; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + int countNeighbor = count(board, i , j); + if (board[i][j] == 1) { + newBoard[i][j] = (countNeighbor < 2 || countNeighbor > 3) ? 0 : 1; + } else { // board[i][j] == 0 + newBoard[i][j] = count(board, i, j) == 3 ? 1 : 0; + } + } + } + // copy + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + board[i][j] = newBoard[i][j]; + } + } + } + + int[] dx = {0, 0, 1, -1, -1, -1, 1, 1}; + int[] dy = {1, -1, 0, 0, -1, 1, -1, 1}; + + // optimize: no need to count all. + private int count(int[][] board, int x, int y) { + int count = 0; + for (int i = 0; i < dx.length; i++) { + int mX = x + dx[i]; + int mY = y + dy[i]; + if (mX >= 0 && mX < board.length && mY >= 0 && mY < board[0].length) { + count += board[mX][mY]; + } + } + return count; + } +} + + +/* +Use 2nd bit as storage for previous status '0' or '1' +0 => 00 +1 => 10 + +This solution is not quite scalable because it only works with '0' and '1' + +We could implement state map if having multiple conditions. +*/ +class Solution { + public void gameOfLife(int[][] board) { + if (board == null || board.length == 0 || board[0] == null || board[0].length ==0) { + return; + } + int m = board.length; + int n = board[0].length; + + // Shift: use 2nd bit to store previous condition + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + board[i][j] = board[i][j] << 1; + } + } + + // Count and calculate + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + int countNeighbor = count(board, i , j); + + // Access 2nd bit for previous value + if ((board[i][j] >> 1) == 1) { + board[i][j] = (countNeighbor >= 2 && countNeighbor <= 3) ? board[i][j] | 1 : board[i][j]; + } else { // board[i][j] == 0 + board[i][j] = count(board, i, j) == 3 ? board[i][j] | 1 : board[i][j]; + } + } + } + + // Filter out 2nd bit and only 1st bit as result + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + board[i][j] = board[i][j] & 1; // remove 2nd bit, which stores previous value + } + } + } + + int[] dx = {0, 0, 1, -1, -1, -1, 1, 1}; + int[] dy = {1, -1, 0, 0, -1, 1, -1, 1}; + + // optimize: no need to count all. + private int count(int[][] board, int x, int y) { + int count = 0; + for (int i = 0; i < dx.length; i++) { + int mX = x + dx[i]; + int mY = y + dy[i]; + if (mX >= 0 && mX < board.length && mY >= 0 && mY < board[0].length) { + count += (board[mX][mY] >> 1); + } + } + return count; + } +} + +/* + +Unlimited? board太大, 一个电脑放不下, 怎么分割? +定一个大框 +每次移动大框的时候, 留着重复2就行了. +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x + +a a a a a a a a a a +y y y y y y y y y y +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x +x x x x x x x x x x + */ +``` \ No newline at end of file diff --git a/Java/Gas Station.java b/Java/Gas Station.java old mode 100644 new mode 100755 index 90cc375..6e78bbc --- a/Java/Gas Station.java +++ b/Java/Gas Station.java @@ -1,7 +1,33 @@ +M +1530150074 +tags: Greedy + +给一串gas station array, 每个index里面有一定数量gas. + +给一串cost array, 每个index有一个值, 是reach下一个gas station的油耗. + +array的结尾地方, 再下一个点是开头, 形成一个circle route. + +找一个index, 作为starting point: 让车子从这个点, 拿上油, 开出去, 还能开回到这个starting point + +#### Greedy +- 不论从哪一个点开始, 都可以记录总油耗, `total = {gas[i] - cost[i]}`. 最后如果total < 0, 无论从哪开始, 必然都不能走回来 +- 可以记录每一步的油耗积累, `remain += gas[i] - cost[i]` +- 一旦 remain < 0, 说明之前的starting point 不合适, 也就是说, 初始点肯定在后面的index. 重设: start = i + 1 +- single for loop. Time: O(n) + +#### NOT DP +- 看似有点像 House Robber II, 但是问题要求的是: 一个起始点的index +- 而不是求: 最后点可否走完/最值/计数 + +``` /* -There are N gas stations along a circular route, where the amount of gas at station i is gas[i]. +There are N gas stations along a circular route, +where the amount of gas at station i is gas[i]. -You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations. +You have a car with an unlimited gas tank and it costs cost[i] of gas +to travel from station i to its next station (i+1). +You begin the journey with an empty tank at one of the gas stations. Return the starting gas station's index if you can travel around the circuit once, otherwise return -1. @@ -17,6 +43,11 @@ You have a car with an unlimited gas tank and it costs cost[i] of gas to travel Tags Expand Greedy +*/ + +/** + +Previous Notes: Thoughts: Loop through the gas station, and track the possible starting index. Start from i = 0 ~ gas.length, and use a second pointer move to track how far we are travelling @@ -25,26 +56,20 @@ You have a car with an unlimited gas tank and it costs cost[i] of gas to travel Thus, once i's station failed to get to x, set index = x + 1: we are moving on to next possible starting point. 'total':simply indicates if we can make it a circle -*/ - + */ public class Solution { - /** - * @param gas: an array of integers - * @param cost: an array of integers - * @return: an integer - */ public int canCompleteCircuit(int[] gas, int[] cost) { if (gas == null || cost == null || gas.length == 0 || cost.length == 0) { return -1; } int start = 0; - int remain = 0; + int remain = 0; // remained gas int total = 0; for (int i = 0; i < gas.length; i++) { remain += gas[i] - cost[i]; if (remain < 0) { remain = 0; - start = i + 1; + start = i + 1; // restart } total += gas[i] - cost[i]; } @@ -54,3 +79,5 @@ public int canCompleteCircuit(int[] gas, int[] cost) { return start; } } + +``` \ No newline at end of file diff --git a/Java/Generate Parentheses.java b/Java/Generate Parentheses.java old mode 100644 new mode 100755 index e14d824..058467c --- a/Java/Generate Parentheses.java +++ b/Java/Generate Parentheses.java @@ -1,5 +1,20 @@ -递归。 -看thought.取或者不取(, ) +M +1528731679 +tags: String, DFS, Backtracking, Sequence DFS + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + ``` /* Generate Parentheses @@ -12,11 +27,82 @@ "((()))", "(()())", "(())()", "()(())", "()()()" +[ + "((()))", + "(()())", + "(())()", + "()(())", + "()()()" +] + Tags Expand String Backtracking Recursion Zenefits Google */ +/* +Thoughts: +1. We know left-parentheses numL <= numR; otherwise it'll be incorrect +2. Pick left or right parentheses always gives up to 2 options at one node: it becomes a tree. We can use DFS and find the leaf +3. Always populate the List +*/ +//Faster because StringBuffer object is reused (add/remove elements of it) +class Solution { + private String LEFT = "("; + private String RIGHT = ")"; + public List generateParenthesis(int n) { + List result = new ArrayList<>(); + if (n == 0) { + return result; + } + dfs(result, new StringBuffer(), n, n); + return result; + } + + private void dfs(List result, StringBuffer sb, int numL, int numR) { + if (numL == 0 && numR == 0) { + result.add(sb.toString()); + return; + } + if (numL > 0) { + dfs(result, sb.append(LEFT), numL - 1, numR); + sb.deleteCharAt(sb.length() - 1); + } + if (numR > 0 && numL < numR) { // hardcheck/validation + dfs(result, sb.append(RIGHT), numL, numR - 1); + sb.deleteCharAt(sb.length() - 1); + } + } +} + +// Slightly slower because building new string every time. +class Solution { + private final static String LEFT = "("; + private final static String RIGHT = ")"; + public List generateParenthesis(int n) { + List result = new ArrayList<>(); + if (n == 0) { + return result; + } + dfs(result, "", n, n); + return result; + } + + private void dfs(List result, String str, int numL, int numR) { + if (numL == 0 && numR == 0) { + result.add(str); + return; + } + if (numL > 0) { + dfs(result, str + LEFT, numL - 1, numR); + } + if (numR > 0 && numL < numR) { + dfs(result, str + RIGHT, numL, numR - 1); + } + } +} + + /* Thoughts: //http://fisherlei.blogspot.com/2012/12/leetcode-generate-parentheses.html @@ -65,70 +151,4 @@ public void helper(ArrayList list, int left, int right, int n) { } } - -/* - // - 1st attempt, timeout. - Thoughts: - n = 0, null - n = 1, trivial: () - Do i-- from n. For each i >= 2 - it can choose: close a paren - open another paren - front = ( - end = ) - helper(front, end, int n) - if (n == 1) { - front + "()" + end - rst.add // check duplicate - } - -*/ -public class Solution { - /** - * @param n n pairs - * @return All combinations of well-formed parentheses - */ - public ArrayList rst = new ArrayList(); - public ArrayList generateParenthesis(int n) { - if (n <= 0) { - return rst; - } else if (n == 1){ - rst.add("()"); - return rst; - } - helper("", "", n); - Collections.sort(rst); - return rst; - } - //3 - public void helper(String front, String end, int n) { - if (n == 1) { - String rt = front + "()" + end; - if (!rst.contains(rt)){ - rst.add(rt); - } - return; - } - n--; - - helper(front + "(", ")" + end, n); - helper(front + "()", end, n); - helper(front, "()" + end, n); - helper(front + end + "(", ")", n); - helper(front + end, "()", n); - helper(front + end + "()", "", n); - helper("(", ")" + front + end, n); - helper("()", front + end, n); - helper("","()" + front + end, n); - helper("(",front+end+")",n); - helper("(" + front+end,")",n); - helper("(" + front, end + ")",n); - helper("("+front+end+")", "", n); - helper("", "("+front+end+")", n); - - } - -} - ``` \ No newline at end of file diff --git a/Java/Graph Valid Tree.java b/Java/Graph Valid Tree.java old mode 100644 new mode 100755 index 18288cc..98ff91f --- a/Java/Graph Valid Tree.java +++ b/Java/Graph Valid Tree.java @@ -1,183 +1,223 @@ -复习Union-Find的另外一个种形式。 -题目类型:查找2个元素是不是在一个set里面。如果不在,false. 如果在,那就合并成一个set,共享parent. -存储的关键都是:元素相对的index上存着他的root parent. +M +1534225885 +tags: DFS, BFS, Union Find, Graph + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 -另一个union-find, 用hashmap的:http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ ``` /* -Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), +Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), write a function to check whether these edges make up a valid tree. -Example +For example: + Given n = 5 and edges = [[0, 1], [0, 2], [0, 3], [1, 4]], return true. Given n = 5 and edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], return false. -Note -You can assume that no duplicate edges will appear in edges. -Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges. +Note: you can assume that no duplicate edges will appear in edges. Since all edges are undirected, +[0, 1] is the same as [1, 0] and thus will not appear together in edges. -Tags Expand -Depth First Search Breadth First Search Union Find Facebook Zenefits Google -*/ + */ + /* - Thoughts: do union-find. //http://www.jiuzhang.com/solutions/graph-valid-tree/ - How to check if we have disconnected pair? Think about it: - A valid tree has n-1 edges. If we have 1 disconnected pair, that means, - we lost 1 edge, like n-2 edgets now. - OR, keep thinking: if we have a cycle, which is a extra edge, that becomes n edgets. - Therefore, a first check (assume if with n-1 edgets is valid), just check if edges.lenght == n -1. +Thoughts: +Tree should not have cycle, which means adding each edge should connect a new node and that node should not be in the tree yet. +If found cycle, return false. - Then, natually we can think of : what if missing a couple edges and have a couple cycles, - which makes edges.length == n - 1? - So if in this complex case, there must be >=1 cycles. Now just explicitly check for cycle. - How to use unin-find to check no cycle: -*** if a pair of node has same parent. If they do, that makes an triangle. False. - - What does Union-Find do? - Union-Find is a data structure (this problem implemented as a array), that union 2 sets and - checks if 2 elements are in the same set. - In another problem, it can be implemented with HashMap as well. - +Note: need to count # of unions after merging. Count should be 1 for a tree. */ - -public class Solution { - int[] parents; +// 简化版 unionfind +class Solution { + int[] father; + int count; public boolean validTree(int n, int[][] edges) { - if (n - 1 != edges.length) { - return false; - } - parents = new int[n]; - //Init - for (int i = 0; i < parents.length; i++) { - parents[i] = i; - } - //Use union-find to detect if pair has common parents, and merge then to 1 set - for (int i = 0; i < edges.length; i++){ - if (find(edges[i][0]) == find(edges[i][1])) { - return false; - } - union(edges[i][0], edges[i][1]); - } - - return true; - } + if (n <= 0) {// No node, false + return false; + } - /* - Not only find parent, also update the spot parents[node] with parent node, recursively. - - *** The fact is, at all levels, if any curr != its parent, it'll trigger the find() method, - Then it makes sure parent node will be assigned to this curr node index. - - Goal: Mark curr node: who is your ancestor parent; and that indicates if other nodes are - in the same union as curr. - */ - public int find(int node) { - if (parents[node] == node) {//If curr node == its parent, return curr node. - return node; - } - //If curr node != its parent, we will attempt to find its grandparents, and assign to curr node. - parents[node] = find(parents[node]); - return parents[node]; + // init union find data structure + father = new int[n]; + count = n; + for (int i = 0; i < n; i++) father[i] = i; + // perform union find + for (int i = 0; i < edges.length; i++) { + int x = edges[i][0]; + int y = edges[i][1]; + if (find(x) == find(y)) return false; // new index has been visited before, so cycle existed. + union(x, y); + } + return count == 1; // no other isolated sub-graph } - /* - Either union x into y, or the other way - */ - public void union(int x, int y) { - int findX = parents[x]; - int findY = parents[y]; - if (findX != findY) { - parents[findX] = findY; - } + + private void union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootX != rootY) { + father[rootX] = rootY; + count--; + } } -} - - - - - - + private int find(int x) { + if (father[x] == x) return x; + return father[x] = find(father[x]); + } +} +/* +DFS +1. Tree should not have cycle, which means adding each edge should connect a new node +and that node should not be in the tree yet. If found cycle, return false. +2. Validate if all edge connected: # of visited node should match graph size +*/ +class Solution { + public boolean validTree(int n, int[][] edges) { + if (n == 0) return false; + Map> graph = buildGraph(n, edges); + Set visited = new HashSet<>(); -/* - Attempt failed: - check:No cycle.Connected. - Sort the input based on edges[i][0]. - However, this is wrong; both 0 and 1 index of an edge can be used a root. -*/ + // dfs(graph, visited, i, -1) and validate cycle + if (!dfs(graph, visited, 0, -1)) return false; + + // validate if all edge connected: # of visited node should match graph size + return visited.size() == graph.size(); + } + + // build graph in form of adjacent list + private Map> buildGraph(int n, int[][] edges) { + Map> graph = new HashMap<>(); + for (int i = 0; i < n; i++) { + graph.putIfAbsent(i, new HashSet<>()); + } + for (int[] edge: edges) { + graph.get(edge[0]).add(edge[1]); + graph.get(edge[1]).add(edge[0]); + } + return graph; + } -//Just use priorityqueue -public class Solution { - /** - * @param n an integer - * @param edges a list of undirected edges - * @return true if it's a valid tree, or false - */ - class Pair { - int from, to; - public Pair(int f, int t) { - this.from = f; - this.to = t; - } + // dfs: mark visited nodes, and keep dfs into children nodes + private boolean dfs(Map> graph, Set visited, int curr, int pre) { + if (visited.contains(curr)) return false; + visited.add(curr); + for (int child : graph.get(curr)) { + if (child == pre) continue; + if (!dfs(graph, visited, child, curr)) return false; + } + return true; } +} + +// Full-length union-find, not necessary +class Solution { public boolean validTree(int n, int[][] edges) { - if (n == 1) { + // No node, false + if (n == 0) { + return false; + } + // 1 Node without edges, true + if (n == 1 && (edges == null || edges.length == 0)) { return true; } - if (edges == null || edges.length == 0 || edges[0].length == 0 || n <= 0) { - return false; - } - HashSet set = new HashSet(); - PriorityQueue queue = new PriorityQueue(2, new Comparator(){ - public int compare(Pair A, Pair B){ - return A.from - B.from; - } - }); - - //add into queue, format it like pair(small, large) - for (int i = 0; i < edges.length; i++) { - if (edges[i][0] < edges[i][1]) { - queue.offer(new Pair(edges[i][0], edges[i][1])); - } else { - queue.offer(new Pair(edges[i][1], edges[i][0])); - } - } - //check - set.add(queue.peek().from);//0 - while (!queue.isEmpty()) { - Pair p = queue.poll(); - //check node existance && cycle - if (!set.contains(p.from) || set.contains(p.to)) { - return false; - } - set.add(p.to); - } - return true; + // >= 2 nodes but no edges, false; + if (edges == null || edges.length == 0 || edges[0] == null || edges[0].length == 0) { + return false; + } + UnionFind unionFind = new UnionFind(n); + for (int i = 0; i < edges.length; i++) { + int x = edges[i][0]; + int y = edges[i][1]; + if (unionFind.find(x) == unionFind.find(y)) { + return false; + } + unionFind.union(x, y); + } + return unionFind.query() == 1; } } +class UnionFind { + int[] father; + int count; + UnionFind(int x) { + father = new int[x]; + count = x; + for (int i = 0; i < x; i++) { + father[i] = i; + } + } + + public void union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootX != rootY) { + father[rootX] = rootY; + count--; + } + } + public int find(int x) { + if (father[x] == x) { + return x; + } + return father[x] = find(father[x]); + } + + public int query() { + return count; + } +} +/* + Thoughts: do union-find. //http://www.jiuzhang.com/solutions/graph-valid-tree/ + How to check if we have disconnected pair? Think about it: + A valid tree has n-1 edges. If we have 1 disconnected pair, that means, + we lost 1 edge, like n-2 edgets now. + OR, keep thinking: if we have a cycle, which is a extra edge, that becomes n edgets. + Therefore, a first check (assume if with n-1 edgets is valid), just check if edges.lenght == n -1. - - - - - - - - - - - - + Then, natually we can think of : what if missing a couple edges and have a couple cycles, + which makes edges.length == n - 1? + So if in this complex case, there must be >=1 cycles. Now just explicitly check for cycle. + How to use unin-find to check no cycle: + if a pair of node has same parent. If they do, that makes an triangle. False. + + What does Union-Find do? + Union-Find is a data structure (this problem implemented as a array), that union 2 sets and + checks if 2 elements are in the same set. + In another problem, it can be implemented with HashMap as well. + +*/ ``` \ No newline at end of file diff --git a/Java/Gray Code.java b/Java/Gray Code.java new file mode 100755 index 0000000..aec8959 --- /dev/null +++ b/Java/Gray Code.java @@ -0,0 +1,180 @@ +M +tags: Backtracking + +TODO: +1. backtracking, using set to perform contains() +2. BFS: use queue to keep the mutations + +题目蛋疼,目前只接受一种结果。 + +BackTracking + DFS: + Recursive helper里每次flip一个 自己/左边/右边. Flip过后还要恢复原样.遍历所有. + +曾用法(未仔细验证): +基本想法就是从一个点开始往一个方向走,每次flip一个bit, 碰壁的时候就回头走。 + +``` +/* + +The gray code is a binary numeral system where two successive values differ in only one bit. + +Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0. + +For example, given n = 2, return [0,1,3,2]. Its gray code sequence is: + +00 - 0 +01 - 1 +11 - 3 +10 - 2 +Note: +For a given n, a gray code sequence is not uniquely defined. + +For example, [0,2,3,1] is also a valid gray code sequence according to the above definition. + +For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that. + +Hide Company Tags Amazon +Hide Tags Backtracking + +*/ + + +//Another solution, 02.10.2016 using DFS +//generate order, output the numerical value of each binary code. Integer.parseInt("10101", 2). +//Start with n-bit char[] of 0's. Flip one bit at a time. +//Recursive helper. char[], index. Flip or not flip. DFS + +public class Solution { + public List grayCode(int n) { + List rst = new ArrayList(); + if (n < 0) { + return rst; + } else if (n == 0) { + rst.add(0); + return rst; + } + char[] list = new char[n]; + for (int i = 0; i < n; i++) { + list[i] = '0'; + } + helper(rst, list, n - 1); + + return rst; + } + + public void helper(List rst, char[] list, int index) { + + rst.add(Integer.parseInt(new String(list), 2)); + + //self + list[index] = list[index] == '0' ? '1' : '0'; + int num = Integer.parseInt(new String(list), 2); + if (!rst.contains(num)) { + helper(rst, list, index); + } + list[index] = list[index] == '0' ? '1' : '0'; + + //left + if (index -1 >= 0) { + list[index - 1] = list[index - 1] == '0' ? '1' : '0'; + num = Integer.parseInt(new String(list), 2); + if (!rst.contains(num)) { + helper(rst, list, index - 1); + } + list[index - 1] = list[index - 1] == '0' ? '1' : '0'; + } + + //right + if (index + 1 < list.length) { + list[index + 1] = list[index + 1] == '0' ? '1' : '0'; + num = Integer.parseInt(new String(list), 2); + if (!rst.contains(num)) { + helper(rst, list, index + 1); + } + list[index + 1] = list[index + 1] == '0' ? '1' : '0'; + } + } +} + + +/* + +Leetcode tags shows backtracking. That should be different approach than what I hvae below: + +*/ + +/* + +TRY: My code works for this run-through, however does not fit the OJ yet + +0 0 0 [start, noting happend, flip index 0] + +0 0 <-1 [move to flip left adjacent] + +0 <-1 1 [move to flip left adjacent] + +1-> 1 1 [move to flip right adjacent] + +1 0-> 1 [move to flip right adjacent] + +1 0 <-0 [move to flip left adjacent] + +1 <-1 0 [move to flip left adjacent] + +0 1 0 [done] + +Conclusion: hit the wall and flip the other direction. + +Every flip, add integer to list + +Convert the char[] to string, then Integer.parse(str, 2) to integer + +Simulate the steps: + +For example, when n = 3, step = n - 1. It takes 2 steps from right side to reach left side, it hits the wall and turn around. + +Now: + +1. Initialize char[] and add '000' + +2. do for loop on 1 ~ 2^n -2. last step '010' is stepped into, but no further action, so take 2^3 - 1 = 7 steps. + +*/ +public class Solution { + public List grayCode(int n) { + List rst = new ArrayList(); + if (n < 0) { + return rst; + } + char[] bits = new char[n]; + for (int i = 0; i < bits.length; i++) { + bits[i] = '0'; + } + String str = new String(bits); + if (n == 0) { + str = "0"; + } + rst.add(Integer.parseInt(str, 2)); + int step = n - 1; + boolean LR = true;//L: true; R: false + int steps = (int)Math.pow(2, n) - 1; + for (int i = 0; i < steps; i++) { + bits[step] = bits[step] == '0' ? '1' : '0'; + str = new String(bits); + rst.add(Integer.parseInt(str, 2)); + if (LR) { + step--; + } else { + step++; + } + if (step == (n - 1) || step == 0) {//Turn around + LR = !LR; + } + } + + return rst; + } +} + + +``` \ No newline at end of file diff --git a/Java/Group Anagrams.java b/Java/Group Anagrams.java new file mode 100755 index 0000000..23507a9 --- /dev/null +++ b/Java/Group Anagrams.java @@ -0,0 +1,211 @@ +M +1524109143 +tags: String, Hash Table + +给一串string, return list of list, 把anagram 放在一起. + +#### Hash Table, key 是 character frequency +- 存anagram +- 用 character frequency 来做unique key +- 用固定长度的char[26] arr 存每个字母的frequency; 然后再 new string(arr). +- 因为每个位子上的frequency的变化,就能构建一个unique的string +- O(nk), k = max word length + + +#### Hash Table, key 是 sorted string +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + +``` + +/* +Given an array of strings, group anagrams together. + +For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"], +Return: + +[ + ["ate", "eat","tea"], + ["nat","tan"], + ["bat"] +] +Note: +For the return value, each inner list's elements must follow the lexicographic order. +All inputs will be in lower-case. +Hide Company Tags Amazon Bloomberg Uber Facebook +Hide Tags Hash Table String +Hide Similar Problems (E) Valid Anagram (E) Group Shifted Strings + +*/ + +/* +Thoughts: +1. Use HashMap to store anagram. Anagram should be stored in same key +2. Figure out the key: use 26-letter to count frequency +3. convrt to list of list +*/ +class Solution { + public List> groupAnagrams(String[] strs) { + List> rst = new ArrayList<>(); + if (strs == null || strs.length == 0) { + return rst; + } + Map> map = new HashMap<>(); + for (String str : strs) { + String key = getKey(str); + if (!map.containsKey(key)) { + map.put(key, new ArrayList<>()); + } + map.get(key).add(str); + } + + // convert + for (Map.Entry> entry : map.entrySet()) { + rst.add(entry.getValue()); + } + return rst; + } + + private String getKey(String s) { + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a']++; + } + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 26; i++) { + sb.append(count[i]); + } + return sb.toString(); + } +} + + + +/* + optmize1: + Use collectoins.sort() instead of Arrays.sort() in front + + 分析: + n: str[].length + p: parts: number of keys in hashmap + m: max string length + (n/p): average ['abc', 'cab', 'cba', ... etc] length + Collections.sort(...) : (n/p)log(n/p) * p = n*log(n/p) + + so, how large is n/p? + 1. p is small, -> result goes large + 2. p is large -> result goes Small + + overal: + n*m + n*log(n/p) + + If Arrays.sort(strs) at first: + n*m + nlogn + + Therefore, using Collections.sort() in this problem is faster than using Arrays.sort() in front. + + optimize2: + char[26] arr: [1,1,2,0,0,0,0,0,0,0,...] + new String(arr) -> key of hashmap + n * m + + optimize3: + If not necessary, we don't have to use map.entrySet(); + we can just use map.keySet(); It's faster + + for (Map.Entry> entry : map.entrySet()) { + Collections.sort(entry.getValue()); + rst.add(entry.getValue()); + } + + LEET CODE SPEED: 80% ~ 97% (25ms ~ 22ms) + + + +*/ +public class Solution { + public List> groupAnagrams(String[] strs) { + List> rst = new ArrayList>(); + if (strs == null || strs.length == 0) { + return rst; + } + + HashMap> map = new HashMap>(); + + for (int i = 0; i < strs.length; i++) { + String str = calcUniqueKey(strs[i]); + if (!map.containsKey(str)) { + map.put(str, new ArrayList()); + } + map.get(str).add(strs[i]); + } + + for(String key: map.keySet()){//FASTER + Collections.sort(map.get(key)); + rst.add(map.get(key)); + } + + return rst; + } + + public String calcUniqueKey(String s) { + char[] arr = new char[26]; + for (int i = 0; i < s.length(); i++) { + arr[s.charAt(i) - 'a'] += 1; + } + return new String(arr); + } +} + + +/* +Thoughts: +brutle force: save to Map O(n) space,time + +Store the anagram in a order list. Collections.sort it. MO(logM) + +Note: use Arrays.sort() to sort string. +Note2: can do (element : array, arraylist) in for loop + +*/ +public class Solution { + public List> groupAnagrams(String[] strs) { + List> rst = new ArrayList>(); + if (strs == null || strs.length == 0) { + return rst; + } + HashMap> map = new HashMap>(); + + for (int i = 0; i < strs.length; i++) { + char[] arr = strs[i].toCharArray(); + Arrays.sort(arr); + String str = new String(arr); + if (!map.containsKey(str)) { + map.put(str, new ArrayList()); + } + map.get(str).add(strs[i]); + } + /* + for (Map.Entry> entry : map.entrySet()) {//SLOW + Collections.sort(entry.getValue()); + rst.add(entry.getValue()); + } + */ + for(String key: map.keySet()){//FASTER + Collections.sort(map.get(key)); + rst.add(map.get(key)); + } + + return rst; + } +} + + + + + + + +``` \ No newline at end of file diff --git a/Java/Group Shifted Strings.java b/Java/Group Shifted Strings.java new file mode 100755 index 0000000..785af2a --- /dev/null +++ b/Java/Group Shifted Strings.java @@ -0,0 +1,94 @@ +M +1534312697 +tags: Hash Table, String + + +#### Convert to orginal string +- shit by offset. `int offset = s.charAt(0) - 'a';` +- increase if less than 'a': `if (newChar < 'a') newChar += 26;` + +#### Previous notes +- 相同shift规则的string, 能被推算到同一个零起始点,就是共同减去一个char,最后就相等。以此作为key,用HashMap。一目了然。 +- 记得根据题目意思,一开始要String[] sort一下。 + +``` +/* +Given a string, we can "shift" each of its letter to its successive letter, for example: "abc" -> "bcd". We can keep "shifting" which forms the sequence: + +"abc" -> "bcd" -> ... -> "xyz" +Given a list of strings which contains only lowercase alphabets, group all strings that belong to the same shifting sequence. + +For example, given: ["abc", "bcd", "acef", "xyz", "az", "ba", "a", "z"], +Return: + +[ + ["abc","bcd","xyz"], + ["az","ba"], + ["acef"], + ["a","z"] +] +Note: For the return value, each inner list's elements must follow the lexicographic order. + +Hide Company Tags Google Uber +Hide Tags Hash Table String +Hide Similar Problems (M) Group Anagrams + +*/ + +class Solution { + public List> groupStrings(String[] strings) { + Map> map = new HashMap<>(); + for (String s : strings) { + String origin = convert(s); + map.putIfAbsent(origin, new ArrayList<>()); + map.get(origin).add(s); + } + return new ArrayList<>(map.values()); + } + + private String convert(String s) { + StringBuffer sb = new StringBuffer(); + int offset = s.charAt(0) - 'a'; + for (char c : s.toCharArray()) { + char newChar = (char)(c - offset); + if (newChar < 'a') newChar += 26; + sb.append(newChar); + } + return sb.toString(); + } +} + +//Reduce each string into initial state: with the char at index 0 equal to integer 0. Save it as key for hashmap +public class Solution { + public List> groupStrings(String[] strings) { + List> rst = new ArrayList>(); + if (strings == null || strings.length == 0) { + return rst; + } + Arrays.sort(strings); + HashMap> map = new HashMap>(); + + for (String s : strings) { + char[] arr = s.toCharArray(); + char head = arr[0]; + for (int i = 0; i < arr.length; i++) { + if (arr[i] - head < 0) { + arr[i] = (char)((arr[i] - head) + 26); + } else { + arr[i] = (char)(arr[i] - head); + } + } + String key = new String(arr); + if (!map.containsKey(key)) { + map.put(key, new ArrayList()); + } + map.get(key).add(s); + } + + for (Map.Entry> entry : map.entrySet()) { + rst.add(entry.getValue()); + } + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/Guess Number Higher or Lower.java b/Java/Guess Number Higher or Lower.java new file mode 100644 index 0000000..0c848d9 --- /dev/null +++ b/Java/Guess Number Higher or Lower.java @@ -0,0 +1,62 @@ +E +1516346617 +tags: Binary Search + +binary search 公式 + +``` +/* +We are playing the Guess Game. The game is as follows: + +I pick a number from 1 to n. You have to guess which number I picked. + +Every time you guess wrong, I'll tell you whether the number is higher or lower. + +You call a pre-defined API guess(int num) which returns 3 possible results (-1, 1, or 0): + +-1 : My number is lower + 1 : My number is higher + 0 : Congrats! You got it! +Example: +n = 10, I pick 6. + +Return 6. +*/ + +/* The guess API is defined in the parent class GuessGame. + @param num, your guess + @return -1 if my number is lower, 1 if my number is higher, otherwise return 0 + int guess(int num); */ + +/* +Thoughts: +Like binary search, based on result, guess another one. +*/ + +/* The guess API is defined in the parent class GuessGame. + @param num, your guess + @return -1 if my number is lower, 1 if my number is higher, otherwise return 0 + int guess(int num); */ + +public class Solution extends GuessGame { + public int guessNumber(int n) { + if (n <= 0) { + return n; + } + int start = 0; + int end = n; + while (start + 1 < end) { + int mid = start + (end - start) / 2; + int result = guess(mid); + if (result == 0) { + return mid; + } else if (result == 1) { + start = mid; + } else { + end = mid; + } + } + return guess(start) == 0 ? start : end; + } +} +``` \ No newline at end of file diff --git a/Java/H-Index II.java b/Java/H-Index II.java new file mode 100755 index 0000000..4422ab0 --- /dev/null +++ b/Java/H-Index II.java @@ -0,0 +1,91 @@ +M +1531083215 +tags: Binary Search + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + +``` +/* +Follow up for H-Index: What if the citations array is sorted in ascending order? +Could you optimize your algorithm? +Hint: + +Expected runtime complexity is in O(log n) and the input is sorted. + +Hide Company Tags Facebook +Hide Tags Binary Search +Hide Similar Problems (M) H-Index + +*/ + +public class Solution { + public int hIndex(int[] citations) { + if (citations == null || citations.length == 0) { + return 0; + } + int n = citations.length; + int start = 0, end = n - 1; + while (start + 1 < end) { + int mid = start + (end - start) / 2; + int h = n - mid; + if (citations[mid] < h) start = mid; + else { // citations[mid] >= h + if (mid - 1 >= 0 && citations[mid - 1] <= h) return h; // verify the prior node: (N-h) + end = mid; + } + } + // validate start && end + if (citations[start] >= n - start) return n - start; + if (citations[end] >= n - end) return n - end; + return 0; + } +} + +/* + citations[0,1,3,5,6] + look for a h, where x = N-h, arr[x] >= h + h is from right ot left. + We want to find smallest x that has arr[x] >= n-x + binary search: + start,mid,end + if match, keep going left until not able to + O(nLogN) +*/ + +public class Solution { + public int hIndex(int[] citations) { + if (citations == null || citations.length == 0) { + return 0; + } + int n = citations.length; + int start = 0; + int end = n - 1; + int mid; + while (start + 1 < end) { + mid = start + (end - start) / 2; + int h = n - mid; + if (citations[mid] >= h) { + if (mid - 1 >= 0 && citations[mid - 1] > h) { // check last (N-h) node + end = mid; + } else { + return h; + } + } else if (citations[mid] < h) { + start = mid; + } + } + if (citations[start] >= n - start) { + return n - start; + } else if (citations[end] >= n - end) { + return n - end; + } + return 0; + } +} + +``` diff --git a/Java/H-Index.java b/Java/H-Index.java new file mode 100755 index 0000000..b9e8e37 --- /dev/null +++ b/Java/H-Index.java @@ -0,0 +1,150 @@ +M +1531081483 +tags: Hash Table, Sort, Bucket Sort + +找到h-index, 给的citation int[] 并不是sorted. h-index 的definition 具体看题目. + +#### Sort, find h from end +- 例子写出来,发现可以sort以后按照定义搜索一遍。 nlogn. +- 搜索一遍时候可以优化,用binary search. 但是没意义,因为array.sort已经用了nlogn +- 题目给的规则, 从小到大排序后: 剩下的 paper `n-h`, 全部要 <= h 个 citation. +- time O(nlogn), search O(n) + +##### 正向思考 +- 从i = 0 开始找第一个 `citations[i] >= h`, 就是第一个符合 h-index 规则的paper, return h + +##### 反向思考 +- 如果从 h = n, 每次h--; 那么 `x = n - h` 就是从 `[0 ~ n)` 开始找第一个 `dictations[x] >= h`, 就是结果 +- 同时, `dictations[x-1]` 就是最后一个(dictation最多的)其余的paper. + +#### Bucket count / Bucket Sort +- O(n) +- Bucket sort的思想(更像是counting sort?): 过一遍 input, 把dictation value 作为 index, 分布在bucket[index]上++ +- bucket[x] 是 count when # of citation == x. +- 如果 x 大于 n的时候, 就超出了index范围, 但是刚好这个问题可以包容, 把这样的情况记位在bucket[n]就可以 +- 巧妙: `sum += bucket[h]` where `h = [n ~ 0]` 利用了h-index的definition: +- #of papers (sum of bucket[n]...bucket[0]) has more than h cidations +- 这里运用到了bucket sort的思想, 但是并不是sorting, 而h-index的定义运用的很巧妙. +- Read more about actual bucket sort: https://en.wikipedia.org/wiki/Bucket_sort + +``` +/* +Given an array of citations (each citation is a non-negative integer) of a researcher, +write a function to compute the researcher's h-index. + +According to the definition of h-index on Wikipedia: "A scientist has index h if h of his/her N papers +have at least h citations each, +and the other N − h papers have no more than h citations each." + +For example, given citations = [3, 0, 6, 1, 5], which means the researcher has 5 papers in total and +each of them had received 3, 0, 6, 1, 5 citations respectively. +Since the researcher has 3 papers with at least 3 citations each and the remaining two with no more than 3 citations each, his h-index is 3. + +Note: If there are several possible values for h, the maximum one is taken as the h-index. + +Hint: + +An easy approach is to sort the array first. +What are the possible values of h-index? +A faster approach is to use extra space. + +Google Facebook +Hide Tags Hash Table Sort +Hide Similar Problems (M) H-Index II + +*/ + +/* + Thoughts:O(nlogn) + N = 5, so max of h = 5. min of h = 0. + 1. h = 5, loop through the array and count the # of citations that are >= h. + 2 .h = 4 ... h=1, h=0. + => O(n^2). + + If sort it : 0,1,3,5,6 + Find find index x = N - h, and arr[x] >= h + that becomes find index x that arr[x] >=h ,where h = N - x. + Foor loop on h, O(n) + pick x = N - h, and check if arr[x] >= h + h = 5, x = 5 -5 = 0. arr[x] = 0 < h. not working + h = 4, x = 5 - 4 = 1. arr[x] = 1. no. + h=3,x=5-3 =2,arr[x]=3 3>=3, okay. + nLogn + N = O(nlogn) + + +*/ +// 正向思考 +public class Solution { + public int hIndex(int[] citations) { + if (citations == null || citations.length == 0) { + return 0; + } + int n = citations.length; + Arrays.sort(citations); // nlogn + for (int i = 0; i < n; i++) { + int h = n - i; + if (citations[i] >= h) { + return h; + } + } + return 0; + } +} + +// 反向思考 +public class Solution { + public int hIndex(int[] citations) { + if (citations == null || citations.length == 0) { + return 0; + } + Arrays.sort(citations); // nlogn + for (int h = citations.length; h > 0; h--) { + int x = citations.length - h; + if (citations[x] >= h) { + return h; + } + } + return 0; + } +} + + + +/* + Thoughts: as the hint shows, use extra space and make it faster. + citations = [3, 0, 6, 1, 5], + (http://buttercola.blogspot.com/2015/09/leetcode-h-index.html?_sm_au_=iHVFjb76ZHj7ND5D) + 1. For loop to count++ in correct buttkit regardless of the index. + If arr[x] <= n, then bucket[arr[x]]++. that means, the bucket with index of arr[x] should store this arr[x] element. + If arr[x] > n, well, that means it exceeds bucket.length (in this application, it means it's already greater than the max of h=n) + so let's just put it in bucket[n]++. + Obvisouly, we need bucket[n + 1] + 2. Each bucket slot now stores #of values that's >= it's index h, in ascending order ofcourse. + so we can do another for loop, to sum one by one, from h = n ~ 0, (because we need higest possible h) + if sum >= h, that is the rst. +*/ +public class Solution { + public int hIndex(int[] citations) { + if (citations == null || citations.length == 0) { + return 0; + } + int n = citations.length; + int[] bucket = new int[n + 1]; // bucket[n] stores paper with more than n citations. + //fill bucket + for (int i = 0; i < n; i++) { + int bucketSlot = citations[i]; + if (bucketSlot <= n) bucket[bucketSlot]++; + else bucket[n]++; //bucketSlot > n + } + + //Find best H + int sum = 0; + for (int h = n; h >= 0; h--) { + sum += bucket[h]; + if (sum >= h) return h; // h-index definition: # of papers (sum of bucket[n]...bucket[0]) has more than h cidations + } + return 0; + } +} + +``` diff --git a/Java/Hamming Distance.java b/Java/Hamming Distance.java new file mode 100644 index 0000000..13ada30 --- /dev/null +++ b/Java/Hamming Distance.java @@ -0,0 +1,44 @@ +E + +bit: XOR, &, shift>> + +``` +/* +The Hamming distance between two integers is the number of positions at which the corresponding bits are different. + +Given two integers x and y, calculate the Hamming distance. + +Note: +0 ≤ x, y < 231. + +Example: + +Input: x = 1, y = 4 + +Output: 2 + +Explanation: +1 (0 0 0 1) +4 (0 1 0 0) + ↑ ↑ + +The above arrows point to positions where the corresponding bits are different. +*/ + +/* +Thoughts: +^XOR: gives out all the different bits. +Count the 1's by shiftting right and & 1 +*/ +class Solution { + public int hammingDistance(int x, int y) { + int xor = x ^ y; + int count = 0; + while (xor != 0) { + count += xor & 1; + xor = xor >> 1; + } + return count; + } +} +``` \ No newline at end of file diff --git a/Java/Happy Number.java b/Java/Happy Number.java new file mode 100755 index 0000000..11a7bed --- /dev/null +++ b/Java/Happy Number.java @@ -0,0 +1,87 @@ +E + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + +``` +/* +Write an algorithm to determine if a number is happy. + +A happy number is a number defined by the following process: +Starting with any positive integer, replace the number by the sum of the squares of its digits, +and repeat the process until the number equals 1 (where it will stay), +or it loops endlessly in a cycle which does not include 1. +Those numbers for which this process ends in 1 are happy numbers. + +Example +19 is a happy number + +1^2 + 9^2 = 82 +8^2 + 2^2 = 68 +6^2 + 8^2 = 100 +1^2 + 0^2 + 0^2 = 1 +Tags Expand +Hash Table Mathematics +*/ +/* +User HashMap/HashSet to record the calculated number +*/ +class Solution { + public boolean isHappy(int n) { + final Set set = new HashSet<>(); + long sum = n; + while (sum != 1) { + sum = calculateTheSum(sum); + if (set.contains(sum)) { + return false; + } else { + set.add(sum); + } + } + return true; + } + + private long calculateTheSum(long n) { + long result = 0; + while (n != 0) { + result += (n % 10)*(n % 10); + n = n / 10; + } + return result; + } +} + +/* + Thoughts: + Try some examples then find out: if it's not happy number, the 'sum of square of its digits' will + repeatedly occur. Use hashset to track existance. +*/ +public class Solution { + public boolean isHappy(int n) { + if (n <= 0) { + return false; + } + long sum = n; + HashSet set = new HashSet(); + while (sum != 1) { + String s = String.valueOf(sum); + sum = 0; + for (char c : s.toCharArray()){ + sum += (c-'0')*(c-'0'); + } + if (set.contains(sum)) { + return false; + } else { + set.add(sum); + } + } + return true; + } +} + + + + + +``` \ No newline at end of file diff --git a/Java/Hash Function.java b/Java/Hash Function.java old mode 100644 new mode 100755 index ba32154..16b9cd2 --- a/Java/Hash Function.java +++ b/Java/Hash Function.java @@ -1,3 +1,23 @@ +E +1525761641 +tags: Hash Table + +#### Hash Function +- 解释Hash怎么做. +- Hash function例子: +- hashcode("abcd") = (ascii(a) * 33^3 + ascii(b) * 33^2 + ascii(c) *33^1 + ascii(d)*33^0) % HASH_SIZE +- 用到的参数比如: magic number 33, HASH_SIZE. + +- Hash的意义是:给一个string key, 转换成数字,从而把size变得更小。 +- 真实的implementation还要处理collision, 可能需要design hash function 等等。 + + +##### 每一步都%HASH_SIZE的原因 +- hashRst = hashRst * 33 + (int)(key[i]); +- hashRst = hashRst % HASH_SIZE; +- 原因是,hashRst会变得太大,所以不能算完和 再 %... + +``` /* In data structure Hash, hash function is used to convert a string(or any other type) into an integer smaller than hash size and bigger or equal to zero. The objective of @@ -8,7 +28,7 @@ In data structure Hash, hash function is used to convert a string(or any other t hashcode("abcd") = (ascii(a) * 33^3 + ascii(b) * 33^2 + ascii(c) *33^1 + ascii(d)*33^0) % HASH_SIZE - = (97* 333 + 98 * 332 + 99 * 33 +100) % HASH_SIZE + = (97* 33^3 + 98 * 33^2 + 99 * 33 +100) % HASH_SIZE = 3595978 % HASH_SIZE @@ -29,12 +49,15 @@ In data structure Hash, hash function is used to convert a string(or any other t Tags Expand Hash Table +*/ + +/* Thinking process: Use given hash function. However, need to consider integer overflow. A simple way: save it as a long during calculation. Then return a (int). -*/ +*/ class Solution { /** * @param key: A String you should hash @@ -54,3 +77,5 @@ public int hashCode(char[] key, int HASH_SIZE) { } }; + +``` \ No newline at end of file diff --git a/Java/HashHeap.java b/Java/HashHeap.java new file mode 100755 index 0000000..12bc24e --- /dev/null +++ b/Java/HashHeap.java @@ -0,0 +1,179 @@ +H +1532965421 +tags: HashHeap, Heap + +非题.是从九章找来的HashHeap implementation. + +``` +/** + * 本代码由九章算法编辑提供。没有版权欢迎转发。 + * - 九章算法致力于帮助更多中国人找到好的工作,教师团队均来自硅谷和国内的一线大公司在职工程师。 + * - 现有的面试培训课程包括:九章算法班,系统设计班,BAT国内班 + * - 更多详情请见官方网站:http://www.jiuzhang.com/ + */ + +class HashHeap { + ArrayList heap; + String mode; + int size_t; + HashMap hash; + + class Node { + public Integer id; + public Integer num; + + Node(Node now) { + id = now.id; + num = now.num; + } + + Node(Integer first, Integer second) { + this.id = first; + this.num = second; + } + } + + public HashHeap(String mod) { // 传入min 表示最小堆,max 表示最大堆 + // TODO Auto-generated constructor stub + heap = new ArrayList(); + mode = mod; + hash = new HashMap(); + size_t = 0; + } + + int peak() { + return heap.get(0); + } + + int size() { + return size_t; + } + + Boolean empty() { + return (heap.size() == 0); + } + + int parent(int id) { + if (id == 0) { + return -1; + } + return (id - 1) / 2; + } + + int lson(int id) { + return id * 2 + 1; + } + + int rson(int id) { + return id * 2 + 2; + } + + boolean comparesmall(int a, int b) { + if (a <= b) { + if (mode == "min") + return true; + else + return false; + } else { + if (mode == "min") + return false; + else + return true; + } + + } + + void swap(int idA, int idB) { + int valA = heap.get(idA); + int valB = heap.get(idB); + + int numA = hash.get(valA).num; + int numB = hash.get(valB).num; + hash.put(valB, new Node(idA, numB)); + hash.put(valA, new Node(idB, numA)); + heap.set(idA, valB); + heap.set(idB, valA); + } + + Integer poll() { + size_t--; + Integer now = heap.get(0); + Node hashnow = hash.get(now); + if (hashnow.num == 1) { + swap(0, heap.size() - 1); + hash.remove(now); + heap.remove(heap.size() - 1); + if (heap.size() > 0) { + siftdown(0); + } + } else { + hash.put(now, new Node(0, hashnow.num - 1)); + } + return now; + } + + void add(int now) { + size_t++; + if (hash.containsKey(now)) { + Node hashnow = hash.get(now); + hash.put(now, new Node(hashnow.id, hashnow.num + 1)); + } else { + heap.add(now); + hash.put(now, new Node(heap.size() - 1, 1)); + } + + siftup(heap.size() - 1); + } + + void delete(int now) { + size_t--; + + Node hashnow = hash.get(now); + int id = hashnow.id; + int num = hashnow.num; + if (hashnow.num == 1) { + + swap(id, heap.size() - 1); + hash.remove(now); + heap.remove(heap.size() - 1); + if (heap.size() > id) { + siftup(id); + siftdown(id); + } + } else { + hash.put(now, new Node(id, num - 1)); + } + } + + void siftup(int id) { + while (parent(id) > -1) { + int parentId = parent(id); + if (comparesmall(heap.get(parentId), heap.get(id)) == true) { + break; + } else { + swap(id, parentId); + } + id = parentId; + } + } + + void siftdown(int id) { + while (lson(id) < heap.size()) { + int leftId = lson(id); + int rightId = rson(id); + int son; + if (rightId >= heap.size() || (comparesmall(heap.get(leftId), heap.get(rightId)) == true)) { + son = leftId; + } else { + son = rightId; + } + if (comparesmall(heap.get(id), heap.get(son)) == true) { + break; + } else { + swap(id, son); + } + id = son; + } + } +} +``` \ No newline at end of file diff --git a/Java/HashWithArray.java b/Java/HashWithArray.java old mode 100644 new mode 100755 index 5b03fa9..46bb835 --- a/Java/HashWithArray.java +++ b/Java/HashWithArray.java @@ -1,3 +1,7 @@ +E + + +``` /* Self Test: Implement HashTable with just array and integer. @@ -62,8 +66,4 @@ public int get(int key) { - - - - - +``` \ No newline at end of file diff --git a/Java/HashWithCustomizedClass(LinkedList).java b/Java/HashWithCustomizedClass(LinkedList).java new file mode 100755 index 0000000..d528669 --- /dev/null +++ b/Java/HashWithCustomizedClass(LinkedList).java @@ -0,0 +1,176 @@ +M +1527991732 +tags: Hash Table + +练习HashMap with customized class. functions: get(), put(), getRandom() + +#### Hash Table +- store map as array: `Entry[] table;` +- store entry as linked list: `public Entry(K key, V value, Entry next)` +- compute hashKey: `Math.abs(key.hashCode()) % this.capacity` +- Handle collision: +- 1. Check if duplicate (matching key), if so, replace and return +- 2. Check through the linked list, find find duplicate (matching key), replace and return. +- 3. If no duplicate, add the entry to the tail +- Find item: compute hashKey -> find linked list -> iterate over list to find a matching key. + +``` +/* +Build Hashtable datastructure with three methods: set, get, getrandom. + +Note1: getrandom needs to pick evenly/uniformaly distributed entry. + +Note2: to be realistic, hashtable should be able to handle collision, that's where linkedlist comes into play. + +Note3: use default object.hashcode() function to get hash code, then apply hashcode % table size + + +More about O(1) random access + If no collision, uniform random access is easy. + +With collision, need to figure our how to access element on the linkedlist with O(1), but it's unlikely. +So, like Java HashMap, we can implement rehashing. +Bascially, when map size increase to over capacity, double the capacity. + +*/ + +import java.io.*; +import java.util.*; + +public class CHashMap { + public int capacity; + public int count; + public Entry[] table; + + public class Entry { + public V value; + public K key; + public Entry next; + + public Entry(K key, V value, Entry next) { + this.value = value; + this.key = key; + this.next = next; + } + } + + + public CHashMap() { + this.capacity = 16; + this.table = new Entry[this.capacity]; + this.count = 0; + } + + public CHashMap(int capacity) { + this.capacity = capacity; + this.table = new Entry[this.capacity]; + this.count = 0; + } + + public V get(K key) { + if (key == null) { + return null; + } + int hashedKey = hashFunction(key); + if (table[hashedKey] != null) { + Entry node = table[hashedKey]; + while (node != null) { + if (node.key.equals(key)) { + return node.value; + } + node = node.next; + } + } + return null; + } + + public void put(K key, V value) { + if (key == null) { + return; + } + this.count++; + Entry entry = new Entry(key, value, null); + int hashedKey = hashFunction(key); + if (table[hashedKey] == null) { + table[hashedKey] = entry; + } else { + // Handle collision + Entry node = table[hashedKey]; + if (node.key.equals(key)) { // replace entry if key matches + table[hashedKey] = entry; + entry.next = node.next; + return; + } + while (node.next != null) { + if (node.next.key.equals(key)) { // replace entry if key matches + Entry temp = node.next; + node.next = entry; + entry.next = temp.next; + return; + } + node = node.next; + } + node.next = entry; + } + } + + public int hashFunction (K key) { + return Math.abs(key.hashCode()) % this.capacity; + } + + public void display() { + for (int i = 0; i < table.length; i++) { + Entry node = table[i]; + StringBuffer sb = new StringBuffer(); + while (node != null) { + sb.append("[key: " + node.key + ", value: " + node.value + "], "); + node = node.next; + } + if (sb.length() != 0) + System.out.println(sb.toString()); + } + System.out.println(); + } + + /* + If no collision, uniform random access is easy. + + With collision, need to figure our how to access element on the linkedlist with O(1), but it's unlikely. + + */ + public V getRandom() { + Random rd = new Random(); + int hashedKey = hashFunction(rd.nextInt(this.capacity)); + return table[hashedKey]; + } + + + public static void main(String[] args) { + CHashMap map = new CHashMap(2); + + System.out.println("TEST SET------"); + map.put('a', "1st string"); + map.put('b', "2nd string!"); + map.display(); + map.put('a', "wowo change"); + map.display(); + + + System.out.println("TEST PUT------"); + System.out.println(map.get('a')); + System.out.println(map.get('c')); + map.put('c', "okay test c"); + System.out.println(map.get('c')); + map.display(); + + System.out.println("TEST COLLISION------"); + map.put('d', "test d"); + map.put('e', "test E"); + map.display(); + + + //test getrandom + + } +} +``` \ No newline at end of file diff --git a/Java/Heapify.java b/Java/Heapify.java old mode 100644 new mode 100755 index f4f17fe..d52bbda --- a/Java/Heapify.java +++ b/Java/Heapify.java @@ -1,13 +1,33 @@ -Heapify里面的siftdown的部分: - 只能从for(i = n/2-1 ~ 0), 而不能从for(i = 0 ~ n/2 -1) - 这是不是因为siftdown每次只顺脚下的孩子,所以必须中间开花,向上跑的时候才能确保脚下是符合heap规则的 +M +1533162679 +tags: Heap, MinHeap + +Turn unsorted array into a min-heap array, where for each A[i], + +A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i]. + +#### Heap +- Heap用的不多. 得用一下, 才好理解. 通常default 的PriorityQueue就是给了一个现成的min-heap: +- 所有后面的对应element都比curr element 小。 +- Heapify里面的**siftdown**的部分: +- 只能从for(i = n/2-1 ~ 0), 而不能从for(i = 0 ~ n/2 -1): 必须中间开花,向上跑的时候才能确保脚下是符合heap规则的 + +#### Heapify/SiftDown做了什么? +- 确保在heap datastructure里面curr node下面的两个孩子,以及下面所有的node都遵循一个规律 +- 比如在这里,若是min-heap,就是后面的两孩子都要比自己大。若不是,就要swap。 + +#### min-heap的判断规律: +- for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. +- siftdown时:在curr node和两个child里面小的比较。如果的确curr < child, 搞定,break while. +- 但若curr 并不比child小,那么就要换位子,而且继续从child的位子往下面盘查。 ``` /* Given an integer array, heapify it into a min-heap array. -For a heap array A, A[0] is the root of heap, and for each A[i], A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i]. -Have you met this question in a real interview? Yes +For a heap array A, A[0] is the root of heap, and for each A[i], A[i * 2 + 1] is the left child of A[i] +and A[i * 2 + 2] is the right child of A[i]. + Example Given [3,2,1,4,5], return [1,2,3,4,5] or any legal heap array. @@ -15,12 +35,15 @@ O(n) time complexity Clarification -What is heap? -Heap is a data structure, which usually have three methods: push, pop and top. where "push" add a new element the heap, "pop" delete the minimum/maximum element in the heap, "top" return the minimum/maximum element. +What is heap? +Heap is a data structure, which usually have three methods: push, pop and top. +where "push" add a new element the heap, "pop" delete the minimum/maximum element in the heap, +"top" return the minimum/maximum element. What is heapify? -Convert an unordered integer array into a heap array. If it is min-heap, for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. +Convert an unordered integer array into a heap array. +If it is min-heap, for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. What if there is a lot of solutions? Return any of them. @@ -31,7 +54,7 @@ /* Thoughts: -Based on the knowledge of Hash Heap: http://www.jiuzhang.com/solutions/hash-heap/ +Based on the knowledge of Hash Heap: http://www.jiuzhang.com/solutions/hashheap/ Try to implement part of the Heap basis, heapify. In this problem, re-organize the input array to fit heap basis @@ -43,30 +66,60 @@ always swap with the smaller child As long as left.child.i < array length, continue while: If no right child, or left.val < right.val, - son = left. + child = left. else - son = right -Check if curr.val < son.val + child = right +Check if curr.val < child.val if so, break, we are good. - If not, swap(curr,son) -curr = son, and move on the next round of while + If not, swap(curr,child) +curr = child, and move on the next round of while NOTE: The for loop start from i = n/2 -1, which makes the right-most index = 2*(n/2-1) + 1 = n - 2 + 1 = n-1. */ +// 简化版 +public class Solution { + public void heapify(int[] nums) { + if (nums == null || nums.length == 0) return; + + int n = nums.length; + int curr = 0, left = 0, right = 0, child = 0; + + for (int i = n/2 - 1; i >= 0; i--) { // [ n/2 -1, 0 ] + curr = i; + while (curr * 2 + 1 < n) { + // pick feasible child. + left = curr * 2 + 1; + right = curr * 2 + 2; + // Pick nums[left] < nums[right], if later curr < nums[left], then curr < nums[right] as well + child = (right >= n || nums[left] <= nums[right]) ? left : right; + if (nums[curr] <= nums[child]) { // meets min-heap requirement + break; + } else { + swap(nums, curr, child); + } + // check all children if applicable + curr = child; + }//end while + } + } + + private void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} +// Original public class Solution { - /** - * @param A: Given an integer array - * @return: void - */ public void heapify(int[] A) { if (A == null || A.length == 0) { return; } - int son = 0; + int child = 0; int currId = 0; int leftId = 0; int rightId = 0; @@ -77,18 +130,18 @@ public void heapify(int[] A) { leftId = currId * 2 + 1; rightId = currId * 2 + 2; if (rightId >= n || A[leftId] <= A[rightId]) { - son = leftId; + child = leftId; } else { - son = rightId; + child = rightId; } - if (A[currId] <= A[son]) { + if (A[currId] <= A[child]) { break; } else { int temp = A[currId]; - A[currId] = A[son]; - A[son] = temp; + A[currId] = A[child]; + A[child] = temp; } - currId = son; + currId = child; }//end while }//end for @@ -99,14 +152,4 @@ public void heapify(int[] A) { - - - - - - - - - - ``` \ No newline at end of file diff --git a/Java/Heaters.java b/Java/Heaters.java new file mode 100644 index 0000000..a5ad05d --- /dev/null +++ b/Java/Heaters.java @@ -0,0 +1,73 @@ +E + +第一步: +生题型, 理解题意需要时间: +从字面和画图而言, 就是定住房子一个个过,房子左右的distance需要足够达到heater. 目标是招尽可能小的radius, 所以house和heater紧贴着是必要的. +在for loop里面定下house,把heater当作一个区间移动, 达到的第一个合适区间,这就是当下最小的理想radius,取这个值跟既定radius作比较。 +比较之后,继续移动house,再试着移动heater区间去match。 + +第二步: +Binary Search + +注意! +题目没有说given array是否sort, 我们必须sort才能够区间移动或者binary search. +TODO: +http://www.cnblogs.com/grandyang/p/6181626.html + +``` +/* +Winter is coming! Your first job during the contest is to design a standard heater with fixed warm radius to warm all the houses. + +Now, you are given positions of houses and heaters on a horizontal line, find out minimum radius of heaters so that all houses could be covered by those heaters. + +So, your input will be the positions of houses and heaters seperately, and your expected output will be the minimum radius standard of heaters. + +Note: +Numbers of houses and heaters you are given are non-negative and will not exceed 25000. +Positions of houses and heaters you are given are non-negative and will not exceed 10^9. +As long as a house is in the heaters' warm radius range, it can be warmed. +All the heaters follow your radius standard and the warm radius will the same. +Example 1: +Input: [1,2,3],[2] +Output: 1 +Explanation: The only heater was placed in the position 2, and if we use the radius 1 standard, then all the houses can be warmed. +Example 2: +Input: [1,2,3,4],[1,4] +Output: 1 +Explanation: The two heater was placed in the position 1 and 4. We need to use radius 1 standard, then all the houses can be warmed. + +*/ + +/* +Thoughts: +When a house sits in between heaters, the diff between left-heater-to-house && house-to-right-heater should at least equal. +Also, because we are finding the smallest sutable radius, we'll always compare heater adjacent to the house. Therefore, we can loop over house, then move heater position to find correct covering. + +Follow the above rule: +1. Loop over houses. +2. Start radius = 0, and find the minmimum feasible (when comparing, pick the larger distance becuase it'll cover both side of house) +3. Calculate abs value of distance, because house can be ahead of 1st heater, in middle of two heaters or after last heater. +4. Keep while loop for each heater, until the house-to-right-heater distance is at least equal to the left-heater-to-house. +*/ + +class Solution { + public int findRadius(int[] houses, int[] heaters) { + int heaterIndex = 0; + int radius = 0; + int heatersLength = heaters.length; + Arrays.sort(houses); + Arrays.sort(heaters); + for (int i = 0; i < houses.length; i++) { + int housePos = houses[i]; + // Keeps looping until this position status: leftHeaterPos - housePos - rightHeaterPos, and the diff of two direction is at least equal + // when equal, heaterIndex+1 beacuse it will be used to calculate the minimum feasible distance right after + while (heaterIndex < heatersLength - 1 && Math.abs(heaters[heaterIndex + 1] - housePos) <= Math.abs(heaters[heaterIndex] - housePos)) { + heaterIndex++; + } + // heaterPos will be exactly the one after current housePos, so use this as the radius + radius = Math.max(radius, Math.abs(heaters[heaterIndex] - housePos)); + } + return radius; + } +} +``` \ No newline at end of file diff --git a/Java/House Robber II.java b/Java/House Robber II.java new file mode 100755 index 0000000..ee437b9 --- /dev/null +++ b/Java/House Robber II.java @@ -0,0 +1,213 @@ +M +1523329114 +tags: DP, Sequence DP, Status DP + +和House Robber I 类似, 搜刮房子, 相邻不能动. 特点是: 现在nums排成了圈, 首尾相连. + +#### Sequence DP +- dp[i][status]: 在 status=[0,1] 情况下, 前i个 房子拿到的 max rob gain. status=0, 1st house robbed; status=1, 1st house skipped +- 根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]); +- 特别的是,末尾的last house 和 first house相连. 这里就需要分别讨论两种情况: 第一个房子被搜刮, 或者第一个房子没被搜刮 +- be careful with edge case nums = [0], only with 1 element. +- Time,space: O(n) + +#### 两个状态 +- 是否搜刮了第一个房子, 分出两个branch, 可以看做两种状态. +- 可以考虑用两个DP array; 也可以加一dp维度, 补充这个状态. +- 连个维度表示的是2种状态(1st house being robbed or not); 这两种状态是平行世界的两种状态, 互不相关. + +#### Rolling array +- 与House Robber I一样, 可以用%2 来操作rolling array, space reduced to O(1) + +``` +/* +Note: This is an extension of House Robber. + +After robbing those houses on that street, the thief has found himself a new place for his thievery +so that he will not get too much attention. This time, all houses at this place are arranged in a circle. +That means the first house is the neighbor of the last one. +Meanwhile, the security system for these houses remain the same as for those in the previous street. + +Given a list of non-negative integers representing the amount of money of each house, +determine the maximum amount of money you can rob tonight without alerting the police. + +Credits: +Special thanks to @Freezen for adding this problem and creating all test cases. + +Subscribe to see which companies asked this question + + + Dynamic Programming +Hide Similar Problems (E) House Robber (M) Paint House (E) Paint Fence (M) House Robber III + +*/ + +// Sequence DP, new dp[n + 1][2] +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } else if (nums.length == 1) { + return nums[0]; + } + int n = nums.length; + long[][] dp = new long[n + 1][2]; + dp[0][0] = 0; // not picking any number + dp[1][0] = 0; // not picking nums[0] + dp[1][1] = nums[0]; // pick nums[0] + + for (int i = 2; i < n; i++) { // skip the (i = n) case + dp[i][0] = Math.max(dp[i - 1][0], dp[i - 2][0] + nums[i - 1]); + dp[i][1] = Math.max(dp[i - 1][1], dp[i - 2][1] + nums[i - 1]); + } + // i = n; + dp[n][0] = Math.max(dp[n - 1][0], dp[n - 2][0] + nums[n - 1]); + dp[n][1] = dp[n - 1][1]; // because the next-connected dp[0][1] was picked + + return (int) Math.max(dp[n][0], dp[n][1]); + } +} + +// rolling array, O(1) space +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } else if (nums.length == 1) { + return nums[0]; + } + int n = nums.length; + long[][] dp = new long[2][2]; + dp[0][0] = 0; // not picking any number + dp[1][0] = 0; // not picking nums[0] + dp[1][1] = nums[0]; // pick nums[0] + + for (int i = 2; i < n; i++) { // spare the (i = n) case + dp[i % 2][0] = Math.max(dp[(i - 1) % 2][0], dp[(i - 2) % 2][0] + nums[i - 1]); + dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], dp[(i - 2) % 2][1] + nums[i - 1]); + } + // i = n; + dp[n % 2][0] = Math.max(dp[(n - 1) % 2][0], dp[(n - 2) % 2][0] + nums[n - 1]); + dp[n % 2][1] = dp[(n - 1) % 2][1]; + + return (int) Math.max(dp[n % 2][0], dp[n % 2][1]); + } +} +/* +Thougths: +If there is a circle, which give 2 branches: index i == 0 was robbed, or index i == 0 was not robbed. +Create the 2nd dimension for this status. dp[i][j], where j = 0 or 1. +dp[i][0]: if index i = 0 was not robbed, what's the max at i. +dp[i][1]: if index i = 0 was robbed, what's the max at i. +Note: +1. Deal with final position with extra care. +2. Initialize the dp carefully +*/ +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } else if (nums.length == 1) { + return nums[0]; + } + int n = nums.length; + int[][] dp = new int[n][2]; + // index i = 0, not robbed + dp[0][0] = 0; + dp[1][0] = nums[1]; + // index i = 0, robbed + dp[0][1] = nums[0]; + dp[1][1] = dp[0][1]; + + for (int i = 2; i < n; i++) { + if (i == n - 1) { + dp[i][0] = Math.max(dp[i - 1][0], dp[i -2][0] + nums[i]); + dp[i][1] = dp[i - 1][1]; + } else { + dp[i][0] = Math.max(dp[i - 1][0], dp[i -2][0] + nums[i]); + dp[i][1] = Math.max(dp[i - 1][1], dp[i -2][1] + nums[i]); + } + } + return Math.max(dp[n - 1][0], dp[n - 1][1]); + } +} +/* +Thougths: +Rolling array: +index [i] is only calculated based on prior [i - 1] and [i - 2]. +[i - 1] and [i - 2] can be distinguished by using %2. +*/ +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } else if (nums.length == 1) { + return nums[0]; + } + int n = nums.length; + int[][] dp = new int[2][2]; + // index i = 0, not robbed + dp[0][0] = 0; + dp[1][0] = nums[1]; + // index i = 0, robbed + dp[0][1] = nums[0]; + dp[1][1] = dp[0][1]; + + for (int i = 2; i < n; i++) { + if (i == n - 1) { + dp[i % 2][0] = Math.max(dp[(i - 1) % 2][0], dp[(i - 2) % 2][0] + nums[i]); + dp[i % 2][1] = dp[(i - 1) % 2][1]; + } else { + dp[i % 2][0] = Math.max(dp[(i - 1) % 2][0], dp[(i - 2) % 2][0] + nums[i]); + dp[i % 2][1] = Math.max(dp[(i - 1) % 2][1], dp[(i - 2) % 2][1] + nums[i]); + } + } + return Math.max(dp[(n - 1) % 2][0], dp[(n - 1) % 2][1]); + } +} + +/* + Each house depends on front and back houses + Two possible case for the last house: rob or not robbed. So we can do two for loop, then compare the + two differnet future. +*/ +public class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } else if (nums.length == 1) { + return nums[0]; + } else if (nums.length == 2) { + return Math.max(nums[0], nums[1]); + } + + int n = nums.length; + + //Last house not robbed + int[] dp1 = new int[n]; + dp1[0] = nums[0]; + dp1[1] = Math.max(nums[0], nums[1]); + for (int i = 2; i < n - 1; i++) { + dp1[i] = Math.max(dp1[i - 1], dp1[i - 2] + nums[i]); + } + dp1[n - 1] = dp1[n - 2]; + + //Last house robbed + int[] dp2 = new int[n]; + dp2[0] = 0; + dp2[1] = nums[1]; + for (int i = 2; i < n - 2; i++) { + dp2[i] = Math.max(dp2[i - 1], dp2[i - 2] + nums[i]); + } + dp2[n - 1] = dp2[n - 3] + nums[n - 1]; + + //Compare + return Math.max(dp2[n - 1], dp1[n - 1]); + } +} + + + + + +``` \ No newline at end of file diff --git a/Java/House Robber III.java b/Java/House Robber III.java new file mode 100755 index 0000000..0e45734 --- /dev/null +++ b/Java/House Robber III.java @@ -0,0 +1,186 @@ +M +1523330238 +tags: Tree, DFS, DP, Status DP + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + +``` +/* +The thief has found himself a new place for his thievery again. +There is only one entrance to this area, called the "root." +Besides the root, each house has one and only one parent house. +After a tour, the smart thief realized that "all houses in this place forms a binary tree". +It will automatically contact the police if two directly-linked houses were broken into on the same night. + +Determine the maximum amount of money the thief can rob tonight without alerting the police. + +Example 1: + 3 + / \ + 2 3 + \ \ + 3 1 +Maximum amount of money the thief can rob = 3 + 3 + 1 = 7. + +Example 2: + 3 + / \ + 4 5 + / \ \ + 1 3 1 +Maximum amount of money the thief can rob = 4 + 5 = 9. + +Subscribe to see which companies asked this question + + + Tree Depth-first Search +Hide Similar Problems (E) House Robber (M) House Robber II + + +*/ + +/* +Thoughts: +There are lots of redundant calculations in this particular dfs. +Let: +- dp[0] represent: max value when you do not pick up root i. +- dp[1] represent: max value when you do pick up root i. + +The logic break down should be the same as in DFS. + +This is different from regular DP, where we have a global dp[][]. +Here we still have to do dp, combine with dfs. +*/ +class Solution { + public int rob(TreeNode root) { + int[] dp = dfs(root); + return Math.max(dp[0], dp[1]); + } + + public int[] dfs(TreeNode root) { + int[] dp = new int[2]; // {0, 0} + if (root == null) { + return dp; + } + int[] leftDP = dfs(root.left); + int[] rightDP = dfs(root.right); + dp[0] = Math.max(leftDP[0], leftDP[1]) + Math.max(rightDP[0], rightDP[1]); // do not pick root + dp[1] = root.val + leftDP[0] + rightDP[0]; // do pick root + return dp; + } +} + +/* +Thoughts: +DFS. Always deal with the 3 ndoes: root, left, and right. +Either of them can be picked or not picked: overeall 6 possibilities: a bit of redundant calculation +Right/Left are independent to create combination cases. +*/ +class Solution { + public int rob(TreeNode root) { + return Math.max(dfs(root, true), dfs(root, false)); + } + + public int dfs(TreeNode root, boolean visited) { + if (root == null) { + return 0; + } + if (root.left == null && root.right == null) { + return visited ? root.val : 0; + } + int leftMaxValue = 0; + int rightMaxValue = 0; + if (visited) { // If root visited, we can't use left/right children + leftMaxValue = dfs(root.left, !visited); + rightMaxValue = dfs(root.right, !visited); + return root.val + leftMaxValue + rightMaxValue; + } else { + leftMaxValue = Math.max(dfs(root.left, visited), dfs(root.left, !visited)); + rightMaxValue = Math.max(dfs(root.right, visited), dfs(root.right, !visited)); + return leftMaxValue + rightMaxValue; + } + } +} + + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +/* + 3.24.2016 + Thought: dfs should be able to handle this. +*/ +public class Solution { + public int rob(TreeNode root) { + if (root == null) { + return 0; + } else if (root.left == null && root.right == null) { + return root.val; + } + return Math.max(dfs(root,true), dfs(root, false)); + } + + public int dfs (TreeNode node, boolean visit) { + if (node.left == null && node.right == null) { + if (visit){ + return node.val; + } else { + return 0; + } + } + int left = 0; + int right = 0; + if (visit) { + if (node.left != null) { + left = dfs(node.left, !visit); + } + if (node.right != null) { + right = dfs(node.right, !visit); + } + return node.val + left + right; + } else { + if (node.left != null) { + left = Math.max(dfs(node.left, !visit), dfs(node.left, visit)); + } + if (node.right != null) { + right = Math.max(dfs(node.right, !visit), dfs(node.right, visit)); + } + return left + right; + } + + } +} + + +``` \ No newline at end of file diff --git a/Java/House Robber.java b/Java/House Robber.java old mode 100644 new mode 100755 index e021fce..ff416d7 --- a/Java/House Robber.java +++ b/Java/House Robber.java @@ -1,7 +1,23 @@ -最基本的dp。 -看前一个或前两个的情况,再总和考虑当下的。 -思考的适合搞清楚当下的和之前的情况的关系。 -滚动数组的优化,就是确定了是这类“只和前一两个位子“相关的Fn而推出的。 +E +1523329104 +tags: DP, Sequence DP +time: O(n) +space: O(n) or rolling array O(1) + +搜刮房子, 相邻的不能碰. 每个房子里有value, 求max. + +#### Sequence DP +- dp[i]: 前i个房子拿到的max gain +- 看最后结尾状态的前一个或前两个的情况,再综合考虑当下的 +- 搞清楚当下[i]的和之前[i-x]的情况的关系: 不可以连着house, 那么就直接考虑 dp[i-2]的情况 +- Sequence DP, new dp[n + 1]; + +#### Rolling Array +- [i]'只和前两个位子 [i-1], [i - 2]'相关 +- 用%2来标记 [i], [i - 1], [i - 2]三个位置. +- 其他滚动时惯用curr/prev来表示坐标, 这里%2虽然抽象, 但是更加实用. + + ``` /* You are a professional robber planning to rob houses along a street. @@ -13,7 +29,7 @@ Given a list of non-negative integers representing the amount of money of each house, determine the maximum amount of money you can rob tonight without alerting the police. -Have you met this question in a real interview? Yes + Example Given [3, 8, 4], return 8. @@ -24,12 +40,65 @@ Dynamic Programming */ + +// DP[i]: max value for first i items; new dp[n + 1]; +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + long[] dp = new long[n + 1]; + dp[0] = 0; + dp[1] = nums[0]; + for (int i = 2; i <= n; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i - 1]); + } + return (int) dp[n]; + } +} +/* +Thoughts: +MAX, think about DP. +DP[i]: max sum at index i. +Either house i is robbed or not: dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]) +Init: dp[0] = nums[0]; dp[1] = Math.max(nums[0], nums[1]) +*/ +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } else if (nums.length == 1) { + return nums[0]; + } + int n = nums.length; + int[] dp = new int[n]; + dp[0] = nums[0]; + dp[1] = Math.max(nums[0], nums[1]); + + for(int i = 2; i < n; i++) { + dp[i] = Math.max(dp[i - 1], dp[i - 2] + nums[i]); + } + return dp[n - 1]; + } +} + +/* + Should be able to do with recursive way. + dfs(nums, index, robFlag, sum) + Based on robFlag (true/false), this recursive level will be limited weather we can pick the current + house or no. Update robFlag, sum, index and go into next level of dfs. +*/ + + + + /* Thoughts: dp[i]: the best we can rob by i. If I'm at house i, I'll either pick i or not pick i. Pick i: dp[i-2] + A[i] - Not pick i: dp[i+1] + Not pick i: dp[i-1] fn: dp[i] = Math.max(dp[i-1], dp[i-2] + A[i]) Init: @@ -96,9 +165,4 @@ public long houseRobber(int[] A) { - - - - - ``` \ No newline at end of file diff --git a/Java/Implement Queue using Stacks.java b/Java/Implement Queue using Stacks.java new file mode 100644 index 0000000..109be6d --- /dev/null +++ b/Java/Implement Queue using Stacks.java @@ -0,0 +1,197 @@ +E +1520797719 +tags: Stack, Design + +#### 双Stack +画图, 知道最后maintain的stack是那个 reverseStack: pop(), peek(), empty() 都在这个stack上, 无需变换. +push()里面做stack和reverseStack的来回倾倒. +相比老的code, 在PUSH里面做倾倒, 更容易读. + +#### Previous notes +双Stack. 一个是等于是queue,一个是backfillStack. +Tricky: 是在pop()和peek()的时候backfill, 并且要等到stack用完再backfill. +写一下例子就知道,如果提早backfill,stack.peek()就不是queue的head了. + + +``` +/* +LeetCode: +Implement the following operations of a queue using stacks. + +push(x) -- Push element x to the back of queue. +pop() -- Removes the element from in front of queue. +peek() -- Get the front element. +empty() -- Return whether the queue is empty. +Notes: +You must use only standard operations of a stack -- which means only push to top, peek/pop from top, size, and is empty operations are valid. +Depending on your language, stack may not be supported natively. You may simulate a stack by using a list or deque (double-ended queue), as long as you use only standard operations of a stack. +You may assume that all operations are valid (for example, no pop or peek operations will be called on an empty queue). +*/ +/* +Thoughts: +Use 2 stacks: +Stack: hold items in regular stack order +ReverseStack: hold items in the queue order. + +- Add: pure from reverseStack into stack, add item, and pure back into reverseStack. +- Remove: remove from reverseStack +- peek, empty are trivial +*/ +class MyQueue { + Stack stack; + Stack reverseStack; + /** Initialize your data structure here. */ + public MyQueue() { + stack = new Stack<>(); + reverseStack = new Stack<>(); + } + + /** Push element x to the back of queue. */ + public void push(int x) { + while (!reverseStack.isEmpty()) { + stack.push(pop()); + } + stack.push(x); + + // Pure back + while (!stack.isEmpty()) { + reverseStack.push(stack.pop()); + } + } + + /** Removes the element from in front of queue and returns that element. */ + public int pop() { + return reverseStack.pop(); + } + + /** Get the front element. */ + public int peek() { + return reverseStack.peek(); + } + + /** Returns whether the queue is empty. */ + public boolean empty() { + return reverseStack.isEmpty(); + } +} + + +/* +Thoughts: +Using two stack. +Stack1 holds the correct representation of the queue: first added item appears on top of stack. +Stack2 used to insert new item, which will be like a inverse queue. +Note: Only backfill stack2 into stack1, when stack1.isEmpty() during queue.pop/queue.peek: only backfilling when regular queue is drained. +*/ +class MyQueue { + private Stack stack; + private Stack backfillStack; + /** Initialize your data structure here. */ + public MyQueue() { + stack = new Stack<>(); + backfillStack = new Stack<>(); + } + + private void backfill() { + while (!backfillStack.isEmpty()) { + stack.push(backfillStack.pop()); + } + } + + /** Push element x to the back of queue. */ + public void push(int x) { + backfillStack.push(x); + } + + /** Removes the element from in front of queue and returns that element. */ + public int pop() { + if (stack.isEmpty()) { + backfill(); + } + return stack.pop(); + } + + /** Get the front element. */ + public int peek() { + if (stack.isEmpty()) { + backfill(); + } + return stack.peek(); + } + + /** Returns whether the queue is empty. */ + public boolean empty() { + return stack.isEmpty() && backfillStack.isEmpty(); + } +} + +/** + * Your MyQueue object will be instantiated and called as such: + * MyQueue obj = new MyQueue(); + * obj.push(x); + * int param_2 = obj.pop(); + * int param_3 = obj.peek(); + * boolean param_4 = obj.empty(); + */ + + +/* +LintCode: Implement Queue by Two Stacks +As the title described, you should only use two stacks to implement a queue's actions. + +The queue should support push(element), pop() and top() where pop is pop the first(a.k.a front) element in the queue. + +Both pop and top methods should return the value of first element. + +Example +For push(1), pop(), push(2), push(3), top(), pop(), you should return 1, 2 and 2 + +Challenge +implement it by two stacks, do not use any other data structure and push, pop and top should be O(1) by AVERAGE. + +Thoughts: +1. Push everything into stack2: whatever comes in last, will be on top. +2. Pop and Top: return stack1's top element. +3. Initially, when stack1 is empty, need to reverse all stack2 and put into stack: like pouring water from cup stack2 into cup stack1. + Or:when stack1 has been top() over, pour stack2 into stack1 again: the stack2's bottom becomes stack1's top, which is correct: returning the oldest element of queue (front of queue) + +Tags Expand +LintCode Copyright Stack Queue +*/ + + +public class Solution { + private Stack stack1; + private Stack stack2; + public void pourS2ToS1(){ + while (!stack2.empty()) { + stack1.push(stack2.peek()); + stack2.pop(); + } + } + public Solution() { + stack1 = new Stack(); + stack2 = new Stack(); + } + + public void push(int element) { + stack2.push(element); + } + + public int pop() { + if (stack1.empty()) { + pourS2ToS1(); + } + return stack1.pop(); + } + + public int top() { + if (stack1.empty()) { + pourS2ToS1(); + } + return stack1.peek(); + } +} + + +``` \ No newline at end of file diff --git a/Java/Implement Stack using Queues.java b/Java/Implement Stack using Queues.java new file mode 100755 index 0000000..86dc5bd --- /dev/null +++ b/Java/Implement Stack using Queues.java @@ -0,0 +1,210 @@ +E +1525667621 +tags: Stack, Design + +如题. + +#### Queue, 倒水 +- 两个Queue,交互倒水 +- 用一个Temp做swap + +##### 做法1 +- 逻辑在push里面: +- 1. x 放q2。 +- 2. q1全部offer/append到q2. +- 3. 用一个Temp做swap q1, q2. +- q1的头,就一直是最后加进去的值. + + +##### 做法2 +- 逻辑在top()/pop()里, 每次换水,查看末尾项. + + +``` +/* +LeetCode: +Implement the following operations of a stack using queues. + +push(x) -- Push element x onto stack. +pop() -- Removes the element on top of the stack. +top() -- Get the top element. +empty() -- Return whether the stack is empty. + +Notes: +You must use only standard operations of a queue -- +which means only push to back, peek/pop from front, size, and is empty operations are valid. + +Depending on your language, queue may not be supported natively. +You may simulate a queue by using a list or deque (double-ended queue), +as long as you use only standard operations of a queue. + +You may assume that all operations are valid +(for example, no pop or top operations will be called on an empty stack). +Credits: +Special thanks to @jianchao.li.fighter for adding this problem and all test cases. +*/ + +class MyStack { + Queue queue; + Queue tempQueue; + /** Initialize your data structure here. */ + public MyStack() { + queue = new LinkedList<>(); + tempQueue = new LinkedList<>(); + } + + /** Push element x onto stack. */ + public void push(int x) { + tempQueue = queue; + queue = new LinkedList<>(); + queue.offer(x); + while (!tempQueue.isEmpty()) { + queue.offer(tempQueue.poll()); + } + } + + /** Removes the element on top of the stack and returns that element. */ + public int pop() { + return queue.poll(); + } + + /** Get the top element. */ + public int top() { + return queue.peek(); + } + + /** Returns whether the stack is empty. */ + public boolean empty() { + return queue.isEmpty(); + } +} + + +/* +Thoughts: +1. When top()/pop() on the queue, we need to consume all the items on that queue first, +then return the last item. +2. Need to save the consumed items back to the queue. +*/ +class MyStack { + private Queue queue; + private Queue tempQueue; + + /** Initialize your data structure here. */ + public MyStack() { + queue = new LinkedList<>(); + tempQueue = new LinkedList<>(); + } + + /** Find the top and backfill queue with all consumed items */ + private int findTop() { + while (queue.size() > 1) { + tempQueue.offer(queue.poll()); + } + int num = queue.poll(); + queue = tempQueue; + tempQueue = new LinkedList<>(); + return num; + } + + /** Push element x onto stack. */ + public void push(int x) { + queue.offer(x); + } + + /** Removes the element on top of the stack and returns that element. */ + public int pop() { + return findTop(); + } + + /** Get the top element. */ + public int top() { + int num = findTop(); + queue.offer(num); + return num; + } + + /** Returns whether the stack is empty. */ + public boolean empty() { + return queue.isEmpty(); + } +} + +/** + * Your MyStack object will be instantiated and called as such: + * MyStack obj = new MyStack(); + * obj.push(x); + * int param_2 = obj.pop(); + * int param_3 = obj.top(); + * boolean param_4 = obj.empty(); + */ + + +/* +LintCode: +Implement Stack by Two Queues + +Implement a stack by two queues. The queue is first in first out (FIFO). +That means you can not directly pop the last element in a queue. + +Have you met this question in a real interview? Yes +Example +push(1) +pop() +push(2) +isEmpty() // return false +top() // return 2 +pop() +isEmpty() // return true +Tags Expand +Stack Queue + +*/ + +/* + Thoughts: + 2 queue are like two cups. We are fliping water into/out between q1 and q2. + pop and top are fliping water. + Use p1 as the base. +*/ + +class Stack { + private Queue q1 = new LinkedList(); + private Queue q2 = new LinkedList(); + // Push a new item into the stack + public void push(int x) { + q1.offer(x); + } + + // Pop the top of the stack + public void pop() { + while (q1.size() > 1) { + q2.offer(q1.poll()); + } + q1.poll(); + swap(); + } + + // Return the top of the stack + public int top() { + while (q1.size() > 1) { + q2.offer(q1.poll()); + } + int rst = q1.poll(); + q2.offer(rst); + swap(); + return rst; + } + + public void swap(){ + Queue temp = q1; + q1 = q2; + q2 = temp; + } + + // Check the stack is empty or not. + public boolean isEmpty() { + return q1.isEmpty(); + } +} +``` \ No newline at end of file diff --git a/Java/Implement Stack.java b/Java/Implement Stack.java new file mode 100755 index 0000000..68b9d8d --- /dev/null +++ b/Java/Implement Stack.java @@ -0,0 +1,69 @@ +E +1525667761 +tags: Stack + +随便用一个data structure, implement stack. + +#### Stack, 先入, 后出 +- ArrayList: return/remove ArrayList的末尾项。 +- 2 Queues + +``` +/* +LintCode + +Implement Stack + +Implement a stack. You can use any data structure inside a stack except stack itself to implement it. + + +Example +push(1) +pop() +push(2) +top() // return 2 +pop() +isEmpty() // return true +push(3) +isEmpty() // return false +Tags Expand +Array Stack +*/ + +/* +Thoughts: +use arraylist and a index tracker - leng +push: add to end +pop: remove end +top: get end. +isEmpty: return length +*/ + +class Stack { + private ArrayList list = new ArrayList(); + // Push a new item into the stack + public void push(int x) { + list.add(x); + } + + // Pop the top of the stack + public void pop() { + if (list.size() > 0) { + list.remove(list.size() - 1); + } + } + + // Return the top of the stack + public int top() { + if (list.size() > 0) { + return list.get(list.size() - 1); + } + return -1; + } + + // Check the stack is empty or not. + public boolean isEmpty() { + return list.size() == 0; + } +} +``` \ No newline at end of file diff --git a/Java/Implement Trie (Prefix Tree).java b/Java/Implement Trie (Prefix Tree).java new file mode 100755 index 0000000..eadcb24 --- /dev/null +++ b/Java/Implement Trie (Prefix Tree).java @@ -0,0 +1,134 @@ +M +1520490310 +tags: Design, Trie + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- HashMap构建Trie. Trie三个Method: +- 1. Inset: 加 word +- 2. Search: 找word +- 3. StartWith: 找prefix + +##### 特点 +- 只有两条children的是binary tree. 那么多个children就是Trie +- 那么没有left/right pointer怎么找孩子? +- 用HashMap,以child的label为Key,value就是child node。 HashMap走位 + +##### 其他 +- node里的char在这是optional. 只要在每个TrieNode里面用map存储向下分布的children就好了. +- 另外有种题目,比如是跟其他种类的search相关,在结尾要return whole string,就可以在node里存一个up-to-this-point的String。 + +##### Previous Note +- 如果是遇到一个一个字查询的题,可以考虑一下。 +- 构建TrieNode的时候要注意:如何找孩子?如果是个map的话,其实就挺好走位的。 +- 而且,每个node里面的 char 或者string有时候用处不大, +- 可以为空。但是有些题目,比如在结尾要return一些什么String,就可以在end string那边存一个真的String。 + + + +``` +/** +LeetCode +Implement a trie with insert, search, and startsWith methods. + +Example: + +Trie trie = new Trie(); + +trie.insert("apple"); +trie.search("apple"); // returns true +trie.search("app"); // returns false +trie.startsWith("app"); // returns true +trie.insert("app"); +trie.search("app"); // returns true +Note: + +You may assume that all inputs are consist of lowercase letters a-z. +All inputs are guaranteed to be non-empty strings. +*/ + +/* +Thougths: +TrieNode: contains the single character, and a list of children. +Note: we will use hashmap, because child access is O(1) +*/ +class Trie { + class TrieNode { + public boolean isEnd; + public Map children; // Map is more applicable to all chars, not limited to 256 ASCII + public TrieNode() { + this.isEnd = false; + this.children = new HashMap<>(); + } + } + + TrieNode root; + /** Initialize your data structure here. */ + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + if (word == null || word.length() == 0 || search(word)) return; + + TrieNode node = root; + for (char c : word.toCharArray()) { + node.children.putIfAbsent(c, new TrieNode()); + node = node.children.get(c); + } + node.isEnd = true; // can set word to node as well, if needed + } + + /** Returns if the word is in the trie: correct path + isEnd */ + public boolean search(String word) { + if (word == null || word.length() == 0) return false; + + TrieNode node = root; + for (char c : word.toCharArray()) { + if (!node.children.containsKey(c)) { + return false; + } + node = node.children.get(c); + } + return node.isEnd; + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + if (prefix == null || prefix.length() == 0) return false; + + TrieNode node = root; + for (char c : prefix.toCharArray()) { + if (!node.children.containsKey(c)) { + return false; + } + node = node.children.get(c); + } + return true; + } +} + +/** + * Your Trie object will be instantiated and called as such: + * Trie obj = new Trie(); + * obj.insert(word); + * boolean param_2 = obj.search(word); + * boolean param_3 = obj.startsWith(prefix); + */ + + + +/* +Previous notes. + Thoughts: + - Trie is a like a dictionary that's populated based on given input. + - Each level indicates each index of the word, where in each level there are multiple separate nodes + (depending on how many words we have used to populate the trie ) + - At end of a string, mark it as end == true; + + - search: find end of word && end == true && + - startWith: find till end of prefix +*/ +``` \ No newline at end of file diff --git a/Java/Implement strStr().java b/Java/Implement strStr().java new file mode 100755 index 0000000..96eb2ee --- /dev/null +++ b/Java/Implement strStr().java @@ -0,0 +1,88 @@ +E +1525220254 +tags: Two Pointers, String + +给两个string A, B, 找一个 B 在 A 种的起始位置. + +#### Two Pointer +- 找到B在A中的起始位置, 然后看一下从这个点开始的substring是否等于B就可以了 +- 还挺多坑的, 这些可以帮助优化: +- 1. 当B是“”的时候,也就是能在A的其实位置找到B....index = 0. +- 2. edge condition: 如果 haystack.length() < needle.length() 的话, 必须错, return -1 +- 3. 如果在某个index, A后面剩下的长度, 比B的长度短, 也是误解, return -1 + +``` +/* +Implement strStr(). + +Returns the index of the first occurrence of needle in haystack, +or -1 if needle is not part of haystack. + +Hide Company Tags Facebook +Hide Tags Two Pointers String +Hide Similar Problems (H) Shortest Palindrome + +Clarification +Do I need to implement KMP Algorithm in an interview? + + - Not necessary. When this problem occurs in an interview, + the interviewer just want to test your basic implementation ability. + +*/ + +class Solution { + public int strStr(String haystack, String needle) { + if (needle == null || needle.length() == 0) { + return 0; + } + if (needle.length() > haystack.length()) { + return -1; + } + int n = haystack.length(); + int m = needle.length(); + for (int i = 0; i < n; i++) { + if (n - i < m) { + return -1; + } + if (haystack.charAt(i) != needle.charAt(0)) { + continue; + } + + if (haystack.substring(i, i + m).equals(needle)) { + return i; + } + } + return -1; + } +} + +// Previous: +public class Solution { + public int strStr(String haystack, String needle) { + if (needle == null || needle.length() == 0) { + return 0; + } + /* + else if (haystack == null || haystack.length() == 0 + || haystack.length() < needle.length()) { + return -1; + }*/ + int i = 0; + int j = 0; + + for (i = 0; i < haystack.length() - needle.length() + 1; i++) { + // if (haystack.charAt(i + j) == needle.charAt(j)) { + for (j = 0; j < needle.length(); j++) { + if (haystack.charAt(i + j) != needle.charAt(j)) { + break; + } + } + if (j == needle.length()) { + return i; + } + // } + } + return -1; + } +} +``` \ No newline at end of file diff --git a/Java/IndexMatch.java b/Java/IndexMatch.java new file mode 100755 index 0000000..12c73e2 --- /dev/null +++ b/Java/IndexMatch.java @@ -0,0 +1,54 @@ +E + +有序, 假设有这样的数字:target. +target 左边的数字,一定不比index大,target右边的数字,一定比index大。 +这样可以binary search.O(logn) + +``` +/* +来自网上uber面经:给一个有序数组(不含重复),返回任意一个数字,这个数字的值和它的数组下标相等 + +可能的follow up: +1. 如果含有重复数字怎么办? + 如果有序,也没问题,还是binary search + +2. 另一种follow up:找这样数字的边界. + 如果存在,那么和index match的一定在一块. + 找到任何一个candiate, 找边界,也可以binary search,只是match的condition不同罢了。 + +*/ +//Binary search. +import java.io.*; +import java.util.*; +class Solution { + + public int indexMatch(int[] nums) { + if (nums == null || nums.length == 0) { + return -1; + } + + int start = 0; + int end = nums.length - 1; + while (start + 1 < end) { + int mid = start + (end - start)/2; + if (nums[mid] == mid) { + return mid; + } else if (nums[mid] < mid) { + start = mid; + } else {//nums[mid] > mid + end = mid; + } + } + + return -1; + } + + public static void main(String[] args) { + System.out.println("START"); + int[] input = {-1, 0, 1, 3, 5, 8, 9};//return 3 + Solution sol = new Solution(); + int rst = sol.indexMatch(input); + System.out.println(rst); + } +} +``` \ No newline at end of file diff --git a/Java/Inorder Successor in BST.java b/Java/Inorder Successor in BST.java new file mode 100755 index 0000000..7499d12 --- /dev/null +++ b/Java/Inorder Successor in BST.java @@ -0,0 +1,172 @@ +M +1533276962 +tags: BST, Tree + +找 Inorder traversal规则里的下一个. + +主要想法是考虑: + 1. 如果 node.right == null, 找上一个unprocessed node alone the inorder traversal path + 2. 如果 node.right != null, successor 一定在这个node.right那个subtree里面 +最后竟然可以简化成几行, 非常全面的BST问题: 有search, 有对inorder traversal的理解, 还有坑. + +#### Short Recursive and Iterative without Stack +- Previous solution, we use stack to hold previous cached/unprocessed items: but do we need use catch to hold them? +- If moving left: `p.val < root.val`, then root (parent of left child) is a successor candidate, so save `rst = root`. +- If moving right or equal: `p.val >= root.val`, the successor has nothing to do with curr node, so just directly dive into root.right. +- Both iterative and recursive solution can be simplified as such. + + +#### Previous Iterative + stack +- Iteratively search +- Still need stack to store previously unprocessed items along the path + +#### Previous Recursive + Stack +- 画inorder图,发现规律.每个node的后继node(successor)有几种情况: +- 1. node.right 是个leaf到底了。那么就return. +- 2. set rightNode = node.right, 但发现rightNode has a lot left children to leaf. +- 3. 比如, node.right == null, 也就是node自己是leaf,要回头看山顶找Inorder traversal规则里的下一个。 +- 发现:其实就是每层都把路过的curr node放在stack里,最上面的,就是当下改return的那个successor:) Done. + +``` +/* +Given a binary search tree (See Definition) and a node in it, find the in-order successor of that node in the BST. + +Have you met this question in a real interview? Yes +Example +Given tree = [2,1] and node = 1: + + 2 + / +1 +return node 2. + +Given tree = [2,1,3] and node = 2: + + 2 + / \ +1 3 +return node 3. + +Note +If the given node has no in-order successor in the tree, return null. + +Challenge +O(h), where h is the height of the BST. + +Tags Expand +Binary Search Tree Binary Tree +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +// Short Recursive version: +class Solution { + public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { + if (root == null) return root; + + if (p.val < root.val) { + TreeNode left = inorderSuccessor(root.left, p); + return left != null ? left : root; + } else { // p.val >= root.val + return inorderSuccessor(root.right, p); + } + } +} + +// Short Iterative version: +class Solution { + public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { + TreeNode rst = null; + while (root != null) { + if (p.val < root.val) { + rst = root; // cache curr node as parent of left child + root = root.left; + } else { // p.val >= root.val + root = root.right; + } + } + return rst; + } +} + + +// Prev iterative: code too long/complicated with stack +public class Solution { + public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { + if (root == null || p == null) return null; + + Stack stack = new Stack<>(); + while (root != null) { + if (p == root) { + break; + } else if (p.val < root.val) { + if (root.left == null) return null; + stack.push(root); + root = root.left; + } else { //p.val > root.val + if (root.right == null) return null; + root = root.right; + } + } + + if (root.right != null) { //in-order next node: Hunt down rightNode's left leaf + TreeNode rst = root.right; + while (rst.left != null) { + rst = rst.left; + } + return rst; + } else if (!stack.isEmpty()) { + return stack.pop(); //Inorder pattern rules: + } + return null; + } +} + +/* + Analysis the structure. + 1. Note1: When directing left, save curr in stack. Curr node will be re-visit with rules of in-order traversal. Write an example, will help refresh memory. + In the future, if node == target && node.right == null, then stack.pop() is the successor. + + 2. Note2: When p is found, and node.right != null. Be careful where: if node.right just ends there, surely return it, that's it; + However, if rightNode has left child, well, have to trace all the way down to left---end of the leaf + + Stack uses O(h) space +*/ +// Prev recursive: code too long/complicated with stack. +public class Solution { + Stack stack = new Stack<>(); + public TreeNode inorderSuccessor(TreeNode root, TreeNode p) { + if (root == null || p == null) return null; + + if (root == p) { + if (root.right != null) { //in-order next node: Hunt down rightNode's left leaf + TreeNode rst = root.right; + while (rst.left != null) { + rst = rst.left; + } + return rst; + } else if (!stack.isEmpty()) { + return stack.pop(); //Inorder pattern rules: + } + return null; + } + + if (p.val < root.val && root.left != null) { // search left, with root added at bottom of stack + stack.push(root); + return inorderSuccessor(root.left, p); + } else if (p.val > root.val && root.right != null) { // search right + return inorderSuccessor(root.right, p); + } + + return null; + } +} + +``` diff --git a/Java/Insert Delete GetRandom O(1).java b/Java/Insert Delete GetRandom O(1).java new file mode 100644 index 0000000..0949f8d --- /dev/null +++ b/Java/Insert Delete GetRandom O(1).java @@ -0,0 +1,147 @@ +M +1531964584 +tags: Design, Array, Hash Table +time: O(1) avg +space: O(n) + +#### Hash Table +- 用`map 来track value->index`, 用`list track index->value` +- map查看value是否存在 +- list maintain 用来 insert/remove/random operations. +- 特点: 一旦remove, 换到list结尾然后 `list.remove(list.size() - 1)`, 这样remove的cost更低. +- list.remove(object) 应该是要O(logn) 做一个search的. + +``` + +/* +Design a data structure that supports all following operations in average O(1) time. + +insert(val): Inserts an item val to the set if not already present. +remove(val): Removes an item val from the set if present. +getRandom: Returns a random element from current set of elements. Each element must have the same probability of being returned. +Example: + +// Init an empty set. +RandomizedSet randomSet = new RandomizedSet(); + +// Inserts 1 to the set. Returns true as 1 was inserted successfully. +randomSet.insert(1); + +// Returns false as 2 does not exist in the set. +randomSet.remove(2); + +// Inserts 2 to the set, returns true. Set now contains [1,2]. +randomSet.insert(2); + +// getRandom should return either 1 or 2 randomly. +randomSet.getRandom(); + +// Removes 1 from the set, returns true. Set now contains [2]. +randomSet.remove(1); + +// 2 was already in the set, so return false. +randomSet.insert(2); + +// Since 2 is the only number in the set, getRandom always return 2. +randomSet.getRandom(); +*/ + +/* +Use a global random number. +map +list, where valid items are tracked. When removing, swap with tail, and drop. +*/ +class RandomizedSet { + Random rd; + Map map; + List list; + /** Initialize your data structure here. */ + public RandomizedSet() { + this.rd = new Random(); + this.map = new HashMap<>(); + this.list = new ArrayList<>(); + } + + /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */ + public boolean insert(int val) { + if (!map.containsKey(val)) { + map.put(val, list.size()); + list.add(val); + return true; + } + return false; + } + + /** Removes a value from the set. Returns true if the set contained the specified element. */ + public boolean remove(int val) { + if (map.containsKey(val)) { + int index = map.get(val); + list.set(index, list.get(list.size() - 1)); + map.put(list.get(index), index); + map.remove(val); + list.remove(list.size() - 1); + return true; + } + return false; + } + + /** Get a random element from the set. */ + public int getRandom() { + return list.get(rd.nextInt(list.size())); + } +} + +/** + * Your RandomizedSet object will be instantiated and called as such: + * RandomizedSet obj = new RandomizedSet(); + * boolean param_1 = obj.insert(val); + * boolean param_2 = obj.remove(val); + * int param_3 = obj.getRandom(); + */ + +/* +Use a global random number, set +Too slow, mostly due to the getRandom() +*/ +class RandomizedSet { + Random rd; + Set set; + /** Initialize your data structure here. */ + public RandomizedSet() { + this.rd = new Random(); + this.set = new HashSet<>(); + } + + /** Inserts a value to the set. Returns true if the set did not already contain the specified element. */ + public boolean insert(int val) { + if (!set.contains(val)) { + set.add(val); + return true; + } + return false; + } + + /** Removes a value from the set. Returns true if the set contained the specified element. */ + public boolean remove(int val) { + if (set.contains(val)) { + set.remove(val); + return true; + } + return false; + } + + /** Get a random element from the set. */ + public int getRandom() { + List list = new ArrayList<>(set); + return list.get(rd.nextInt(list.size())); + } +} + +/** + * Your RandomizedSet object will be instantiated and called as such: + * RandomizedSet obj = new RandomizedSet(); + * boolean param_1 = obj.insert(val); + * boolean param_2 = obj.remove(val); + * int param_3 = obj.getRandom(); + */ +``` \ No newline at end of file diff --git a/Java/Insert Interval.java b/Java/Insert Interval.java old mode 100644 new mode 100755 index 8498a3e..58528b6 --- a/Java/Insert Interval.java +++ b/Java/Insert Interval.java @@ -1,9 +1,34 @@ +H +1528350453 +tags: Array, Sort, PriorityQueue + +#### Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn) + + +#### Basic Implementation +- 这里已经给了 **sorted** intervals by start point. +- 直接找到可以insert newInterval的位子. Insert +- 然后loop to merge entire interval array +- 因为给的是个list, 所以方便`intervals.remove(i)` +- remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture +- O(n) + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + +``` + /* Given a non-overlapping interval list which is sorted by start point. -Insert a new interval into it, make sure the list is still in order and non-overlapping (merge intervals if necessary). +Insert a new interval into it, make sure the list is still in order and non-overlapping +(merge intervals if necessary). -Have you met this question in a real interview? Yes Example Insert [2, 5] into [[1,2], [5,9]], we get [[1,9]]. @@ -12,14 +37,8 @@ Tags Expand Basic Implementation -Thoughts: -1. Find right position to insert: find the last start position that's <= newInterval.start -2. After insertion, merge. -3. How to merge? Look at merge inerval question */ - - /** * Definition of Interval: * public classs Interval { @@ -29,14 +48,78 @@ * this.end = end; * } */ + + + +/* +Thoughts: +What's the difference from merge intervals? +1. Create Class point (x, flag) +2. sort point in min-heap +3. when count increase and decreases to 0, that means we can close an interval +*/ +/* +Option1: +- convert all intervals into points with open/close flag, sort, and merge. When count == 0, close a interval. +- priority queue, O(n) space, O(nlogn) time +*/ class Solution { - /** - * Insert newInterval into intervals. - * @param intervals: Sorted interval list. - * @param newInterval: A new interval. - * @return: A new sorted interval list. - */ - public ArrayList insert(ArrayList intervals, Interval newInterval) { + class Point { + int val, flag; + public Point(int val, int flag) { + this.val = val; + this.flag = flag; + } + } + public List insert(List intervals, Interval newInterval) { + List rst = new ArrayList<>(); + if (intervals == null || intervals.size() == 0 || newInterval == null) { + if (newInterval != null) rst.add(newInterval); + return rst; + } + + // build priority queue + PriorityQueue queue = buildQueue(intervals, newInterval); + // iterate over queue, count and build interval + int count = 0; + Interval interval = new Interval(); + while (!queue.isEmpty()) { + Point p = queue.poll(); + if (count == 0) {//detect start + interval.start = p.val; + } + + count += p.flag; + while (!queue.isEmpty() && p.val == queue.peek().val) { + p = queue.poll(); + count += p.flag; + } + + if (count == 0) { + interval.end = p.val; + rst.add(interval); + interval = new Interval(); + } + } + return rst; + } + + private PriorityQueue buildQueue(List intervals, Interval newInterval) { + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(p -> p.val)); + queue.offer(new Point(newInterval.start, 1)); + queue.offer(new Point(newInterval.end, -1)); + for (Interval interval : intervals) { + queue.offer(new Point(interval.start, 1)); + queue.offer(new Point(interval.end, -1)); + } + return queue; + } + +} + +// intervals is sorted +class Solution { + public List insert(List intervals, Interval newInterval) { if (intervals == null || intervals.size() == 0 || newInterval == null) { if (newInterval != null) { intervals.add(newInterval); @@ -51,9 +134,8 @@ public ArrayList insert(ArrayList intervals, Interval newInt front = i; } } - if (front == -1) { - intervals.add(0, newInterval); - } + + // if front==-1, add to front + 1 = 0 intervals.add(front + 1, newInterval); //Merge @@ -72,4 +154,106 @@ public ArrayList insert(ArrayList intervals, Interval newInt return intervals; } -} \ No newline at end of file +} + + + +/* +O(n) time, O(1) space +Thoughts: +1. Find right position to insert: find the last start position that's <= newInterval.start +2. After insertion, merge. +3. How to merge? Look at merge inerval question +*/ +class Solution { + public List insert(List intervals, Interval newInterval) { + if (intervals == null || intervals.size() == 0 || newInterval == null) { + if (newInterval != null) { + intervals.add(newInterval); + } + return intervals; + } + + //Insert + int index = 0; + for (int i = 0; i < intervals.size(); i++) { + if (intervals.get(i).start <= newInterval.start) { + index = i; + break; + } + } + intervals.add(index, newInterval); + return merge(intervals); + } + + private List merge(List intervals) { + intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn) + int i = 0; + while(i < intervals.size() - 1) { + Interval curr = intervals.get(i), next = intervals.get(i + 1); + if (curr.end >= next.start) { + curr.end = curr.end >= next.end ? curr.end : next.end; + intervals.remove(i + 1); + continue; + } + i++; + } + return intervals; + } +} + + +/* + 2.26.2016 Not done. Looks like merging process still take O(n) + Can we do Binary Search for the index to insert newInterval.start, newInterval.end + find position x that has newInterval.start <= x.start + find position y that has y.end <= newInterval.end +*/ +class Solution { + public ArrayList insert(ArrayList intervals, Interval newInterval) { + ArrayList result = new ArrayList(); + + //Biary search for inStart, inEnd + int inStart = 0 + int inEnd = 0; + int start = 0; + int end = intervals.size(); + int mid; + while (start + 1 < end) { + mid = start + (end - start)/2; + if (newInterval.start <= intervals.get(mid)) { + if (mid == 0 || intervals.get(mid).start == newInterval.start + || intervals.get(mid-1).start < newInterval.start) { + inStart = mid; + break; + } + end = mid; + } else { + start = mid; + } + } + start = 0; + end = intervals.size(); + while (start + 1 < end) { + mid = start + (end - start)/2; + if (newInterval.end <= intervals.get(mid)) { + if (mid == 0 || intervals.get(mid).start == newInterval.end + || intervals.get(mid-1).start < newInterval.end) { + inEnd = mid; + break; + } + end = mid; + } else { + start = mid; + } + } + + int diff = + + + return result; + } +} + + +``` diff --git a/Java/Insert Node in a Binary Search Tree .java b/Java/Insert Node in a Binary Search Tree .java old mode 100644 new mode 100755 index d44d293..abb5d9b --- a/Java/Insert Node in a Binary Search Tree .java +++ b/Java/Insert Node in a Binary Search Tree .java @@ -1,38 +1,65 @@ -/* -43% Accepted -Given a binary search tree and a new tree node, insert the node into the tree. You should keep the tree still be a valid binary search tree. - -Example -Given binary search tree as follow: +E +tags: BST - 2 +往Binary Search Tree里面加东西,一定会找到一个合适的leaf加上去。 - / \ +那么:就是说someNode.left or someNode.right是null时,就是insert node的地方。 -1 4 +找到那个someNode就按照正常的Binary Search Tree规律。 - / +``` - 3 - -after Insert node 6, the tree should be: +/* +Given a binary search tree and a new tree node, insert the node into the tree. +You should keep the tree still be a valid binary search tree. - 2 +Example +Given binary search tree as follow, after Insert node 6, the tree should be: - / \ + 2 2 + / \ / \ +1 4 --> 1 4 + / / \ + 3 3 6 +Challenge +Can you do it without recursion? -1 4 +Tags Expand +LintCode Copyright Binary Search Tree - / \ +*/ - 3 6 +//2.23.2016 recap +//Find node that has left or right==null. insert node on left/right +public class Solution { + public TreeNode insertNode(TreeNode root, TreeNode node) { + if (node == null || root == null) { + return node; + } + TreeNode dummy = root; + while (root != null) { + if (node.val < root.val) { + if (root.left == null) { + root.left = node; + break; + } + root = root.left; + } else if (node.val > root.val) { + if (root.right == null) { + root.right = node; + break; + } + root = root.right; + } + } + return dummy; + } +} -Challenge -Do it without recursion -Tags Expand -Binary Search Tree LintCode Copyright +/* +Previous solution Thinking process: Binary Search Tree: parent must < left node @@ -40,7 +67,6 @@ use a dummy node runNode to flow around on the binary search tree, compare with target node. Find the leaf node and add into appropriate pos. */ - public class Solution { /** * @param root: The root of the binary search tree. @@ -74,3 +100,5 @@ public TreeNode insertNode(TreeNode root, TreeNode node) { } } + +``` \ No newline at end of file diff --git a/Java/Insertion Sort List.java b/Java/Insertion Sort List.java old mode 100644 new mode 100755 index b9d1b74..72cc5f7 --- a/Java/Insertion Sort List.java +++ b/Java/Insertion Sort List.java @@ -1,8 +1,20 @@ -想明白原理就好做了: -基本上就是正常的想法:已经有个sorted list, insert一个element进去。怎么做? - while 里面每个元素都小于 curr, keep going - 一旦curr在某个点小了,加进去当下这个空隙。 -这个题目也就是:把list里面每个元素都拿出来,scan and insert一遍! +M +1525222523 +tags: Linked List, Sort + +input一串数字, 需要出sorted output. 每次insert一个数字时, 都要放到正确的sorted的位置 + +每次insertion的时候, 都从input里面减掉这个数字 + +#### Linked List +- 把list里面每个元素都拿出来,scan and insert一遍 +- Time O(n^2), worst case, 每次放入n个数字里面的element, 刚好都是最大的 +- 所以每次要traverse n nodes, 然后走n次 + +##### 思考方法 +- 如果已经有个sorted list, insert一个element进去。怎么做? +- while 里面每个元素都小于 curr, keep going +- 一旦curr在某个点小了,加进去当下这个空隙。 ``` /* @@ -15,6 +27,50 @@ Sort Linked List */ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +/* +Thoughts: +1. Linked ListNode +2. Find right position to insert by iterating the output list +*/ +class Solution { + public ListNode insertionSortList(ListNode head) { + // Check head + if (head == null || head.next == null) { + return head; + } + // Create dummy head, dummy.next will be returned + ListNode output = new ListNode(-1); + + // iterate over head to populate all nodes + while (head != null) { + // Create new node for each insertion + final ListNode node = new ListNode(head.val); + if (output.next == null || node.val <= output.next.val) { + node.next = output.next; + output.next = node; + } else { + // Iterate over the output to find the correct position to insert + ListNode moveNode = output.next; + while(moveNode.next != null && node.val > moveNode.next.val) { + moveNode = moveNode.next; + } + node.next = moveNode.next; + moveNode.next = node; + } + head = head.next; + } + + return output.next; + } +} /* Recap. 12.10.2015 diff --git a/Java/Integer to English Words.java b/Java/Integer to English Words.java new file mode 100755 index 0000000..a5be332 --- /dev/null +++ b/Java/Integer to English Words.java @@ -0,0 +1,166 @@ +H +1533443082 +tags: Math, String, Enumeration + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 + +#### 注意 +- StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 +- 注意加 " " 的时候, 如果多余, 要`trim()` +- 注意, 小于20的数字, 有自己的特殊写法, 需要额外handle +- 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 + + +``` +/* +Convert a non-negative integer to its english words representation. +Given input is guaranteed to be less than 2^31 - 1. + +For example, +123 -> "One Hundred Twenty Three" +12345 -> "Twelve Thousand Three Hundred Forty Five" +1234567 -> "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven" +Hint: + +Did you see a pattern in dividing the number into chunk of words? For example, 123 and 123000. +Group the number by thousands (3 digits). You can write a helper function that takes a number less than 1000 and convert just that chunk to words. +There are many edge cases. What are some good test cases? Does your code work with input such as 0? Or 1000010? (middle chunk is zero and should not be printed out) + +Tags: Math, String +Similar Problems: (M) Integer to Roman + +*/ + +/* +Thoughts: +1. Find the max number 2^31 - 1 = 2,147,483,647, maximum is 'Billion' +2. Break into 3-digt parts +3. Translate each 3-digit number +4. Be carefull with numbers < 20, && 10's that's >= 20 +*/ +class Solution { + public String[] v1 = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}; + public String[] v2 = {"", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"}; + public String[] v3 = {"", "Thousand ", "Million ", "Billion "}; + public String numberToWords(int num) { + if (num < 0) return ""; + if (num == 0) return "Zero"; + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < 4; i++) { + int partial = num % 1000; // Obtain smaller 3-digit section + if (partial > 0) { // Append suffix depending on i, where v3[0] = ""; + sb.insert(0, calcPartialNumber(partial) + " " + v3[i]); + } + num /= 1000; + } + + return sb.toString().trim(); + } + + private String calcPartialNumber(int num) { + StringBuffer sb = new StringBuffer(); + if (num >= 100) { + int hund = num / 100; + sb.append(v1[hund] + " Hundred "); + num = num % 100; + } + + if (num < 20) { + sb.append(v1[num] + " "); + } else { + int numTens = num / 10; + int numDigit = num % 10; + sb.append(v2[numTens] + " " + v1[numDigit] + " "); + } + + return sb.toString().trim(); + } +} + + +/** +Previous notes: + +2^31 - 1 = 2,147,483,647 +Trillion, Billion, Million, Thousand, Hundred, Ninty .... Ten, Nine ... One, Zero. + +1. Break the words to up to 4 parts: new break[4] by /(1000 ^ i) +2. For each i, deal with that 3-digit number in break[i] +3. Append corresponding words for each break[i] + +Special case: +zero +000 in one break[i]: skip the whole thing +*/ + + +public class Solution { + public String[] v1 = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}; + public String[] v2 = {"", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"}; + public String numberToWords(int num) { + if (num < 0) { + return ""; + } + if (num == 0) { + return "Zero"; + } + String rst = ""; + for (int i = 0; i < 4; i++) { + int partial = num - (num / 1000) * 1000; + if (partial > 0) { + rst = helper(partial, i) + " " + rst; + } + num /= 1000; + } + while (rst.charAt(rst.length() - 1) == ' ') { + rst = rst.substring(0, rst.length() - 1); + } + return rst; + } + + public String helper(int num, int i) { + String str = ""; + if (num >= 100) { + int hund = num / 100; + str = v1[hund] + " Hundred "; + num = num % 100; + } + + if (num < 20) { + str += v1[num] + " "; + } else { + int numTens = num / 10; + int numDigit = num % 10; + str += v2[numTens] + " "; + str += v1[numDigit] + " "; + } + + while (str.charAt(str.length() - 1) == ' ') { + str = str.substring(0, str.length() - 1); + } + + //depending on i: + switch (i) { + case 1 : + str += " Thousand"; + break; + case 2 : + str += " Million"; + break; + case 3 : + str += " Billion"; + break; + } + + return str; + } +} +``` diff --git a/Java/Interleaving Positive and Negative Numbers.java b/Java/Interleaving Positive and Negative Numbers.java old mode 100644 new mode 100755 index 4463fb2..8799701 --- a/Java/Interleaving Positive and Negative Numbers.java +++ b/Java/Interleaving Positive and Negative Numbers.java @@ -1,10 +1,25 @@ -不管遇到啥,先排个序。 -这里主要要特别考虑,正数多还是负数多的问题。 -count一下,然后举两个小栗子就看出来端倪了。 -然后Two Pointer +M +1525230897 +tags: Two Pointers + +给一串数组 有正负数. 重新排列, 让数组里面 正数 和 负数 相隔开. 原来的order无所谓 + +#### Two pointer +- 需要知道正负的位置, 所以排序 O(nlogN) +- 考虑: 正数多还是负数多的问题, 举栗子就看出来端倪了 +- 然后Two Pointer, swap +- Time O(nlogn), space O(n) + +#### extra space +- 用extra O(n) space, 把正负分成两个list +- 然后分别按照index填回去 +- time O(n). space O(n) +- 但是就么有用到Two pointer了 + ``` /* -Given an array with positive and negative integers. Re-range it to interleaving with positive and negative integers. +Given an array with positive and negative integers. +Re-range it to interleaving with positive and negative integers. Example Given [-1, -2, -3, 4, 5, 6], after re-range, it will be [-1, 5, -2, 4, -3, 6] or any other reasonable answer. @@ -20,6 +35,81 @@ */ /* +Thoughts: +Sort the array and swap to correct order. +1. Negative # == positive #, swap start/end untill start == end +2. Negative # != positive #, if mid point is positive, end = end - 1; if mid point is negative, start = start + 1. +3. Then do the same swap based on start, end + +O(nlogn) runtime +*/ +public class Solution { + /* + * @param A: An integer array. + * @return: nothing + */ + public void rerange(int[] A) { + if (A == null || A.length <= 1) { + return; + } + Arrays.sort(A); + int start = 0; + int end = A.length - 1; + if (A.length % 2 != 0) { + if (A[(start + end) / 2] >= 0) { + end--; + } else { + start++; + } + } + + while (start < end) { + int temp = A[start]; + A[start] = A[end]; + A[end] = temp; + start += 2; + end -= 2; + } + } +} + +// Extra O(n) space, O(n) runtime +public class Solution { + /* + * @param A: An integer array. + * @return: nothing + */ + public void rerange(int[] A) { + if (A == null || A.length <= 1) { + return; + } + List positive = new ArrayList<>(); + List negative = new ArrayList<>(); + for (int num : A) { + if (num >= 0) { + positive.add(num); + } else { + negative.add(num); + } + } + + int extraPositive = positive.size() > negative.size() ? 0 : 1; + + for (int i = 0; i < A.length; i++) { + if (i % 2 == extraPositive) { + A[i] = positive.get(0); + positive.remove(0); + } else { + A[i] = negative.get(0); + negative.remove(0); + } + System.out.println(A[i]); + } + } +} + +/* + Previous notes: Thoughts: Sort, so it becomes [-1,-2,-3,-4,4,5,6,7] Two pointer start,end. diff --git a/Java/Interleaving String.java b/Java/Interleaving String.java old mode 100644 new mode 100755 index a1b4c87..3c2b0eb --- a/Java/Interleaving String.java +++ b/Java/Interleaving String.java @@ -1,3 +1,111 @@ +H +1519199180 +tags: String, DP + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + +``` +/* +LeetCode: +Given s1, s2, s3, find whether s3 is formed by the interleaving of s1 and s2. + +For example, +Given: +s1 = "aabcc", +s2 = "dbbca", + +When s3 = "aadbbcbcac", return true. +When s3 = "aadbbbaccc", return false. + */ + +/* +Thoughts: +Take continuous part of s1, and part of s2 to form s3. +Consider last index of s3: where is this last char from (s1, or s2)? +Two possible conditioins: +1. s3 last char from s1. Next step: consider s1[0 ~ n-1] and s2 to form s3[0 ~ m - 1]; +2. s3 last char from s2. Next step: consider s1 and s2[0 ~ n-1] to form s3[0 ~ m - 1]; + +dp[i][j]: up to ith and jth index of s1 and s2, is it possible to form s3[i + j]; + +dp[i][j] = dp[i - 1][j]|s1[i - 1] == s3[i + j - 1] OR dp[i][j - 1]|s2[i - 1] == s3[i + j - 1] + +dp[0][0] = false; // 0th length, false; + +Time: O(MN) +Space: O(MN) +*/ +class Solution { + public boolean isInterleave(String s1, String s2, String s3) { + if (s1 == null || s2 == null || s1.length() + s2.length() != s3.length()) { + return false; + } + int m = s1.length(); + int n = s2.length(); + boolean[][] dp = new boolean[m + 1][n + 1]; + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (i == 0 && j == 0) { // since s3.length() = s1.length() + s2.length(), so it'll be true here. + dp[i][j] = true; + continue; + } + + dp[i][j] = false; + if (i > 0 && s1.charAt(i - 1) == s3.charAt(i + j - 1)) { + dp[i][j] |= dp[i - 1][j]; + } + + if (j > 0 && s2.charAt(j - 1) == s3.charAt(i + j - 1)) { + dp[i][j] |= dp[i][j - 1]; + } + } + } + return dp[m][n]; + } +} + +//Optimize: rolling array +// Time: O(MN), Space O(N) +class Solution { + public boolean isInterleave(String s1, String s2, String s3) { + if (s1 == null || s2 == null || s1.length() + s2.length() != s3.length()) { + return false; + } + int m = s1.length(); + int n = s2.length(); + boolean[][] dp = new boolean[2][n + 1]; + int curr = 0; + int prev = 0; + + for (int i = 0; i <= m; i++) { + prev = curr; + curr = 1 - prev; + + for (int j = 0; j <= n; j++) { + if (i == 0 && j == 0) { // since s3.length() = s1.length() + s2.length(), so it'll be true here. + dp[curr][j] = true; + continue; + } + + dp[curr][j] = false; + if (i > 0 && s1.charAt(i - 1) == s3.charAt(i + j - 1)) { + dp[curr][j] |= dp[prev][j]; + } + + if (j > 0 && s2.charAt(j - 1) == s3.charAt(i + j - 1)) { + dp[curr][j] |= dp[curr][j - 1]; + } + } + } + return dp[curr][n]; + } +} + /* Given three strings: s1, s2, s3, determine whether s3 is formed by the interleaving of s1 and s2. @@ -99,3 +207,5 @@ public boolean isInterleave(String s1, String s2, String s3) { return true; } } + +``` \ No newline at end of file diff --git a/Java/Intersection of Two Arrays II.java b/Java/Intersection of Two Arrays II.java new file mode 100644 index 0000000..ca1e1d3 --- /dev/null +++ b/Java/Intersection of Two Arrays II.java @@ -0,0 +1,109 @@ +E +1518453586 +tags: Hash Table, Two Pointers, Binary Search, Sort + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + +``` +/* +Given two arrays, write a function to compute their intersection. + +Example: +Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2, 2]. + +Note: +Each element in the result should appear as many times as it shows in both arrays. +The result can be in any order. +Follow up: +What if the given array is already sorted? How would you optimize your algorithm? +What if nums1's size is small compared to nums2's size? Which algorithm is better? +What if elements of nums2 are stored on disk, and the memory is limited such that you cannot load all elements into the memory at once? +*/ + +/* +Thoughts: +As problem suggests, items appeared in nums1 && appears in nums2 should be recorded. Use Map. +Space O(n) +Time O(n) +*/ +class Solution { + public int[] intersect(int[] nums1, int[] nums2) { + if (nums1 == null || nums2 == null) { + return new int[0]; + } + final List result = new ArrayList<>(); + final Map map = new HashMap<>(); + // fill nums1 in to map + for (int num : nums1) { + if (!map.containsKey(num)) { + map.put(num, 0); + } + map.put(num, map.get(num) + 1); + } + // check nums2 against the map + for (int num : nums2) { + if (map.containsKey(num) && map.get(num) > 0) { + result.add(num); + map.put(num, map.get(num) - 1); + } + } + int[] rst = new int[result.size()]; + for (int i = 0; i < result.size(); i++) { + rst[i] = result.get(i); + } + return rst; + } + +} + +/* +Thoughts: +Result can be in any other: sort nums1, nums2, then find each in the other? +*/ +/* +class Solution { + public int[] intersect(int[] nums1, int[] nums2) { + if (nums1 == null || nums2 == null) { + return new int[0]; + } + List indexes = new ArrayList<>(); + Arrays.sort(nums1); + for (int i = 0; i < nums2.length; i++) { + int index = binarySearch(nums1, indexes, nums2[i]); + if(index != -1) { + indexes.add(index); + } + } + int[] rst = new int[indexes.size()]; + for (int i = 0; i < indexes.size(); i++) { + rst[i] = nums1[indexes.get(i)]; + } + return rst; + } + + private int binarySearch(int[] nums, List indexes, int target) { + int start = 0; + int end = nums.length - 1; + while (start <= end) { + int mid = start + (end - start) / 2; + if (nums[mid] == target) { + if (indexes.contains(mid)) { + start = mid + 1; + } else { + return mid; + } + } else if (nums[mid] < target) { + start = mid + 1; + } else { + end = mid - 1; + } + } + return -1; + } +}*/ +``` \ No newline at end of file diff --git a/Java/Intersection of Two Arrays.java b/Java/Intersection of Two Arrays.java new file mode 100644 index 0000000..ddf769b --- /dev/null +++ b/Java/Intersection of Two Arrays.java @@ -0,0 +1,93 @@ +E +1533542058 +tags: Hash Table, Two Pointers, Binary Search, Sort + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + +``` +/* +Given two arrays, write a function to compute their intersection. + +Example: +Given nums1 = [1, 2, 2, 1], nums2 = [2, 2], return [2]. + +Note: +Each element in the result must be unique. +The result can be in any order. +*/ + + +/* +Thoughts: +Sol1: Use hashSet and add common item to result. +set.contains() will be a search, which is O(1), plus all items -> O(n + m) + +*/ +class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + if (nums1 == null || nums2 == null || nums1.length == 0 || nums2.length == 0) { + return new int[0]; + } + Set unionSet = new HashSet<>(); + Set resultSet = new HashSet<>(); + for (int num: nums1) { + unionSet.add(num); + } + for (int num: nums2) { + if (unionSet.contains(num)) { + resultSet.add(num); + } + } + int i = 0; + int[] result = new int[resultSet.size()]; + for (int num: resultSet) { + result[i++] = num; + } + return result; + } +} + + +/* +Sol2: Binary search each item of nums1 from nums2. The runtime will be nlog(n) +Also need to sort one array fist, it's another nLog(n) +*/ +class Solution { + public int[] intersection(int[] nums1, int[] nums2) { + if (nums1 == null || nums2 == null || nums1.length == 0 || nums2.length == 0) { + return new int[0]; + } + + Arrays.sort(nums1);// nLog(n) + final Set resultSet = new HashSet<>(); + for (final int num: nums2) { // nLog(m) + if(binarySearch(nums1, num)) { + resultSet.add(num); + } + } + int i = 0; + final int[] result = new int[resultSet.size()]; + for (final int num: resultSet) { + result[i++] = num; + } + return result; + } + + private boolean binarySearch(int[] nums, int target) { + int start = 0; + int end = nums.length - 1; + while(start <= end) { + int mid = (start + end) / 2; + if (nums[mid] < target) { + start = mid + 1; + } else if (nums[mid] > target) { + end = mid - 1; + } else { + return true; + } + } + return false; + } +} +``` \ No newline at end of file diff --git a/Java/Intersection of Two Linked Lists.java b/Java/Intersection of Two Linked Lists.java old mode 100644 new mode 100755 index 28078fa..a7d64e6 --- a/Java/Intersection of Two Linked Lists.java +++ b/Java/Intersection of Two Linked Lists.java @@ -1,5 +1,15 @@ -长短list,找重合点。 -长度不同的话,切掉长的那个的extra length。 那么起点一样后,重合点就会同时到达。 +E +1525664839 +tags: Linked List + +给两个 linked list, 问从哪个node开始, 两个 linked list 开始有重复? + +#### Basics +- 长短list,找重合点 +- 长度不同的话,切掉长的list那个的extra length +- 那么起点一样后,重合点就会同时到达 +- Time O(n) * 2, constant space + ``` /* Write a program to find the node at which the intersection of two singly linked lists begins. @@ -24,6 +34,51 @@ Your code should preferably run in O(n) time and use only O(1) memory. Tags Expand Linked List */ +// Count list length for headA, headB +// Shift the longer list and compare +public class Solution { + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + if (headA == null || headB == null) { + return null; + } + int countA = calcListLength(headA); + int countB = calcListLength(headB); + int diff = Math.abs(countA - countB); + + if (countA > countB) { + headA = shiftNode(headA, diff); + } else { + headB = shiftNode(headB, diff); + } + + while (headA != null && headB != null) { + if (headA == headB) { + return headA; + } + headA = headA.next; + headB = headB.next; + } + + return null; + } + + private int calcListLength(ListNode node) { + int count = 0; + while (node != null) { + count++; + node = node.next; + } + return count; + } + + private ListNode shiftNode(ListNode node, int shift) { + while (shift != 0) { + shift--; + node = node.next; + } + return node; + } +} /* Thoughts: diff --git a/Java/Interval Minimum Number.java b/Java/Interval Minimum Number.java old mode 100644 new mode 100755 index 13b5047..e83b942 --- a/Java/Interval Minimum Number.java +++ b/Java/Interval Minimum Number.java @@ -1,12 +1,18 @@ -又一个Segment tree的例子。 -把min number存在区间里面。 +M +1527994488 +tags: Segment Tree, Binary Search, Divide and Conquer, Lint -类似的有存:max, sum, min, count +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min -如果考到的几率不高。那么这一系列题目就是练习写代码的能力,和举一反三的心态。 ``` /* -Given an integer array (index from 0 to n-1, where n is the size of this array), and an query list. Each query has two integers [start, end]. For each query, calculate the minimum number between index start and end in the given array, return the result list. +Given an integer array (index from 0 to n-1, where n is the size of this array), and an query list. +Each query has two integers [start, end]. For each query, calculate the minimum number between +index start and end in the given array, return the result list. Example For array [1,2,7,8,5], and queries [(1,2),(0,4),(2,4)], return [2,1,5] @@ -25,6 +31,7 @@ Given an integer array (index from 0 to n-1, where n is the size of this array), Thoughts: Build a SegmentMinTree. Do search using the interval + 如果考到的几率不高。那么这一系列题目就是练习写代码的能力,和举一反三的心态。 */ /** @@ -62,7 +69,7 @@ public SegmentMinTreeNode build(int start, int end, int[] A) { return node; } - + //Query method public int search(SegmentMinTreeNode root, int start, int end){ if (root.start == start && root.end == end) { return root.min; @@ -98,21 +105,4 @@ public ArrayList intervalMinNumber(int[] A, ArrayList queries } } - - - - - - - - - - - - - - - - - ``` \ No newline at end of file diff --git a/Java/Interval Sum II.java b/Java/Interval Sum II.java old mode 100644 new mode 100755 index 32e5a9d..803b030 --- a/Java/Interval Sum II.java +++ b/Java/Interval Sum II.java @@ -1,5 +1,14 @@ -这个题如果一上来就是做,拿的确烧脑,原因只有一个:太长了呀。就像Expression Tree一样。一旦知道了怎么做,就简单了,可如果生疏,也就难。 -这题是好几个SegmentTree的结合,但是没什么创新的。 +H +1532902848 +tags: Segment Tree, Binary Search, Lint + +SegmentTree大集合. Methods: `build, query, modify`. 不难。只是要都记得不犯错. + +#### Segment Tree +- build: recursively build children based on index-mid and link to curr node +- query: recursively try to find `node.start == targetStart && node.end == targetEnd`. Compare with node.mid +- modify: recursively try to find `node.start == targetStart && node.end == targetEnd`; modify and recursively assign upper interval with updated interval property. + ``` /* Given an integer array in the construct method, implement two methods query(start, end) and modify(index, value): @@ -34,7 +43,6 @@ For modify(index, value), modify the number in the given index to value 4. modify: binary search, and re-compare the max at each level. */ public class Solution { - /* you may need to use some attributes here */ class SegmentSumTreeNode { int start,end; long sum; @@ -43,44 +51,37 @@ public SegmentSumTreeNode(int start, int end, long sum){ this.start = start; this.end = end; this.sum = sum; - this.left = null; - this.right = null; } } - public SegmentSumTreeNode build(int start, int end, int[] A) { - if (start == end) { - return new SegmentSumTreeNode(start, end, A[start]); - } - int mid = (start + end)/2; - SegmentSumTreeNode left = build(start, mid, A); - SegmentSumTreeNode right = build(mid + 1, end, A); - - SegmentSumTreeNode node = new SegmentSumTreeNode(start, end, left.sum + right.sum); - node.left = left; - node.right = right; - return node; - } SegmentSumTreeNode root = null; - /** - * @param A: An integer array - */ public Solution(int[] A) { - if (A == null || A.length == 0) { - return; - } + if (A == null || A.length == 0) return; root = build(0, A.length - 1, A); } - /** - * @param start, end: Indices - * @return: The sum from start to end - */ public long query(int start, int end) { return queryHelper(root, start, end); } + + public void modify(int index, int value) { + modifyHelper(root, index, value); + } + + private SegmentSumTreeNode build(int start, int end, int[] A) { + if (start == end) return new SegmentSumTreeNode(start, end, A[start]); + + int mid = (start + end)/2; + SegmentSumTreeNode left = build(start, mid, A); + SegmentSumTreeNode right = build(mid + 1, end, A); - public long queryHelper(SegmentSumTreeNode root, int start, int end){ + SegmentSumTreeNode node = new SegmentSumTreeNode(start, end, left.sum + right.sum); + node.left = left; + node.right = right; + return node; + } + + private long queryHelper(SegmentSumTreeNode root, int start, int end){ if (start > end) { return 0; } else if (root.start == start && root.end == end) { @@ -94,15 +95,8 @@ public long queryHelper(SegmentSumTreeNode root, int start, int end){ } return queryHelper(root.left, start, root.left.end) + queryHelper(root.right, root.right.start, end); } - - /** - * @param index, value: modify A[index] to value. - */ - public void modify(int index, int value) { - modifyHelper(root, index, value); - } - public void modifyHelper(SegmentSumTreeNode node, int index, int value) { + private void modifyHelper(SegmentSumTreeNode node, int index, int value) { if (node.start == index && node.end == index) { node.sum = value; return; @@ -115,9 +109,6 @@ public void modifyHelper(SegmentSumTreeNode node, int index, int value) { } node.sum = node.left.sum + node.right.sum; } - - } - ``` \ No newline at end of file diff --git a/Java/Interval Sum.java b/Java/Interval Sum.java old mode 100644 new mode 100755 index 0e18dfe..3be0bae --- a/Java/Interval Sum.java +++ b/Java/Interval Sum.java @@ -1,11 +1,20 @@ -其实是segment tree 每个node上面加个sum。 -构建tree记得怎么弄就好了。很简单的。 -但是,我犯了错..在search的时候求mid时,忘记了以root为基准(我用interval.start and interval.end为基准,当然错了) -但实际上search起来就是binary search的想法,在interval/segment tree上面跑。顺顺哒。 +M +1527997211 +tags: Segment Tree, Binary Search, Lint + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的sum. + +#### Segment Tree + Binary Search +- 其实是segment tree 每个node上面加个sum +- 记得Segment Tree methods: Build, Query +- Note: 存在SegmentTreeNode里面的是sum. 其他题目可能是min,max,count ... or something else. + ``` /* -Given an integer array (index from 0 to n-1, where n is the size of this array), and an query list. Each query has two integers [start, end]. For each query, calculate the sum number between index start and end in the given array, return the result list. +Given an integer array (index from 0 to n-1, where n is the size of this array), +and an query list. Each query has two integers [start, end]. +For each query, calculate the sum number between index start and end in the given array, return the result list. Example For array [1,2,7,8,5], and queries [(0,4),(1,2),(2,4)], return [23,9,20] @@ -30,7 +39,9 @@ Given an integer array (index from 0 to n-1, where n is the size of this array), * this.start = start; * this.end = end; * } + * } */ + public class Solution { public class SegmentSumTreeNode { public int start, end; @@ -44,14 +55,30 @@ public SegmentSumTreeNode(int start, int end, long sum) { this.right = null; } } + + /** + *@param A, queries: Given an integer array and an query list + *@return: The result list + */ + public List intervalSum(int[] A, List queries) { + List rst = new ArrayList<>(); + if (A == null || A.length == 0 || queries == null || queries.size() == 0) { + return rst; + } + SegmentSumTreeNode root = build(A, 0, A.length - 1); - public SegmentSumTreeNode buildTree(int[] A, int start, int end) { - if (start == end) { - return new SegmentSumTreeNode(start, end, A[start]); - } - int mid = (start + end)/2; - SegmentSumTreeNode leftChild = buildTree(A, start, mid); - SegmentSumTreeNode rightChid = buildTree(A, mid + 1, end); + for (Interval range : queries) { + rst.add(query(root, range.start, range.end)); + } + return rst; + } + + private SegmentSumTreeNode build(int[] A, int start, int end) { + if (start == end) return new SegmentSumTreeNode(start, end, A[start]); + + int mid = (start + end) / 2; + SegmentSumTreeNode leftChild = build(A, start, mid); + SegmentSumTreeNode rightChid = build(A, mid + 1, end); SegmentSumTreeNode node = new SegmentSumTreeNode(start, end, leftChild.sum + rightChid.sum); node.left = leftChild; @@ -60,50 +87,18 @@ public SegmentSumTreeNode buildTree(int[] A, int start, int end) { return node; } - public long searchTree(SegmentSumTreeNode root, int start, int end) { - if (root.start == start && root.end == end) { - return root.sum; - } - int mid = (root.start + root.end)/2; + private long query(SegmentSumTreeNode root, int start, int end) { + if (root.start == start && root.end == end) return root.sum; + + int mid = (root.start + root.end) / 2; if (end <= mid) { - return searchTree(root.left, start, end); + return query(root.left, start, end); } else if (start > mid) { - return searchTree(root.right, start, end); + return query(root.right, start, end); } //start <= mid < end - return searchTree(root.left, start, root.left.end) + searchTree(root.right, root.right.start, end); + return query(root.left, start, root.left.end) + query(root.right, root.right.start, end); } - - /** - *@param A, queries: Given an integer array and an query list - *@return: The result list - */ - public ArrayList intervalSum(int[] A, ArrayList queries) { - ArrayList rst = new ArrayList(); - if (A == null || A.length == 0 || queries == null || queries.size() == 0) { - return rst; - } - SegmentSumTreeNode root = buildTree(A, 0, A.length - 1); - - for (Interval range : queries) { - long sum = 0; - /* - //Check for errors, but don't have to do these checks - //Well, it's being checked in segment query II - if (range.start < root.start && range.end > root.end) { - sum = root.sum; - } else if (range.start < root.start && range.end <= root.end) { - sum = searchTree(root, root.start, range.end); - } else if (range.start >= root.start && range.end > root.end) { - sum = searchTree(root, range.start, root.end); - } else { - sum = searchTree(root, range.start, range.end); - }*/ - sum = searchTree(root, range.start, range.end); - rst.add(sum); - } - return rst; - } - } + ``` \ No newline at end of file diff --git a/Java/Invert Binary Tree.java b/Java/Invert Binary Tree.java old mode 100644 new mode 100755 index 181aa0d..14206f2 --- a/Java/Invert Binary Tree.java +++ b/Java/Invert Binary Tree.java @@ -1,3 +1,17 @@ +E +1525668228 +tags: Tree, DFS, BFS + +#### DFS +- 简单处理swap +- recursively swap children + +#### BFS +- BFS with Queue +- 每次process一个node, swap children; 然后把child加进queue里面 +- 直到queue process完 + +``` /* Invert a binary tree. @@ -13,11 +27,6 @@ Tags Expand Binary Tree -Thoughts: -1. Swap every node's left and right child. Recursion seems good. - -2. If not recursion, can use a queue to keep track of nodes. Keep swapping until the queue -is processed. */ @@ -31,52 +40,54 @@ * this.left = this.right = null; * } * } - */ +*/ + + +/* +Thoughts: +Use a queue to keep track of nodes. Keep swapping until the queue is processed. +*/ public class Solution { - /** - * @param root: a TreeNode, the root of the binary tree - * @return: nothing - */ public void invertBinaryTree(TreeNode root) { if (root == null) { - return; + return; } - Queue queue = new LinkedList(); + Queue queue = new LinkedList<>(); queue.offer(root); while(!queue.isEmpty()) { - TreeNode node = queue.poll(); - TreeNode temp = node.left; - node.left = node.right; - node.right = temp; - if (node.left != null) { - queue.offer(node.left); - } - if (node.right != null) { - queue.offer(node.right); - } + TreeNode node = queue.poll(); + TreeNode temp = node.left; + node.left = node.right; + node.right = temp; + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } } } } -//Now, solution 2, try recursion. -public class Solution { - /** - * @param root: a TreeNode, the root of the binary tree - * @return: nothing - */ - public void invertBinaryTree(TreeNode root) { +/* +Thoughts: swap every left && right +*/ +class Solution { + public TreeNode invertTree(TreeNode root) { if (root == null) { - return; + return root; } - TreeNode temp = root.left; - root.left = root.right; - root.right = temp; - - invertBinaryTree(root.left); - invertBinaryTree(root.right); + TreeNode temp = root.left; + root.left = root.right; + root.right = temp; + invertTree(root.left); + invertTree(root.right); + + return root; } } +``` \ No newline at end of file diff --git a/Java/Island Perimeter.java b/Java/Island Perimeter.java new file mode 100644 index 0000000..f77de4e --- /dev/null +++ b/Java/Island Perimeter.java @@ -0,0 +1,124 @@ +E +1516263328 +tags: Hash Table + +#### Brutle +- 每个格子4个墙;每个shared的墙要-2 (墙是两面, -1 * 2) +- 最后合计结果就好. + +#### Hash Table +- 不必想太多用HashMap做.但是也可以思考一下: +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么久可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不打,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + +``` +/* +You are given a map in form of a two-dimensional integer grid +where 1 represents land and 0 represents water. +Grid cells are connected horizontally/vertically (not diagonally). +The grid is completely surrounded by water, and there is exactly one island +(i.e., one or more connected land cells). + +The island doesn't have "lakes" (water inside that isn't connected to the water around the island). +One cell is a square with side length 1. + +The grid is rectangular, width and height don't exceed 100. +Determine the perimeter of the island. + +Example: + +[[0,1,0,0], + [1,1,1,0], + [0,1,0,0], + [1,1,0,0]] + +Answer: 16 +https://leetcode.com/problems/island-perimeter/description/ +*/ + +/* +DFS all of the 1's +1. each island gives 4 walls +2. one side of shared wall will cause -1. Each shared wall will be counted 2 times, from each size of the wall. +*/ +class Solution { + + public int islandPerimeter(int[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + final int[] dx = {1, -1, 0, 0}; + final int[] dy = {0, 0, 1, -1}; + + int count = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == 1) { + count += 4; + for (int k = 0; k < dx.length; k++) { + int mX = i + dx[k]; + int mY = j + dy[k]; + if (mX >= 0 && mY >= 0 && mX < grid.length && mY < grid[0].length && grid[mX][mY] == 1) { + count -= 1; + } + } + } + } + } + return count; + } +} + +// incomplete version using DFS. The code became complicated and not necessary. +/* +class Solution { + private final int[] dx = {1, -1, 0, 0}; + private final int[] dy = {0, 0, 1, -1}; + int blocks = 0; + int walls = 0; + public int islandPerimeter(int[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + final Map> map = new HashMap<>(); + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == 1) { + dfs(map, grid, i, j); + return blocks * 4 - walls * 2; + } + } + } + return 0; + } + + private void dfs(Map> map, int[][] grid, int x, int y) { + if (grid[x][y] != 1) { + return; + } + + grid[x][y] = -1; + int index = calculateIndex(grid[0].length, x, y); + if (!map.containsKey(index)) { + map.put(index, new ArrayList<>()); + } + + for (int i = 0; i < dx.length; i++) { + int mX = x + dx[i]; + int mY = y + dy[i]; + if (mX >= 0 && mY >= 0 && mX < grid.length && mY < grid[0].length && grid[mX][mY] != 0) { + map.put(index, calculateIndex(grid[0].length, mX, mY)); + dfs(grid, mX, mY); + } + } + } + + private calculateIndex(int width, int x, int y) { + return width * x + y; + } +}*/ + +``` diff --git a/Java/Isomorphic Strings.java b/Java/Isomorphic Strings.java new file mode 100755 index 0000000..df36d17 --- /dev/null +++ b/Java/Isomorphic Strings.java @@ -0,0 +1,83 @@ +E +1534215677 +tags: Hash Table + +#### HashMap +- two failture cases: +- same key, value not matching +- two key maps to same value + +#### Previous note +1. Match. 就是map.containsKey, map.containsValue, and char1 == char2. Perfect. +2. Either Key not exist, or Value not exit. False; +3. Both key and Value exist, but map.get(char1) != char2. Miss-match. False. +4. None of Key or Value exist in HashMap. Then add the match. + +``` +/* +Given two strings s and t, determine if they are isomorphic. + +Two strings are isomorphic if the characters in s can be replaced to get t. + +All occurrences of a character must be replaced with another character while preserving the order of characters. No two characters may map to the same character but a character may map to itself. + +For example, +Given "egg", "add", return true. + +Given "foo", "bar", return false. + +Given "paper", "title", return true. + +Note: +You may assume both s and t have the same length. + +Hide Company Tags LinkedIn +Hide Tags Hash Table +Hide Similar Problems (E) Word Pattern + +*/ + +class Solution { + public boolean isIsomorphic(String s, String t) { + if (s == null || t == null || s.length() != t.length()) return false; + Map map = new HashMap<>(); + + for (int i = 0; i < s.length(); i++) { + char charS = s.charAt(i), charT = t.charAt(i); + if (map.containsKey(charS)) { + if (map.get(charS) != charT) return false; // same key, value not matching + } else { + if (map.containsValue(charT)) return false; // two key maps to same value + map.put(charS, charT); + } + } + + return true; + } +} + +//Use hashMap to store matches. If conflict, return false +public class Solution { + public boolean isIsomorphic(String s, String t) { + if (s == null || t == null) { + return false; + } else if (s.equals(t)) { + return true; + } + HashMap map = new HashMap(); + for (int i = 0; i < s.length(); i++) { + char charS = s.charAt(i); + char charT = t.charAt(i); + + if (!map.containsKey(charS) && !map.containsValue(charT)) { + map.put(charS, charT); + } else if (!map.containsKey(charS) || !map.containsValue(charT)) { + return false; + } else if (map.containsKey(charS) && map.containsValue(charT) && map.get(charS) != charT) { + return false; + } + } + return true; + } +} +``` diff --git a/Java/Jewels and Stones.java b/Java/Jewels and Stones.java new file mode 100644 index 0000000..459872b --- /dev/null +++ b/Java/Jewels and Stones.java @@ -0,0 +1,54 @@ +E +tags: Hash Table +1524017454 + +给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 + +#### Basic HashSet + +``` +/* +You're given strings J representing the types of stones that are jewels, +and S representing the stones you have. Each character in S is a type of stone you have. +You want to know how many of the stones you have are also jewels. + +The letters in J are guaranteed distinct, and all characters in J and S are letters. +Letters are case sensitive, so "a" is considered a different type of stone from "A". + +Example 1: + +Input: J = "aA", S = "aAAbbbb" +Output: 3 +Example 2: + +Input: J = "z", S = "ZZ" +Output: 0 +Note: + +S and J will consist of letters and have length at most 50. +The characters in J are distinct. +*/ + +/* +Thoughts: +Have jewels in set. Iterate over S to check existance and count. +O(m + n), m = J.length(), n = S.length() +*/ +class Solution { + public int numJewelsInStones(String J, String S) { + if (J == null || J.length() == 0 || S == null || S.length() == 0) { + return 0; + } + int m = J.length(); + Set set = new HashSet<>(); + int count = 0; + for (char c : J.toCharArray()) { + set.add(c); + } + for (char c : S.toCharArray()) { + count += set.contains(c) ? 1 : 0; + } + return count; + } +} +``` \ No newline at end of file diff --git a/Java/Judge Route Circle.java b/Java/Judge Route Circle.java new file mode 100644 index 0000000..172ee9c --- /dev/null +++ b/Java/Judge Route Circle.java @@ -0,0 +1,54 @@ +E +1516256263 +tags: String + +简单的character checking. 各个方向, 加加减减. + +``` + +/* +Initially, there is a Robot at position (0, 0). Given a sequence of its moves, judge if this robot makes a circle, which means it moves back to the original place. + +The move sequence is represented by a string. And each move is represent by a character. The valid robot moves are R (Right), L (Left), U (Up) and D (down). The output should be true or false representing whether the robot makes a circle. + +Example 1: +Input: "UD" +Output: true +Example 2: +Input: "LL" +Output: false +*/ + +/* +Thoughts: +Each letter represent a movement: +U: {0, 1} +D: {0, -1} +R: {1, 0} +L: {-1, 0} +Just add them all together O(n). + +Or, even easier: +Add all of U && D; check if equal to 0. +Add all of R && L; check if equal to 0 +*/ + +class Solution { + public boolean judgeCircle(String moves) { + if (moves == null || moves.length() % 2 == 1) { + return false; + } + int trackX = 0; + int trackY = 0; + for (int i = 0; i < moves.length(); i++) { + if (moves.charAt(i) == 'U' || moves.charAt(i) == 'D') { + trackY += moves.charAt(i) == 'U' ? 1 : -1; + } + if (moves.charAt(i) == 'L' || moves.charAt(i) == 'R') { + trackX += moves.charAt(i) == 'L' ? 1 : -1; + } + } + return trackX == 0 && trackY == 0; + } +} +``` \ No newline at end of file diff --git a/Java/Jump Game II.java b/Java/Jump Game II.java old mode 100644 new mode 100755 index 26720f0..7f9b1ca --- a/Java/Jump Game II.java +++ b/Java/Jump Game II.java @@ -1,5 +1,43 @@ +H +1530083174 +tags: Array, Greedy, DP, Coordinate DP + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + +``` /* -Given an array of non-negative integers, you are initially positioned at the first index of the array. +Given an array of non-negative integers, +you are initially positioned at the first index of the array. Each element in the array represents your maximum jump length at that position. @@ -8,11 +46,18 @@ Example Given array A = [2,3,1,1,4] -The minimum number of jumps to reach the last index is 2. (Jump 1 step from index 0 to 1, then 3 steps to the last index.) +The minimum number of jumps to reach the last index is 2. +(Jump 1 step from index 0 to 1, then 3 steps to the last index.) Tags Expand Greedy Array +*/ + + + +/* + Thanks to Yu’s Garden blog Thinking process: 0. Use two pointers pStart and pEnd to track the potential locations we can move to. @@ -20,38 +65,105 @@ If no max can read the tail of array, that means we need to move on. At this point, let pStart = pEnd + 1. At same time, move pEnd to the max spot we can go to. Since pEnd moves forward, we could step++ If max reach the tail of array, return the steps. */ - +// 95% public class Solution { - /** - * @param A: A list of lists of integers - * @return: An integer - */ - public int jump(int[] A) { - if (A == null || A.length == 0) { + public int jump(int[] nums) { + if (nums == null || nums.length == 0) { return 0; } - int pStart = 0; - int pEnd = 0; - int steps = 0; - while (pEnd < A.length - 1) { - steps++; //Cound step everytime when pEnd is moving to the farthest. + int n = nums.length; + int start = 0, end = 0, steps = 0; + while (end < n - 1) { + steps++; //Cound step everytime when pEnd is moving to the farthest. int farthest = 0; //Find farest possible and see if reach the tail - for (int i = pStart; i <= pEnd; i++) { - farthest = Math.max(farthest, i + A[i]); - if (farthest >= A.length - 1) { + for (int i = start; i <= end; i++) { + farthest = Math.max(farthest, i + nums[i]); + if (farthest >= n - 1) { return steps; } } //Re-select pointer position for start and end - pStart = pEnd + 1; - pEnd = farthest; + start = end + 1; + end = farthest; } - return -1; //This is the case where no solution can be found. + return 0; //This is the case where no solution can be found. } } -//Also DP from nineChapter: -http://www.ninechapter.com/solutions/jump-game-ii/ +/* + Wihtin farest we can go, renew farest we can go, renew number of steps. + http://blog.csdn.net/havenoidea/article/details/11853301 + + 图解: + http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +*/ +//Greedy. Within max range we can jump to, do another loop to renew the max we can go. +public class Solution { + public int jump(int[] nums) { + if (nums == null || nums.length <= 1) { + return 0; + } + int index = 0; + int step = 0; + int range = 0; + int maxRange = 0; + + while (index < nums.length) { + if (range >= nums.length - 1) { + break; + } + while (index <= range) { + maxRange = Math.max(maxRange, index + nums[index]); + index++; + } + range = maxRange; + step++; + } + return step; + } +} + +// even simpler, 95%, O(n) +public class Solution { + public int jump(int[] nums) { + if (nums == null || nums.length <= 1) { + return 0; + } + int count = 0, farest = 0, maxRange = 0; + for (int i = 0; i < nums.length - 1; i++) { + max = Math.max(max, i + nums[i]); + if (i == farest) { + count++; + farest = max; + } + } + return count; + } +} + + +// DP, coordinate DP, timeout, O(n^2) +class Solution { + public int jump(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] dp = new int[n]; + for (int i = 1; i < n; i++) { + dp[i] = Integer.MAX_VALUE; + } + for (int i = 1; i < n; i++) { + for (int j = 0; j < i; j++) { + if (dp[j] != Integer.MAX_VALUE && j + nums[j] >= i) { + dp[i] = Math.min(dp[i], dp[j] + 1); + } + } + } + return dp[n - 1]; + } +} +``` \ No newline at end of file diff --git a/Java/Jump Game.java b/Java/Jump Game.java old mode 100644 new mode 100755 index 53664b9..fc61732 --- a/Java/Jump Game.java +++ b/Java/Jump Game.java @@ -1,3 +1,30 @@ +M +1522684902 +tags: Array, Greedy, DP + +给出步数,看能不能jump to end. + +#### Greedy - start from index = 0 +- Keep track of farest can go +- 一旦 farest >= nums.length - 1, 也就是到了头, 就可以停止, return true. +- 一旦 farest <= i, 也就是说, 在i点上, 已经走过了步数, 不能再往前跳, 于是 return false +- This can be done using DP. However, greedy algorithm is fast in this particular problem. + +#### Greedy - start from index = n - 1 +- greedy: start from end, and mark last index +- loop from i = [n - 2 -> 0], where i + nums[i] should always >= last index +- check if last == 0 when returning. It means: can we jump from index=0 to the end? +- Time: O(n), beat 100% + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (A[j] >= i - j), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- Time: O(n^2) + + +``` /* Given an array of non-negative integers, you are initially positioned at the first index of the array. @@ -5,74 +32,53 @@ Determine if you are able to reach the last index. -Example -A = [2,3,1,1,4], return true. +Example 1: -A = [3,2,1,0,4], return false. +Input: [2,3,1,1,4] +Output: true +Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index. +Example 2: -This can be done using DP. However, greedy algorithm is fast in this particular problem. Consider both solutions. +Input: [3,2,1,0,4] +Output: false +Explanation: You will always arrive at index 3 no matter what. Its maximum + jump length is 0, which makes it impossible to reach the last index. -DP -Thinking Process: -We have array A, that stores the # of steps for each index. -State: f[i] means if previous steps can reach to i. True/False -Function: f[i] = f[j] && (j + A[j] >= i) -Init: f[0] = true -Answer: f[n-1], if n is the length of A -*/ - -public class Solution { - /** - * @param A: A list of integers - * @return: The boolean answer - **/ - //DP - public boolean canJump(int[] A) { - if (A == null || A.length == 0) { - return false; - } - //By default, boolean[] can is all false - boolean[] can = new boolean[A.length]; - can[0] = true; - for (int i = 1; i < A.length; i++) { - for (int j = 0; j < i; j++) { - if (A[j] && (j + A[j] >= i)) { - can[i] = true; - break; - } - } - } - return can[A.length - 1]; - } -} +*/ /* Greedy. Ideas from Yu’s Garden -At each index, check how far we can jump, store this forest-can-jump position in variable ‘farest’. Take max of current farest and (index + A[index]), store is in farest +At each index, check how far we can jump, store this forest-can-jump position in variable ‘farest’. +Take max of current farest and (index + A[index]), store is in farest + At each index, compare if ‘farest’ is greater than the end of array, if so, found solution, return true. -At each index, also check if ‘farest == current index’, that means the farest we can move is to current index and we cannot move forward. Then return false. + +At each index, also check if ‘farest == current index’, +that means the farest we can move is to current index and we cannot move forward. +Then return false. */ -public class Solution { - /** - * @param A: A list of integers - * @return: The boolean answer - **/ - - public boolean canJump(int[] A) { - if (A == null || A.length == 0) { +/* +Thoughts: +Greedy. +Find a farest position at current position. +If farest <= i, that means it can't jump forward, false. +*/ +class Solution { + public boolean canJump(int[] nums) { + if (nums == null || nums.length == 0) { return false; } int farest = 0; - for (int i = 0; i < A.length; i++) { - farest = Math.max(farest, i + A[i]); - if (farest > A.length - 1) { + for (int i = 0; i < nums.length; i++) { + farest = Math.max(farest, i + nums[i]); + if (farest >= nums.length - 1) { return true; } - if (farest == i) { + if (farest <= i) { return false; } } @@ -80,3 +86,64 @@ public boolean canJump(int[] A) { } } +// even simpler, check from end. beat 100% +class Solution { + public boolean canJump(int[] nums) { + if (nums == null || nums.length == 0) { + return false; + } + int n = nums.length; + int last = n - 1; + for (int i = n - 2; i >= 0; i--) { + if (i + nums[i] >= last) last = i; + } + return last == 0; + } +} + +/* +Thoughts: +Can/Cannot -> DP. +dp[x] = if able to reach dp[x], store true/false +if (dp[x-j] >= 1), then able to reach dp[x] +becomes: if able to jump to dp[x-1]. +equation: +dp[x] = dp[j] && A[j] >= x - j +dp[0] = true +*/ +class Solution { + public boolean canJump(int[] nums) { + if (nums == null || nums.length == 0) { + return false; + } + int n = nums.length; + boolean[] dp = new boolean[n]; + + dp[0] = true; + + for (int i = 1; i < n; i++) { + for (int j = 0; j < i; j++) { + if (dp[j] && nums[j] >= (i - j)) { + dp[i] = true; + break; + } + } + } + + return dp[n - 1]; + } +} + +/* +Same solution as above. +Thinking Process: +We have array A, that stores the # of steps for each index. +State: dp[i] means if previous steps can reach to i. True/False +Function: dp[i] = dp[j] && (j + A[j] >= i) +Init: dp[0] = true +Answer: dp[n-1], if n is the length of A + */ + + + +``` \ No newline at end of file diff --git a/Java/K Edit Distance.java b/Java/K Edit Distance.java new file mode 100644 index 0000000..8e6410e --- /dev/null +++ b/Java/K Edit Distance.java @@ -0,0 +1,97 @@ +H +1522220157 +tags: Trie, DP, Double Sequence DP, Sequence DP + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + +``` +/* +LintCode +Given a set of strings which just has lower case letters and a target string, +output all the strings for each the edit distance with the target no greater than k. + +You have the following 3 operations permitted on a word: + +Insert a character +Delete a character +Replace a character + +Example +Given words = ["abc", "abd", "abcd", "adc"] and target = "ac", k = 1 +Return ["abc", "adc"] +*/ + +/* +Thoughts: +We can calculate min moves needed to reach target with dp +dp[i][j] represents # steps to convert S[0, i - 1] to T[0, j - 1] +dp[m][n] is the minimum moves + +It's double sequence dp, initialize with [m+1][n+1] +dp[0][0] = 0. no sequence to compare/edit. +init: if i == 0, take j steps to become T. if j == 0, takes i steps to become S. + +Apply the dp for all words + +Some pre-validations: +- length diff > k, skip +- equal to target: just return +*/ +public class Solution { + public List kDistance(String[] words, String target, int k) { + List rst = new ArrayList<>(); + if (words == null || words.length == 0 || target == null || k <= 0) { + return rst; + } + for (String word: words) { + if (validate(word, target, k)) { + rst.add(word); + } + } + return rst; + } + + private boolean validate(String word, String target, int k) { + if (word.equals(target)) { + return true; + } + if (Math.abs(word.length() - target.length()) > k) { + return false; + } + + int m = word.length(); + int n = target.length(); + + int[][] dp = new int[2][n + 1]; + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (i == 0) { + dp[i % 2][j] = j; + continue; + } + if (j == 0) { + dp[i % 2][j] = i; + continue; + } + + dp[i % 2][j] = Math.min(dp[(i - 1) % 2][j - 1], Math.min(dp[i % 2][j - 1], dp[(i - 1) % 2][j])) + 1; + if (word.charAt(i - 1) == target.charAt(j - 1)) { + dp[i % 2][j] = Math.min(dp[i % 2][j], dp[(i - 1) % 2][j - 1]); + } + } + } + return dp[m % 2][n] <= k; + } +} +``` \ No newline at end of file diff --git a/Java/K Empty Slots.java b/Java/K Empty Slots.java new file mode 100644 index 0000000..603e9ae --- /dev/null +++ b/Java/K Empty Slots.java @@ -0,0 +1,116 @@ +H +1528420589 +tags: Array, BST, TreeSet + +题目解析后: find 2 number, that: 1. k slots between the 2 number, 2. no slots taken between the two number. + +#### BST +- BST structure not given, use TreeSet to build BST with each node +- Every time find last/next inorder element +- `treeSet.lower(x)`, `treeSet.higher(x)` +- 一旦位置相隔(k + 1), 就满足题目条件 +- O(nlogn), good enough + +#### Track slots of days +- Reverse the array, save days index into days[], where the new index is slot. +- days[i]: at slot i, which day a flower will be planted +- O(n) +- Needs to understand: http://www.cnblogs.com/grandyang/p/8415880.html + +``` +/* +There is a garden with N slots. In each slot, there is a flower. +The N flowers will bloom one by one in N days. In each day, +there will be exactly one flower blooming and it will be in the status of blooming since then. + +Given an array flowers consists of number from 1 to N. +Each number in the array represents the place where the flower will open in that day. + +For example, flowers[i] = x means that the unique flower that blooms at day i will be at position x, +where i and x will be in the range from 1 to N. + +Also given an integer k, you need to output in which day there exists two flowers in the status of blooming, +and also the number of flowers between them is k and these flowers are not blooming. + +If there isn't such day, output -1. + +Example 1: +Input: +flowers: [1,3,2] +k: 1 +Output: 2 +Explanation: In the second day, the first and the third flower have become blooming. +Example 2: +Input: +flowers: [1,2,3] +k: 1 +Output: -1 +Note: +The given array will be in the range [1, 20000]. + +*/ + +/* +Thoughts: +Goal is to find 2 number, that is k distance from the other, and no nodes between them. +- Build Binary Search Tree using flow position from flowers[], insert node each day +- Ingest iteratively and check if there is inorder node that has value diff of (k+1) +- example: [1, 100, 50, 20, 3, 2] => in BST, 1 and 3 are consecutive in inorder sequence, valid. +NOTE: When BST not given, just use TreeSet: treeSet.add(x), treeSet.lower(x), treeSet.higher(x) +Time, O(nlogn) +*/ + +class Solution { + public int kEmptySlots(int[] flowers, int k) { + // check edge case + if (flowers == null || flowers.length == 0 || k < 0) { + return - 1; + } + + // build binary tree with each node + TreeSet treeSet = new TreeSet<>(); + for (int i = 0; i < flowers.length; i++) { + int num = flowers[i]; + treeSet.add(num); + Integer lower = treeSet.lower(num); + Integer higher = treeSet.higher(num); + if (higher != null && higher - num == k + 1 ) { + return i + 1; + } + if (lower != null && num - lower == k + 1) { + return i + 1; + } + } + + return -1; + } +} + + +// Track days of slot of days +class Solution { + public int kEmptySlots(int[] flowers, int k) { + if (flowers == null || flowers.length == 0 || k < 0) { + return - 1; + } + int n = flowers.length; + int[] days = new int[n]; + for (int i = 0; i < n; i++) { + days[flowers[i] - 1] = i + 1; // 1-based + } + + int left = 0, right = k + 1, result = Integer.MAX_VALUE; + for (int i = 0; right < n; i++) { + if (days[i] < days[left] || days[i] <= days[right]) { + if (i == right) { + result = Math.min(result, Math.max(days[left], days[right])); + } + left = i; + right = k + 1 + i; + } + } + return result == Integer.MAX_VALUE ? - 1 : result; + } +} + +``` \ No newline at end of file diff --git a/Java/Kth Largest Element in an Array.java b/Java/Kth Largest Element in an Array.java new file mode 100644 index 0000000..b75dead --- /dev/null +++ b/Java/Kth Largest Element in an Array.java @@ -0,0 +1,180 @@ +M +1533137926 +tags: Divide and Conquer, Heap, PriorityQueue, MinHeap, Quick Sort + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high queue = new PriorityQueue<>(); // min-heap + + for (int i = 0; i < nums.length; i++) { + if (i < k || nums[i] > queue.peek()) queue.offer(nums[i]); + if (queue.size() > k) queue.poll(); + } + + return queue.poll(); + } +} + + +// Quick sort/ partition +// Partition to return the `low` index, which should match targetIndex. +class Solution { + public int findKthLargest(int[] nums, int k) { + if (nums == null || nums.length == 0) return -1; + int n = nums.length; + return partition(nums, 0, n - 1, n - k); + } + + private int partition (int[] nums, int start, int end, int targetIndex) { + // define low/high + int pivot = end; + int low = start, high = end, num = nums[pivot]; + + // move pointer and swap + while (low < high) { + while (low < high && nums[low] < num) { + low++; + } + while (low < high && nums[high] >= num) { + high--; + } + swap(nums, low, high); + } + swap(nums, low, pivot); + + // compare if low == targetIndex; or recursively partition to find targetIndex + if (low == targetIndex) { + return nums[low]; + } else if (low < targetIndex) { + return partition(nums, low + 1, end, targetIndex); + } else { + return partition(nums, start, low - 1, targetIndex); + } + } + + private void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + +/* +LintCode +Find K-th largest element in an array. + +Example +In array [9,3,2,4,8], the 3rd largest element is 4 + +In array [1,2,3,4,5], the 1st largest element is 5, +2nd largest element is 4, 3rd largest element is 3 and etc. + +Note +You can swap elements in the array + +Challenge +O(n) time, O(1) space + +Tags Expand +Quick Sort Sort + +*/ + +/* + +Thoughts: +Almost the same as the Median problem: +the only difference is, this one is not looking for the middle point, but for the last kth element. +Using the same quick sort code with minor modifications, and we can solve this problem. +*/ + +class Solution { + //param k : description of k + //param numbers : array of numbers + //return: description of return + public int kthLargestElement(int k, ArrayList nums) { + if (nums == null || nums.size() == 0) { + return 0; + } + return helper(nums, 0, nums.size() - 1, nums.size() - k); + } + + public void swap( ArrayListnums, int x, int y){ + int temp = nums.get(x); + nums.set(x, nums.get(y)); + nums.set(y, temp); + } + + public int helper( ArrayList nums, int start, int end, int mid) { + int pivot = end; + int num = nums.get(pivot); + int low = start; + int high = end; + while (low < high) { + while(low < high && nums.get(low) < num) { + low++; + } + while(low < high && nums.get(high) >= num) { + high--; + } + swap(nums, low, high); + } + swap(nums, low, pivot); + if (low == mid) { + return nums.get(low); + } else if (low < mid) { + return helper(nums, low + 1, end, mid); + } else { + return helper(nums, start, low - 1, mid); + } + } +}; + +``` \ No newline at end of file diff --git a/Java/Kth Smallest Element in a BST.java b/Java/Kth Smallest Element in a BST.java new file mode 100755 index 0000000..1de4789 --- /dev/null +++ b/Java/Kth Smallest Element in a BST.java @@ -0,0 +1,131 @@ +M +1527998417 +tags: BST, Tree, DFS, Stack + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + +``` +/* +Given a binary search tree, write a function kthSmallest to find the kth smallest element in it. + +Note: +You may assume k is always valid, 1 ≤ k ≤ BST's total elements. + +Follow up: +What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine? + +Hint: + +Try to utilize the property of a BST. +What if you could modify the BST node's structure? +The optimal runtime complexity is O(height of BST). +Credits: +Special thanks to @ts for adding this problem and creating all test cases. + +Hide Company Tags Google +Hide Tags Tree Binary Search +Hide Similar Problems (M) Binary Tree Inorder Traversal + +*/ + +/* + + Based on binary seach tree, just do a in-order-traversal. + Store in rst. +*/ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + + +/* + //Iterative + + Add all left. + pop top (which will be left-most node) + set node = node.right; + if right != null, add to stack. Will trigger the left-adding-while-loop + if right == null, now node = null. Will not trigger the left-adding-whilte-loop +*/ +public class Solution { + public int kthSmallest(TreeNode root, int k) { + if (root == null || k <= 0) { + return -1; + } + + Stack stack = new Stack<>(); + stack.push(root); + TreeNode node = root; + while(!stack.isEmpty()) { + //Left first + while (node != null && node.left != null) { + stack.add(node.left); + node = node.left; + } + //Process left/curr + node = stack.pop(); + k--; + if (k == 0) { + return node.val; + } + node = node.right; + if (node != null) { + stack.push(node); + } + } + return -1; + } +} + +// Recursive +public class Solution { + public int kthSmallest(TreeNode root, int k) { + if (root == null || k <= 0) { + return -1; + } + + ArrayList rst = new ArrayList(); + helper(rst, root, k); + + if (rst.size() < k) { + return -1; + } + return rst.get(k - 1).val; + } + + + public void helper(ArrayList rst, TreeNode node, int k) { + if (rst.size() == k) { + return; + } + if (node.left == null && node.right == null) { + rst.add(node); + return; + } + + if (node.left != null) { + helper(rst, node.left, k); + } + rst.add(node); + if (node.right != null) { + helper(rst, node.right, k); + } + } +} + + + + +``` \ No newline at end of file diff --git a/Java/Kth Smallest Element in a Sorted Matrix.java b/Java/Kth Smallest Element in a Sorted Matrix.java new file mode 100755 index 0000000..d20806f --- /dev/null +++ b/Java/Kth Smallest Element in a Sorted Matrix.java @@ -0,0 +1,208 @@ +M +1520234108 +tags: Binary Search, Heap +time: O(n + klogn) +space: O(n) + +给一个sorted matrix, 找kth smallest number (not distinct) + +Related: `Kth Largest Element in an Array` + +#### PriorityQueue +- 和Merge K sorted Array/ List 类似:使用PriorityQueue。 +- 因为Array的element无法直接找到next,所以用一个class Node 存value, x,y positions. +- Initial O(n) time, also find k O(k), sort O(logn) => O(n + klogn) +- 变型: Kth Largest in N Arrays + +#### Binary Search +- we know where the boundary is start/end are the min/max value. +- locate the kth smallest item (x, y) by cutt off partition in binary fasion: +- find mid-value, and count # of items < mid-value based on the ascending matrix +- O(nlogn) + + +``` +/* +LeetCode: +Given a n x n matrix where each of the rows and columns are sorted in ascending order, +find the kth smallest element in the matrix. + +Note that it is the kth smallest element in the sorted order, not the kth distinct element. + +Example: + +matrix = [ + [ 1, 5, 9], + [10, 11, 13], + [12, 13, 15] +], +k = 8, + +return 13. +Note: +You may assume k is always valid, 1 ≤ k ≤ n^2. + */ + +/* +Thougths: +Like merge k sorted list: +1. Append the head using priority queue +2. Keep adding to the queue and removing elments. + +O(k) space +O(n + klogk) time if going through all elements +*/ +class Solution { + class Node { + int x; + int y; + int val; + public Node(int x, int y, int val) { + this.x = x; + this.y = y; + this.val = val; + } + } + + public int kthSmallest(int[][] matrix, int k) { + if (matrix == null || matrix.length == 0 || k <= 0) { + return 0; + } + int n = matrix.length; + Queue queue = new PriorityQueue(new Comparator(){ + public int compare(Node a, Node b){ + return a.val - b.val; + } + }); + + // Initialize the queue with head elements + for (int i = 0; i < n; i++) { + queue.offer(new Node(i, 0, matrix[i][0])); + } + + while (!queue.isEmpty()) { + Node node = queue.poll(); + if (k == 1) { + return node.val; + } + if (node.y + 1 < n) { + queue.offer(new Node(node.x, node.y + 1, matrix[node.x][node.y + 1])); + } + k--; + } + + return 0; + } +} + +// Binary Search +class Solution { + public int kthSmallest(int[][] matrix, int k) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0 || k <= 0) return -1; + + int n = matrix.length; + int min = matrix[0][0]; + int max = matrix[n - 1][n - 1]; + while (min < max) { + int target = min + (max - min) / 2; + int count = countSmallerItem(matrix, target); + if (count < k) { // need larger target + min = target + 1; // target is counted, skip over + } else { + max = target; + } + } + return min; + } + + // O(n) + private int countSmallerItem(int[][] matrix, int target) { + int n = matrix.length; + int count = 0; + int i = 0, j = n - 1; // n*n + while (i < n && j >= 0) { + if (matrix[i][j] > target) { //item too large, skip + j--; + } else { // meet requirement, add (j+1) since j is 0-based, also move on to next row + count += j + 1; + i++; + } + } + return count; + } +} + +/* +Find the kth smallest number in at row and column sorted matrix. + +Have you met this question in a real interview? Yes +Example +Given k = 4 and a matrix: + +[ + [1 ,5 ,7], + [3 ,7 ,8], + [4 ,8 ,9], +] +return 5 + +Challenge +O(k log n), n is the maximal number in width and height. + +Tags Expand +Heap Priority Queue Matrix + +*/ + + + +//PriorityQueue store front node. (Class Node), then output the kth in queue. +public class Solution { + class Node { + int val; + int x,y; + public Node(int val, int x, int y){ + this.val = val; + this.x = x; + this.y = y; + } + } + public int kthSmallest(int[][] matrix, int k) { + if (matrix == null || matrix[0] == null || matrix.length == 0 + || matrix[0].length == 0 || k <= 0) { + return -1; + } + + //Init queue + PriorityQueue queue = new PriorityQueue(k, + new Comparator(){ + public int compare(Node a, Node b) { + return a.val - b.val; + } + }); + + for (int i = 0; i < matrix.length; i++) { + if (matrix[i].length > 0) { + queue.offer(new Node(matrix[i][0], i, 0)); + } + } + + //Find kth + while (!queue.isEmpty()) { + Node node = queue.poll(); + if(k == 1) { + return node.val; + } + int x = node.x; + int y = node.y; + if (y < matrix[x].length - 1) { + queue.offer(new Node(matrix[x][y+1], x, y+1)); + } + k--; + } + + return -1; + } +} + +``` \ No newline at end of file diff --git a/Java/Kth Smallest Sum In Two Sorted Arrays.java b/Java/Kth Smallest Sum In Two Sorted Arrays.java old mode 100644 new mode 100755 index 1f14c84..650f0ff --- a/Java/Kth Smallest Sum In Two Sorted Arrays.java +++ b/Java/Kth Smallest Sum In Two Sorted Arrays.java @@ -1,15 +1,19 @@ -挺勇猛. 还好网上有个题目是找kth最大。 -用priority queue. 每次把最小的展开,移位。分别x+1,或者y+1。 -因为当下的Min里面x,y都是最小的。所以下一个最小的不是(x+1,y),就是(x,y+1)。当然,放在PriorityQueue里面的原因就是,很可能跟之前在queue里面的pair产生比较。 -每次就poll()一个,放新candidate进去就好了。 -注意,这样的做法会用重复,比如例子(7,4)会出现两次。用一个HashSet挡一下。 +H + -学会用priorityqueue. +用priority queue. 每次把最小的展开,移位。分别x+1,或者y+1: +因为当下的Min里面x,y都是最小的。所以下一个最小的不是(x+1,y),就是(x,y+1)。 + +每次就poll()一个,放2个新candidate进去就好了。 +注意,这样的做法会用重复,比如例子(7,4)会出现两次。用一个HashSet挡一下。 注意,HashSet的唯一性,用一个"x,y"的string就可以代为解决。 + ``` /* -Given two integer arrays sorted in ascending order and an integer k. Define sum = a + b, where a is an element from the first array and b is an element from the second one. Find the kth smallest sum out of all possible sums. +Given two integer arrays sorted in ascending order and an integer k. Define sum = a + b, +where a is an element from the first array and b is an element from the second one. +Find the kth smallest sum out of all possible sums. Example Given [1, 7, 11] and [2, 4, 6]. diff --git a/Java/LFU Cache.java b/Java/LFU Cache.java new file mode 100644 index 0000000..d4ffb8c --- /dev/null +++ b/Java/LFU Cache.java @@ -0,0 +1,189 @@ +H +tags: Design, Hash Table + +#### Hash Table +- 具体看thoughts, 几种不同的方式使用map +- `regular object map`: map of , where `item : {int val; int count}` +- Use a Map to track the frequency +- Track constant capacity, and minimum frequency +- Every get(): update all frequency map as well +- Every put(): update all frequency map as well, with optional removal (if over capacity) + +- Original post: http://www.cnblogs.com/grandyang/p/6258459.html +- TODO: one doubly linked list might be good enough to replace below: +- `frequency list map`: map of >, where the list preserves `recency` +- `item location in frequency map`: map of : +- index relative to the item in a particular list, not tracking which list here + +``` +/* +Design and implement a data structure for Least Frequently Used (LFU) cache. +It should support the following operations: get and put. + +get(key) - Get the value (will always be positive) of the key +if the key exists in the cache, otherwise return -1. + +put(key, value) - Set or insert the value if the key is not already present. +When the cache reaches its capacity, it should invalidate the least frequently used item +before inserting a new item. For the purpose of this problem, +when there is a tie (i.e., two or more keys that have the same frequency), +the least recently used key would be evicted. + +Follow up: +Could you do both operations in O(1) time complexity? + +Example: + +LFUCache cache = new LFUCache( 2 ); // capacity + +cache.put(1, 1); +cache.put(2, 2); +cache.get(1); // returns 1 +cache.put(3, 3); // evicts key 2 +cache.get(2); // returns -1 (not found) +cache.get(3); // returns 3. +cache.put(4, 4); // evicts key 1. +cache.get(1); // returns -1 (not found) +cache.get(3); // returns 3 +cache.get(4); // returns 4 + +*/ + + +/* +Goal: +get(key): value(+ int), -1 +put(key, val). +if at cap, remove least frequent (smallest count); if count equal, remove old key +option1. pq, to store sort the order (frequency, recency). O(logn) for insertion. NOT OKAY +option2: +- hashmap to find value by key quickly, +- frequency map to track +- freqPosition map to track +- int minFrequency to track the lowest frequency (to remove when reached capacity) +- constant capacity + +Option2 gives O(1) get(); put() avg O(1) + +Get: +Find key from map, retrieve existing frequency and find it on freqMap, move the item to a new frequency++ on freqMap, update freqPosition + +Put: +If exist, simply update the value for the key, return. +If not exist: +- (optional)if over capacity: find freqMap[minFreq]'s least frequent item, remove item.key from map, remove item.key freqPosition map, remove the item from freqMap (first item in the list) +- it's new item with freq==1, add to hashmap, add to frequency map, and add the freq list position to freqPosition. + +*/ + +class LFUCache { + class Node { + int key, value, count; + Node next, prev; + public Node(int key, int value, int count) { + this.key = key; + this.value = value; + this.count = count; + } + } + int minFreq, capacity; + Map map; + // INCORRECT, Use doubly-linked list to replace below structures + Map> freqMap; // list: [head:oldest] + Map freqPosMap; //TODO: + + public LFUCache(int capacity) { + this.capacity = capacity; + this.minFreq = 0; + this.map = new HashMap<>(); + this.freqMap = new HashMap<>(); + this.freqPosMap = new HashMap<>(); + } + + public int get(int key) { + System.out.println("start get key:" + key); + if (!map.containsKey(key)) { + return -1; + } + Node node = map.get(key); + + // update freqMap + freqMap.get(node.count).remove((int)(freqPosMap.get(key))); + node.count += 1; + if (!freqMap.containsKey(node.count)) { + freqMap.put(node.count, new ArrayList<>()); + } + freqMap.get(node.count).add(node); + + // update freqPosMap: + freqPosMap.put(key, freqMap.get(node.count).size() - 1); + + // update minFreq + if (freqMap.get(minFreq).size() == 0) minFreq++; + //minFreq = Math.min(minFreq, node.count); + + System.out.println("after get key:" + key); + printmap(); + + return node.value; + } + + public void put(int key, int value) { + System.out.println("start put key:" + key + " value:" + value); + if (capacity <= 0) { + return; + } + if (map.containsKey(key)) { + map.get(key).value = value; + return; + } + // clean up the queue + if (map.size() >= capacity) { + Node node = freqMap.get(minFreq).get(0); + map.remove(node.key); + freqMap.get(minFreq).remove(0); + freqPosMap.remove(node.key); + } + + Node newNode = new Node(key, value, 1); + map.put(key, newNode); + if (!freqMap.containsKey(newNode.count)) { + freqMap.put(newNode.count, new ArrayList<>()); + } + freqMap.get(newNode.count).add(newNode); + freqPosMap.put(key, freqMap.get(newNode.count).size() - 1); + minFreq = 1; + System.out.println("after put key:" + key + " value:" + value); + printmap(); + } + + private void printmap() { + System.out.println("map"); + for (Node node : map.values()) { + System.out.println("key: " + node.key + " value:" + node.value); + } + System.out.println("freqMap"); + for (Map.Entry> entry: freqMap.entrySet()) { + System.out.println(entry.getKey() + " list size:" + entry.getValue().size()); + } + System.out.println("freqPosmap"); + for (Map.Entry entry: freqPosMap.entrySet()) { + System.out.println("key:" + entry.getKey() + " pos: " + entry.getValue()); + } + System.out.println("min freq list"); + for (Node node : freqMap.get(minFreq)){ + System.out.print(node.key + "->"); + } + System.out.println(); + System.out.println(); + } +} + +/** + * Your LFUCache object will be instantiated and called as such: + * LFUCache obj = new LFUCache(capacity); + * int param_1 = obj.get(key); + * obj.put(key,value); + */ + +``` \ No newline at end of file diff --git a/Java/LRU Cache.java b/Java/LRU Cache.java new file mode 100755 index 0000000..f9fea7d --- /dev/null +++ b/Java/LRU Cache.java @@ -0,0 +1,248 @@ +H +1526021243 +tags: Design, Linked List, Hash Table + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度。 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) + +##### 巧妙点 +- 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +- 2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +- 直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法: +- moveToHead() +- insertHead() +- remove() + +#### O(n) 检查重复 +- timeout method, 天真的来了一个O(n) 的解法,结果果然timeout. +- 一个map存数值。一个queue来存排位。 +- 每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + + +``` +/* +Design and implement a data structure for Least Recently Used (LRU) cache. +It should support the following operations: get and put. + +get(key) - Get the value (will always be positive) of the key +if the key exists in the cache, otherwise return -1. + +put(key, value) - Set or insert the value if the key is not already present. +When the cache reached its capacity, it should invalidate the least recently used item +before inserting a new item. + +Follow up: +Could you do both operations in O(1) time complexity? + +Example: + +LRUCache cache = new LRUCache( 2 ); //capacity + +cache.put(1, 1); +cache.put(2, 2); +cache.get(1); // returns 1 +cache.put(3, 3); // evicts key 2 +cache.get(2); // returns -1 (not found) +cache.put(4, 4); // evicts key 1 +cache.get(1); // returns -1 (not found) +cache.get(3); // returns 3 +cache.get(4); // returns 4 + +*/ +/* +It looks like we need to do some design, according to (http://www.cnblogs.com/yuzhangcmu/p/4113462.html). Though, the solution's concept is quite similar as attempt1. + +1. The design uses HashMap, and 2-way LinkedList. Map +2. Use two dummy node: head and tail. When add/remove nodes, we are add/remove nodes in between head and tail. + So. head.next is our real 1st element + andd tail.pre is our real last element + +Note: +Be careful: when removing a node due to capacity issue, remember to remove both 1st node(head.next) and the corresponding entry in the map: map.remove(head.next.key) +*/ + +//Use double linked list to store value. +//Store key in hashmap to find node easily +//Functions: insert in front, remove node, +public class LRUCache { + class DoubleLinkedListNode { + int key, val; + DoubleLinkedListNode next,prev; + public DoubleLinkedListNode(int key, int val){ + this.key = key; + this.val = val; + next = null; + prev = null; + } + } + public int capacity; + public HashMap map; + public DoubleLinkedListNode head, tail; + public LRUCache(int capacity) { + this.capacity = capacity; + this.map = new HashMap<>(); + this.head = new DoubleLinkedListNode(-1, -1); + this.tail = new DoubleLinkedListNode(-1, -1); + head.next = tail; + head.prev = tail; + tail.next = head; + tail.prev = head; + } + + public int get(int key) { + if(!map.containsKey(key)) { + return -1; + } + DoubleLinkedListNode node = map.get(key); + moveToHead(node); + return node.val; + } + + public void put(int key, int value) { + if (map.containsKey(key)) { + map.get(key).val = value; + moveToHead(map.get(key)); + } else { + DoubleLinkedListNode node = new DoubleLinkedListNode(key, value); + if (map.size() >= this.capacity) { + DoubleLinkedListNode rm = tail.prev; + remove(rm); + map.remove(rm.key); + } + insertHead(node); + map.put(key, node); + } + } + + public void moveToHead(DoubleLinkedListNode node) { + remove(node); + insertHead(node); + } + + //Helper functions + /* + Put node to front, where the latest item is at. + */ + public void insertHead(DoubleLinkedListNode node) { + DoubleLinkedListNode next = head.next; + head.next = node; + node.prev = head; + node.next = next; + next.prev = node; + } + + /* + Find front and end, link them. + */ + public void remove(DoubleLinkedListNode node) { + DoubleLinkedListNode front = node.prev; + DoubleLinkedListNode end = node.next; + front.next = end; + end.prev = front; + } +} + + +/* +First Attempt: time exceeds + +Thoughts: +Easy to implement: cache the least-added item. However, we are looking for the cache of 'leaset-recently-used item'. + +'used' means the get() method: +whenever get, remove that key from the queue, and move that key to top. + +Time Cost: O(n) on get(), set() + + +*/ + +public class LRUCache { + private int cap; + private HashMap map; + private LinkedList queue; + public LRUCache(int capacity) { + this.cap = capacity; + this.map = new HashMap(); + this.queue = new LinkedList(); + } + + public int get(int key) { + if (map.containsKey(key)) { + moveUsedToTop(key); + return map.get(key); + } else { + return -1; + } + } + + public void set(int key, int value) { + if (map.containsKey(key)) { + moveUsedToTop(key); + map.put(key, value); + } else { + if (queue.size() >= cap) { + map.remove(queue.poll()); + } + queue.offer(key); + map.put(key, value); + } + + } + //O(n) + public void moveUsedToTop(int key) { + for (int i = 0; i < queue.size(); i++) { + if (queue.get(i) == key) { + queue.remove(i); + queue.offer(key); + break; + } + } + } +} + +//O(n) timeout +//Get: find target in arraylist, remove it, insert in front, return map.get(target) +//Set: if exist, find in arraylist, remove it, insert in front. +// if not exist: check capacity: if full, remove last element and remove it from map. Then, insert in front, and insert into map. +public class LRUCache { + public ArrayList list = new ArrayList(); + public HashMap map = new HashMap(); + public int capacity; + public LRUCache(int capacity) { + this.capacity = capacity; + } + + public int get(int key) { + if (map.containsKey(key)) { + int ind = list.indexOf(key); + list.remove(ind); + list.add(0, key); + return map.get(key); + } + return -1; + } + + public void set(int key, int value) { + if (map.containsKey(key)) { + int ind = list.indexOf(key); + list.remove(ind); + list.add(0, key); + map.put(key, value); + } else { + list.add(0, key); + map.put(key, value); + if (list.size() > capacity) { + int rm = list.get(list.size() - 1); + list.remove(list.size() - 1); + map.remove(rm); + } + } + + } +} + +``` \ No newline at end of file diff --git a/Java/Largest Number At Least Twice of Others.java b/Java/Largest Number At Least Twice of Others.java new file mode 100644 index 0000000..b6b43c5 --- /dev/null +++ b/Java/Largest Number At Least Twice of Others.java @@ -0,0 +1,66 @@ +E +1517560733 +tags: Array + +找最大值, 和第二大的值, 看是否符合题意, 就行了. +分析题意, 最简单方法, 可以loop 两遍: 找最值; 作比较. +但其实, 举个反例: 有一个不满足, 就够反对这个'at least twice of alllll others'. + +``` +/* +In a given integer array nums, there is always exactly one largest element. + +Find whether the largest element in the array is at least twice as much as every other number in the array. + +If it is, return the index of the largest element, otherwise return -1. + +Example 1: + +Input: nums = [3, 6, 1, 0] +Output: 1 +Explanation: 6 is the largest integer, and for every other number in the array x, +6 is more than twice as big as x. The index of value 6 is 1, so we return 1. + + +Example 2: + +Input: nums = [1, 2, 3, 4] +Output: -1 +Explanation: 4 isn't at least as big as twice the value of 3, so we return -1. + + +Note: + +nums will have a length in the range [1, 50]. +Every nums[i] will be an integer in the range [0, 99]. +*/ + +/* +Thoughts: +1. find max +2. validate if max>2x of the rest? + +Only need to pick one exception to break it: so find the max, and leastMax. +Do it in O(n) one time. +*/ +class Solution { + public int dominantIndex(int[] nums) { + if (nums == null || nums.length <= 1) { + return 0; + } + int max = -1; + int leastMax = -1; + int index = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] >= max) { + leastMax = max; + max = nums[i]; + index = i; + } else { + leastMax = Math.max(leastMax, nums[i]); + } + } + return max >= leastMax * 2 ? index : -1; + } +} +``` \ No newline at end of file diff --git a/Java/Largest Number.java b/Java/Largest Number.java old mode 100644 new mode 100755 index c88247c..d284541 --- a/Java/Largest Number.java +++ b/Java/Largest Number.java @@ -1,3 +1,21 @@ +M +1525233126 +tags: Sort + +给一串数字, 非负数, 把所有数字串联起来, 组成最大数字. + +因为结果很大, 所以用string表示 + +#### Sort, Comparator +- 考虑 more significant spot 应该拿到更大的值 +- 如果sort number, comparator 会比较难写: 每个digit的weight不同, 要分别讨论个位数和多位数. +- goal: 让较大的组合数排在前面, 让较小的组合数排在后面 +- 不如: 组合两种情况, 用String比较一下大小 (也可以用 integer来比较组合数, 但是为保险不超Integer.MAX_VALUE, 这里比较String) +- String.compareTo() 是按照 lexicographically, 字典顺序排列的 +- 利用compareTo, 来倒序排列 string, 刚好就得到我们要的结果. +- O(nlogn), 排序 + +``` /* Given a list of non negative integers, arrange them such that they form the largest number. @@ -15,11 +33,14 @@ */ + +/* class CustomComparator implements Comparator { public int compare(String s1, String s2) { return (s2 + s1).compareTo(s1 + s2); } } +*/ public class Solution { /** *@param num: A list of non negative integers @@ -33,32 +54,25 @@ public String largestNumber(int[] num) { for (int i = 0; i < num.length; i++) { strs[i] = num[i] + ""; } - Arrays.sort(strs, new CustomComparator()); - StringBuffer sb= new StringBuffer(); + + // The lexicographically order is "1" < "2"; here we reverse the compareTo() to get reverse effect + Arrays.sort(strs, new Comparator() { + public int compare(String s1, String s2) { + return (s2 + s1).compareTo(s1 + s2); + } + }); + + StringBuffer sb = new StringBuffer(); for (int i = 0; i < num.length; i++) { sb.append(strs[i]); } String rst = sb.toString(); + + // The case "000000" if (rst.charAt(0) == '0') { return "0"; } return rst; } } - - - - - - - - - - - - - - - - - +``` \ No newline at end of file diff --git a/Java/Largest Rectangle in Histogram.java b/Java/Largest Rectangle in Histogram.java old mode 100644 new mode 100755 index 4f2f6a8..c6643c0 --- a/Java/Largest Rectangle in Histogram.java +++ b/Java/Largest Rectangle in Histogram.java @@ -1,37 +1,106 @@ +H +1520813180 +tags: Array, Stack, Monotonous Stack + +给n个bar,组成柱状图histogram. 求在这一排柱状图里面可以找到的面积最大的长方形. + +思考: 找长方形面积, 无非是找两个index, 然后底边长度 * height. + +#### Monotonous Stack +- 重点是根据找Histogram里面rectangle的性质, 维持一个单调递增的Stack +- 在loop over indexes的时候: +- 如果高度>= previous peek(), 那么对于那个peek, 就意味着, 往下走, 一直走高嘛, 之前的peek总可以继续抄底 +- 什么时候不能抄底了呢? 就是有一个下降趋势的时候 +- 这时候并不是calculate所有前面的peek, 而是考虑 大于 current height的之前所有的peek. +- 把这些peek到 current height 前一格的rectangle全部找出来: stack.pop() +- 这个stack.pop()的过程里面, 其实没有算上 current height, 因为需要留到下一轮, 把current index加进stack 再说 +- 为什么用stack? 因为需要知道连续递增的peek, stack.peek() O(1), 好用 + 而其实不用stack, 也可以用其他方式记录所有height, 只不过要 O(n)去找peek不方便 + +#### 知识点 +- 理解monotonous stack 是如何被维护的 +- 维护monotonous stack 是题目需要, 而不是stack本身性质, 是一种借助 stack.peek() O(1)的巧妙用法. + + +``` /* -Example -Given height = [2,1,5,6,2,3], -return 10. +LeetCode: +https://leetcode.com/problems/largest-rectangle-in-histogram/description/ -Tags Expand -Array Stack +Given n non-negative integers representing the histogram's bar height, +where the width of each bar is 1, find the area of largest rectangle in the histogram. -Thinking Process: -///TODO: missing thinking process for Largest Rectangle in Histogram +[missing image] +Above is a histogram where width of each bar is 1, given height = [2,1,5,6,2,3]. -*/ +[missing image] +The largest rectangle is shown in the shaded area, which has area = 10 unit. + +For example, +Given heights = [2,1,5,6,2,3], +return 10. + */ -public class Solution { - /** - * @param height: A list of integer - * @return: The area of largest rectangle in the histogram - */ - public int largestRectangleArea(int[] height) { - if (height == null || height.length == 0) { +/* +Thoughts: +Maintain monotonous stack: whenever seeing a decreasing element, process. +Some characteristics when calculating rectangle in histogram +- You turn to think: I have to loop over all indexes then I know for any specific height, for example, 1, occurs across [0 ~ n]. + That's partially true +- We should make be best efforts on calculating: up to certain index, what's maximum we could get. + As we maintain the monotonous ascending stack, stack.peek() element is always the starting point of the rectangle +- [important] Only need to stop and calculate rectangle when seeing a descending element +- It's like, keep climbing the mountain; when it descreases at a point, + trace back to use all previous peeks and form rectangle with current position +*/ +class Solution { + public int largestRectangleArea(int[] heights) { + if (heights == null || heights.length == 0) { return 0; - } - Stack stack = new Stack(); + } + int n = heights.length; int max = 0; - for (int i = 0; i <= height.length; i++) { - int current = (i == height.length) ? -1 : height[i]; - while (!stack.empty() && current <= height[stack.peek()]) { - int h = height[stack.pop()]; - int w = stack.empty() ? i : i - stack.peek() - 1; - max = Math.max(max, w * h); + Stack stack = new Stack<>(); // Use stack to store the index + for (int i = 0; i <= n; i++) { + int currHeight = i == n ? -1 : heights[i]; + // Keep stack monotonous; if not, process && calculate rectangle + while (!stack.isEmpty() && currHeight <= heights[stack.peek()]) { + int currPeekHeight = heights[stack.pop()]; + // exclude current position; it'll be calculate in next round. + int width = stack.isEmpty() ? i : i - stack.peek() - 1; + max = Math.max(max, currPeekHeight * width); } stack.push(i); } + return max; } } + +/** + * @param {number[]} heights + * @return {number} + */ + // javascript version +var largestRectangleArea = function(heights) { + if(heights == null || heights.length == 0) { + return 0; + } + let n = heights.length; + let max = 0; + let stack = []; // use it as stack, with index 0 being the top + for (let i = 0; i <= n; i++) { + let currHeight = i == n ? -1 : heights[i]; + while (stack.length > 0 && currHeight <= heights[stack[0]]) { + let currPeekHeight = heights[stack[0]]; + stack.splice(0, 1); + let width = stack.length == 0 ? i : i - stack[0] - 1; + max = Math.max(max, currPeekHeight * width); + } + stack.unshift(i); + } + + return max; +}; +``` \ No newline at end of file diff --git a/Java/Last Position of Target.java b/Java/Last Position of Target.java old mode 100644 new mode 100755 index 236c6f0..97464fc --- a/Java/Last Position of Target.java +++ b/Java/Last Position of Target.java @@ -1,7 +1,16 @@ -``` +E +1525238120 +tags: Binary Search + +给一个sorted integer array, 找target出现的最后的index. array 里有重复数字 + 有重复,不是末尾点,继续binary search + +``` + /* -Find the last position of a target number in a sorted array. Return -1 if target does not exist. +Find the last position of a target number in a sorted array. +Return -1 if target does not exist. Example Given [1, 2, 2, 4, 5, 5]. diff --git a/Java/Length of Last Word.java b/Java/Length of Last Word.java old mode 100644 new mode 100755 index 4f478bb..929c419 --- a/Java/Length of Last Word.java +++ b/Java/Length of Last Word.java @@ -1,5 +1,19 @@ +E +1525238501 +tags: String + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + +``` /* -Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return the length of last word in the string. +Given a string s consists of upper/lower-case alphabets +and empty space characters ' ', + +return the length of last word in the string. If the last word does not exist, return 0. @@ -12,14 +26,37 @@ Tags Expand String +*/ + +/* +Thoughts: +Traverse from end, find space, return length +*/ +class Solution { + public int lengthOfLastWord(String s) { + if (s == null || s.length() == 0) { + return 0; + } + s = s.trim(); + for (int i = s.length() - 1; i >= 0; i--) { + if (s.charAt(i) == ' ') { + return s.length() - i - 1; + } + } + + return s.length(); + } +} + +/** + Thoughts: 1. Split by space 2. return last word's length Note: Java split: have to add '\\' in order to pass the key word. -*/ - +*/ public class Solution { /** @@ -36,3 +73,5 @@ public int lengthOfLastWord(String s) { return lastWord.length(); } } + +``` \ No newline at end of file diff --git a/Java/Letter Combinations of a Phone Number.java b/Java/Letter Combinations of a Phone Number.java old mode 100644 new mode 100755 index bbda83e..740e1b1 --- a/Java/Letter Combinations of a Phone Number.java +++ b/Java/Letter Combinations of a Phone Number.java @@ -1,3 +1,12 @@ +M +1519232186 +tags: String, Backtracking + +方法1: Iterative with BFS using queue. + +方法2: Recursively adding chars per digit + +``` /* Given a digit string, return all possible letter combinations that the number could represent. @@ -18,18 +27,118 @@ A mapping of digit to letters (just like on the telephone buttons) is given belo */ /* - Thoughts: have done this on Leetcode. - map integer to letters - combination of existing letters (by pressing fist number) with next number's letters. - put combinations into queue, reuse the queue. - finally, output into arraylist - - NON-recursive/iterative: use a queue. (Done this one Leetcode) - - This time, use recursive: - pass along rst, list, level number, digits, - for (combine list with all next level's candidates, map) - when level number == digits.length(), return the candidate into rst. +Thoughts: +1. Have to use all letters +2. DFS: can skip a letter but need to use it later for other combinations +3. When string length matches target length, add string. +4. When input digits are used up, return final result. +*/ +class Solution { + final Map> map = new HashMap<>(); + + public List letterCombinations(String digits) { + final List rst = new ArrayList(); + if (digits == null || digits.length() == 0) { + return rst; + } + + //Prepare map + map.put('2', Arrays.asList("a","b","c")); + map.put('3', Arrays.asList("d","e","f")); + map.put('4', Arrays.asList("g","h","i")); + map.put('5', Arrays.asList("j","k","l")); + map.put('6', Arrays.asList("m","n","o")); + map.put('7', Arrays.asList("p","q","r","s")); + map.put('8', Arrays.asList("t","u","v")); + map.put('9', Arrays.asList("w","x","y","z")); + + List list = new ArrayList<>(); + dfs(rst, list, digits.toCharArray(), 0); + + return rst; + } + + public void dfs(List rst, List list, char[] digit, int level) { + if (list.size() == digit.length) { + StringBuffer sb = new StringBuffer(); + for (String str : list) { + sb.append(str); + } + rst.add(sb.toString()); + return; + } + + List letters = map.get(digit[level]); + for (String letter : letters) { + list.add(letter); + dfs(rst, list, digit, level + 1); + list.remove(list.size() - 1); + } + } +} + + +/*3.7.2016 recap. Iterative way using BFS*/ +//Hashmap the list of chars. +//Use queue to append all possibile candidates. BFS +public class Solution { + public List letterCombinations(String digits) { + List rst = new ArrayList(); + if (digits == null || digits.length() == 0) { + return rst; + } + //Init map + HashMap map = new HashMap(); + map.put('2',"abc"); + map.put('3',"def"); + map.put('4',"ghi"); + map.put('5',"jkl"); + map.put('6',"mno"); + map.put('7',"pqrs"); + map.put('8',"tuv"); + map.put('9',"wxyz"); + // Init 1 digits and the chars in queue + Queue queue = new LinkedList(); + char c = digits.charAt(0); + String s = map.get(c); + for (int i = 0; i < s.length(); i++) { + queue.offer(s.charAt(i) + ""); + } + + int size = 0; + for (int i = 1; i < digits.length(); i++) {//iteratve all numbers + c = digits.charAt(i); + s = map.get(c); + size = queue.size(); + for (int j = 0; j < size; j++) {//iteratve old queue + String str = queue.poll(); + for (int k = 0; k < s.length(); k++) {//iteratve possibile chars per number key + queue.offer(str + s.charAt(k)); + } + } + } + while (!queue.isEmpty()) { + rst.add(queue.poll()); + } + + return rst; + } +} + + +/* + Thoughts: have done this on Leetcode. + map integer to letters + combination of existing letters (by pressing fist number) with next number's letters. + put combinations into queue, reuse the queue. + finally, output into arraylist + + NON-recursive/iterative: use a queue. (Done this one Leetcode) + + This time, use recursive: + pass along rst, list, level number, digits, + for (combine list with all next level's candidates, map) + when level number == digits.length(), return the candidate into rst. */ public class Solution { /** @@ -60,25 +169,25 @@ public ArrayList letterCombinations(String digits) { } public void helper(ArrayList rst, ArrayList list, - ArrayList map, String digits, int level){ - //If level is finished, compress into string - if (level == digits.length()) { - StringBuffer sb = new StringBuffer(); - for (String s : list) { - sb.append(s); - } - rst.add(sb.toString()); - return; - } - //For a specific list of candidates, face the level of chars - int num = Integer.parseInt(digits.substring(level, level + 1)); - String[] strs = map.get(num); - - for (int i = 0; i < strs.length; i++) { - list.add(strs[i]); - helper(rst, list, map, digits, level + 1); - list.remove(list.size() - 1); - } + ArrayList map, String digits, int level){ + //If level is finished, compress into string + if (level == digits.length()) { + StringBuffer sb = new StringBuffer(); + for (String s : list) { + sb.append(s); + } + rst.add(sb.toString()); + return; + } + //For a specific list of candidates, face the level of chars + int num = Integer.parseInt(digits.substring(level, level + 1)); + String[] strs = map.get(num); + + for (int i = 0; i < strs.length; i++) { + list.add(strs[i]); + helper(rst, list, map, digits, level + 1); + list.remove(list.size() - 1); + } } } @@ -148,3 +257,5 @@ public List letterCombinations(String digits) { + +``` \ No newline at end of file diff --git a/Java/Line Reflection.java b/Java/Line Reflection.java new file mode 100644 index 0000000..054741d --- /dev/null +++ b/Java/Line Reflection.java @@ -0,0 +1,69 @@ +M +1531928255 +tags: Hash Table, Math +time: O(n) +space: O(n) + +给一串点, 找是否有一个所有点中间的, 跟y-axis平行的中线. + +#### Hash Table +- 1. store in `Map>`, 2. iterate over map, check head,tail against the mid point +- 很好的细节题目: +- 1. 除以2, 需要存double +- 2. (问面试官)可以有重复的点! 所以track `set` +- 3. 处理 left==right时候, 就当做两个点来处理. +- 4. 存进set里面没有sort, 但是最后做check的时候, 需要sort list +- 时间: visit all nodes 两遍, O(n) + +``` +/* +Given n points on a 2D plane, find if there is such a line parallel to y-axis that reflect the given points. + +Example 1: +Given points = [[1,1],[-1,1]], return true. + +Example 2: +Given points = [[1,1],[-1,-1]], return false. + +Follow up: +Could you do better than O(n2)? + +Credits: +Special thanks to @memoryless for adding this problem and creating all test cases. +*/ + +/* +line parrel to y-axis: for all points with same y, all head/tail should have same mid point +1. store in Map> +2. iterate over map, check head,tail mid +*/ +class Solution { + public boolean isReflected(int[][] points) { + if (points == null || points.length == 0 || points[0] == null || points[0].length == 0) return true; + + Map> map = new HashMap<>(); + for (int[] point : points) { + map.putIfAbsent(point[1], new HashSet<>()); + map.get(point[1]).add((double)point[0]); + } + + List sampleRow = new ArrayList<>(map.get(points[0][1])); + Collections.sort(sampleRow); + double mid = (sampleRow.get(0) + sampleRow.get(sampleRow.size() - 1)) / 2; + + for (Set row: map.values()) { + int left = 0, right = row.size() - 1; + List sortedRow = new ArrayList<>(row); + Collections.sort(sortedRow); + while (left <= right) { + if ((sortedRow.get(left) + sortedRow.get(right)) / 2 != mid) { + return false; + } + left++; + right--; + } + } + return true; + } +} +``` \ No newline at end of file diff --git a/Java/Linked List Cycle II.java b/Java/Linked List Cycle II.java old mode 100644 new mode 100755 index a4501f5..79040c7 --- a/Java/Linked List Cycle II.java +++ b/Java/Linked List Cycle II.java @@ -1,13 +1,30 @@ -HashMap很简单就做了。 +M +1520231597 +tags: Linked List, Two Pointers, Math +LinkedList 里面有 cycle, 找到cycle的起始点(第一个重复出现的element). -O(1)要首先break while loop when there is a slow==fast -然后,然后就有个我不懂得地方: +#### Slow, fast Pointer +- 快慢指针, O(1)space. +- 1. 确认有cycle后 2. 数学问题:找到开头. +- 当head == slow.next时候, head就是cycle starting point. +- 也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +#### 证明 +- 1. 假设慢指针走t步, 快指针走快一倍, 也就是2t. +- 2. 我们假设cycle的长度是Y, 而进入cycle之前的长度为X. +- 3. 假设慢指针走了m圈cycle, 而快指针走了n圈cycle之后, 两个pointer相遇. +- 4. 最终在Y cycle里面的K点相遇, 也就是两个指针都在这最后一圈里面走了K 步. +- 那么: +- t = X + mY + K +- 2t = X + nY + K +- 整合公式: X + K = (n - 2m)Y +- 这里的m和n不过是整数的跑圈数, 也就是说X和K加在一起, 总归是结束cycle. X 和 K 互补 +- 结论: 当slow/fast 指针在K点相遇后, 再走X步, 就到了cycle的起点, 也就是题目要求的起点. + +#### Hash Table, O(n) space -当head == slow.next时候, head就是cycle starting point. -也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... -这个可能要写一写,装一装,证明证明才行...不是特别清楚。 ``` /* Given a linked list, return the node where the cycle begins. @@ -26,6 +43,44 @@ Two Pointers Linked List */ +/** + * Definition for singly-linked list. + * class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +/* +http://fisherlei.blogspot.com/2013/11/leetcode-linked-list-cycle-ii-solution.html +Thougths: +Once the slow/fast pointer meets, it becomes a math problem. +O(n) +*/ +public class Solution { + public ListNode detectCycle(ListNode head) { + if (head == null || head.next == null) { + return null; + } + ListNode slow = head; + ListNode fast = head.next; + while (fast.next != null && fast.next.next != null) { + if (slow == fast) break; + slow = slow.next; + fast = fast.next.next; + } + if (slow != fast) return null; + + while (head != slow.next) { + head = head.next; + slow = slow.next; + } + return head; + } +} /** * Definition for ListNode. diff --git a/Java/Linked List Cycle.java b/Java/Linked List Cycle.java old mode 100644 new mode 100755 index 5a87d8d..ced8c63 --- a/Java/Linked List Cycle.java +++ b/Java/Linked List Cycle.java @@ -1,7 +1,14 @@ -O(1) sapce: 用快慢指针。一个跑.next, 一个跑.next.next。 总有一次,fast会因为cycle而追上slow。 -那个时候其实slow.val = fast.val. +E +1520009168 +tags: Linked List, Two Pointers + +#### Two Pointer: Slow Fast Pointer +- O(1) sapce: 用快慢指针。一个跑.next, 一个跑.next.next。 总有一次,fast会因为cycle而追上slow。 +- 那个时候其实slow.val = fast.val. + +#### Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle -O(n):用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle le ``` /* 50% Accepted @@ -31,6 +38,28 @@ * } * } */ +/* +Thoughts: +Fast/Slow pointer: fast pointer will eventually catch up with slow pointer +*/ +public class Solution { + public boolean hasCycle(ListNode head) { + if (head == null || head.next == null) { + return false; + } + + ListNode slow = head; + ListNode fast = head.next; + while (fast.next != null && fast.next.next != null) { + if (slow == fast) { + return true; + } + slow = slow.next; + fast = fast.next.next; + } + return slow == fast; + } +} /* REDO 12.10.2015 @@ -61,36 +90,4 @@ public boolean hasCycle(ListNode head) { return true; } } - - -/* -Thinking process: -If there is a cycle, then slow node and fast node will meet at some point. - - -However, we cannot simply compare slow.val == fast.val. -This is because, there can be different link node with same value, but they are stored on different index, -so they should not be cycle. - -The below passes the test case in lintcode, which is wrong. - -public class Solution { - public boolean hasCycle(ListNode head) { - if (head == null) { - return false; - } - ListNode slow = head; - ListNode fast = head.next; - while (fast != null && fast.next != null) { - if (slow.val == fast.val) { - return true; - } - slow = slow.next; - fast = fast.next.next; - } - return false; - } -} -*/ - ``` \ No newline at end of file diff --git a/Java/Longest Common Prefix.java b/Java/Longest Common Prefix.java old mode 100644 new mode 100755 index c029d70..4cc9edb --- a/Java/Longest Common Prefix.java +++ b/Java/Longest Common Prefix.java @@ -1,3 +1,20 @@ +E +1528005440 +tags: String + +找一串String里面最长的公共prefix. + +#### Sort, compare string +- Sort O(nlogn) +- first and last string should share common prefix +- 这里假设题目要求的是所有string的公共 prefix, 而不是部分strings + +#### Brutle +- Nested loop, 每一次比较所有string 同位是否相等 +- 相等,append string. 不等,return. +- O(mn) + +``` /* Given k strings, find the longest common prefix (LCP). @@ -9,33 +26,57 @@ Given k strings, find the longest common prefix (LCP). Tags Expand Enumeration Basic Implementation String LintCode Copyright +*/ + +/* +Assumption: requirement is common prefix for all strings. +Sort the strings: then head and tail should extract the common prefix +*/ +class Solution { + public String longestCommonPrefix(String[] strs) { + if (strs == null || strs.length == 0) { + return ""; + } + + Arrays.sort(strs); + String head = strs[0]; + String tail = strs[strs.length - 1]; + + int size = head.length() < tail.length() ? head.length() : tail.length(); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < size; i++) { + if (head.charAt(i) != tail.charAt(i)) { + break; + } + sb.append(head.charAt(i)); + } + return sb.toString(); + } +} + +/* + Thoughts: 1. Continuous while loop until something breaks out. 2. Str to record the longest prefix 3. for loop on each while-loop, based on strs[0] - Check if index exist - check if all other strs have same char at this point. If so, add it. If not, break. + Check if index exist + check if all other strs have same char at this point. If so, add it. If not, break. Note: Arrays: use strs.length -String: use str.length(). -Odd, right? I know ... dislike. -String seems to be supirior, it's a god damn object, and we have a method for checking string length. -For array, well, looks like it's been mistreated ... we are only reading a length property of the array object. +String: use str.length(). Note2: Ask for border case: when only 1 string, longest prefix turns out it's the strs[0] itself. + */ public class Solution { - /** - * @param strs: A list of strings - * @return: The longest common prefix - */ public String longestCommonPrefix(String[] strs) { if (strs == null || strs.length == 0) { - return ""; + return ""; } if (strs.length == 1) { return strs[0]; @@ -43,23 +84,26 @@ public String longestCommonPrefix(String[] strs) { String prefix = ""; int ind = 0; while (ind < strs[0].length()) { - char c = strs[0].charAt(ind); - boolean valid = false; - for (int i = 1; i < strs.length; i++) { - if (strs[i].length() > ind && strs[i].charAt(ind) == c) { - valid = true; - } else { - valid = false; - break; - } - } - if (valid) { - prefix += "" + c; - } else { - break; - } - ind++; + char c = strs[0].charAt(ind); + boolean valid = false; + for (int i = 1; i < strs.length; i++) { + if (strs[i].length() > ind && strs[i].charAt(ind) == c) { + valid = true; + } else { + valid = false; + break; + } + } + if (valid) { + prefix += "" + c; + } else { + break; + } + ind++; }//END WHILE return prefix; } -} \ No newline at end of file +} + + +``` \ No newline at end of file diff --git a/Java/Longest Common Subsequence.java b/Java/Longest Common Subsequence.java old mode 100644 new mode 100755 index f423e2f..7502cc6 --- a/Java/Longest Common Subsequence.java +++ b/Java/Longest Common Subsequence.java @@ -1,3 +1,16 @@ +M +1519197096 +tags: DP, Double Sequence DP, Sequence DP + +给两个string, A, B. 找这两个string里面的LCS: 最长公共字符长度 (不需要是continuous substring) + +#### Double Sequence DP +- 设定dp长度为(n+1), 因为dp[i]要用来表示前i个(ith)时候的状态, 所以长度需要时i+1才可以在i位置, hold住i. +- 双序列: 两个sequence之间的关系, 都是从末尾字符看起, 分析2种情况: +- 1. A最后字符不在common sequence 或者 B最后字符不在common sequence. +- 2. A/B最后字符都在common sequence. 总体count + 1. + +``` /* Given two strings, find the longest comment subsequence (LCS). @@ -18,6 +31,151 @@ Given two strings, find the longest comment subsequence (LCS). Tags Expand LintCode Copyright Longest Common Subsequence Dynamic Programming +*/ + +/* +Thoughts: +Sequence DP. +Common Subsequence: doesn't have to be conneccted subsequence. +Consider the last position of each string, there are 3 possible conditions: +1. A's last index is not part of common subsequence. Next step, consider: A[0 ~ n-1] and B +2. B's last index is not part of common subsequence Next step, consider: B[0 ~ n-1] and A +3. A's last index == B's last index, +1 on the result. Next step, consider: A[0 ~ n-1] and B[0 ~ n-1] + +=> Each condition results in a sub problem. + +dp[i][j]: longest common subsequence length for items: A[0 ~ i - 1] and B[0 ~ j - 1] +dp[i][j] = Max{dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1] + 1| A[i - 1]==B[i - 1] } + +Space: O(MN) +Time: O(MN) +*/ +/* +Thoughts: +dp[i][j] represent max LCS length for A[0, i - 1], B[0, j - 1] +Conditions: +- A[i-1] != B[j - 1]: no action +- A[i-1] == B[j - 1]: dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1); +*/ +public class Solution { + public int longestCommonSubsequence(String A, String B) { + if (A == null || B == null || A.length() == 0 || B.length() == 0) { + return 0; + } + int m = A.length(); + int n = B.length(); + int[][] dp = new int[m + 1][n + 1]; + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + // Init + if (i == 0 || j == 0) { + dp[i][j] = 0; + continue; + } + // Base condition: equals to previous's best + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + if (A.charAt(i - 1) == B.charAt(j - 1)) { + // match, take previous' best + 1 + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1); + } + } + } + return dp[m][n]; + } +} + +// Optimization: Rolling array +// Space: O(N), Time: O(MN) +public class Solution { + public int longestCommonSubsequence(String A, String B) { + if (A == null || B == null || A.length() == 0 || B.length() == 0) { + return 0; + } + int m = A.length(); + int n = B.length(); + int[][] dp = new int[2][n + 1]; + + int curr = 1; + int prev = 0; + for (int i = 1; i <= m; i++) { + prev = curr; + curr = 1 - prev; + for (int j = 1; j <= n; j++) { + if(i == 0 || j == 0) { + dp[i][j] = 0; + continue; + } + dp[curr][j] = Math.max(dp[prev][j], dp[curr][j - 1]); + if (A.charAt(i - 1) == B.charAt(j - 1)) { + dp[curr][j] = Math.max(dp[curr][j], dp[prev][j - 1] + 1); + } + } + } + + return dp[curr][n]; + } +} + +// Print the longest common subsequence +public class Solution { + public int longestCommonSubsequence(String A, String B) { + if (A == null || B == null || A.length() == 0 || B.length() == 0) { + return 0; + } + int m = A.length(); + int n = B.length(); + int[][] dp = new int[m + 1][n + 1]; + int[][] pi = new int[m + 1][n + 1]; // stores case1, case2, or case3 + + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if(i == 0 || j == 0) { + dp[i][j] = 0; + continue; + } + dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]); + pi[i][j] = dp[i - 1][j] > dp[i][j - 1] ? 1 : 2; + + if (A.charAt(i - 1) == B.charAt(j - 1)) { + dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - 1] + 1); + if (dp[i][j] == dp[i - 1][j - 1] + 1) { + pi[i][j] = 3; + } + } + } + } + + //Prepare for printing + char[] res = new char[dp[m][n]]; + int i = m; + int j = n; + int w = dp[m][n] - 1; + while (i > 0 && j > 0) { + if (pi[i][j] == 1) { + i--; + } else if (pi[i][j] == 2) { + j--; + } else {//3 + res[w] = A.charAt(i - 1); + i--; + j--; + w--; + } + } + + for (int k = 0; k < dp[m][n]; k++) { + System.out.print(res[k]); + } + System.out.println(); + + return dp[m][n]; + } +} + +/* +Preivous Note. + Thinking process: Using DP. check[i][j] means: the length of longest common subsequnce between A(0 ~ i) and B(0 ~ j). @@ -25,7 +183,7 @@ Given two strings, find the longest comment subsequence (LCS). 1. A(i-1) == B(j - 1), then check[i][j] = check[i - 1][j - 1] + 1; 2. A(i-1) != B(j - 1), then pick the max between (i-1,j) , (i,j-1) and (i, j ) Note: check[][] is initialized with all 0's. Index (0,0) is used as starting 0. -*/ + */ public class Solution { /** * @param A, B: Two strings. @@ -51,3 +209,5 @@ public int longestCommonSubsequence(String A, String B) { } + +``` \ No newline at end of file diff --git a/Java/Longest Common Substring.java b/Java/Longest Common Substring.java old mode 100644 new mode 100755 index 62d320c..75adabe --- a/Java/Longest Common Substring.java +++ b/Java/Longest Common Substring.java @@ -1,3 +1,25 @@ +M +1525239157 +tags: DP, Double Sequence DP, String, Sequence DP + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + +``` /* Given two strings, find the longest common substring. @@ -15,6 +37,106 @@ Tags Expand LintCode Copyright Longest Common Subsequence Dynamic Programming +*/ + +/** +Thoughts: double sequence DP +dp[i][j]: for first i chars in A, and first j chars in B, what's the longest common substring length? + +init: +if i == 0 || j == 0, no common substring, dp[i][j] = 0; + +function: +dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +otherwise, dp[i][j] = 0; start over + +Track max +*/ +public class Solution { + public int longestCommonSubstring(String A, String B) { + if (A == null || B == null || A.length() == 0 || B.length() == 0) { + return 0; + } + int m = A.length(); + int n = B.length(); + int[][] dp = new int[m + 1][n + 1]; + int max = 0; + for (int i = 0; i <= m; i++) { + for(int j = 0; j <= n; j++) { + if (i == 0 || j == 0) { + dp[i][j] = 0; + continue; + } + if (A.charAt(i - 1) == B.charAt(j - 1)) { + dp[i][j] = dp[i - 1][j - 1] + 1; + } else { + dp[i][j] = 0; + } + max = Math.max(max, dp[i][j]); + } + } + return max; + } +} + +// Rolling array, space O(n) +public class Solution { + public int longestCommonSubstring(String A, String B) { + if (A == null || B == null || A.length() == 0 || B.length() == 0) { + return 0; + } + int m = A.length(); + int n = B.length(); + int[][] dp = new int[2][n + 1]; + int max = 0; + for (int i = 0; i <= m; i++) { + for(int j = 0; j <= n; j++) { + if (i == 0 || j == 0) { + dp[i % 2][j] = 0; + continue; + } + if (A.charAt(i - 1) == B.charAt(j - 1)) { + dp[i % 2][j] = dp[(i - 1) % 2][j - 1] + 1; + } else { + dp[i % 2][j] = 0; + } + max = Math.max(max, dp[i % 2][j]); + } + } + return max; + } +} + + + + + +/** +String: find all possible substring, try all of them +O(n^2) + */ +public class Solution { + public int longestCommonSubstring(String A, String B) { + if (A == null || B == null || A.length() == 0 || B.length() == 0) { + return 0; + } + int n = A.length(); + + int max = 0; + for (int i = 0; i < n; i++) { + for(int j = 0; j <= i; j++) { + String subStr = A.substring(j, i + 1); + if (B.contains(subStr)) { + max = Math.max(max, subStr.length()); + } + } + } + return max; + } +} + +/** +Previous notes Thoughts: 1. Compare all i X j. 2. Use a D[i][j] to mark the amount of common substring based on D[i - 1][j -1]. Could be 0. @@ -22,10 +144,7 @@ NOTE1: create 2D array that's [N + 1][M + 1] because we want to hold D[n][M] in the 2d array NOTE2: be carefule with init index 0's - -*/ - - + */ public class Solution { /** * @param A, B: Two string. @@ -54,3 +173,7 @@ public int longestCommonSubstring(String A, String B) { return max; } } + + + +``` \ No newline at end of file diff --git a/Java/Longest Consecutive Sequence.java b/Java/Longest Consecutive Sequence.java old mode 100644 new mode 100755 index 19f3b02..150f662 --- a/Java/Longest Consecutive Sequence.java +++ b/Java/Longest Consecutive Sequence.java @@ -1,3 +1,32 @@ +H +1527002881 +tags: Array, Hash Table, Union Find + +给一串数字, unsorted, 找这串数字里面的连续元素序列长度 (consecutive序列, 是数字连续, 并不是说要按照原order) + +#### HashSet +- 要想看连续元素, 必须要num++, num--这样搜索 +- 1. 需要O(1)找到元素 +- 2. 需要简单快速找到 num - 1, num + 1. +- 如果用min,max开array, 耗费空间 +- 用HashSet来存, 用set.contains() 来查找 num - 1, num + 1 存在与否 +- for loop. O(n) +- 里面的while loop 一般不会有O(n); 一旦O(n), 也意味着set 清零, for loop也不会有更多 inner while 的衍生. +- overall O(n) 时间复杂度 + + +#### Union Find +- 最终是要把相连的元素算一下总长, 其实也就是把元素group起来, 相连的group在一起, 于是想到UnionFind +- 这里用到了一个`int[] size` 来帮助处理 `合并的时候parent是哪个`的问题: 永远往group大的union里去 +- main function 里面, 有一个map来track, 每个元素, 只处理1遍. +- union的内容: current number - 1, current number + 1 +- https://www.jianshu.com/p/e6b955ca208f + +##### 特点 +- Union Find 在index上做好像更加容易 +- 其他union find function: `boolean connected(a,b){return find(a) == find(b)}` + +``` /* Given an unsorted array of integers, find the length of the longest consecutive elements sequence. @@ -8,7 +37,132 @@ Your algorithm should run in O(n) complexity. Hide Tags Array +*/ + +/* +Thoughts: +Find a way to: +1. directly check existance of a number +2. quickly gets to the num-1, num+1, if exist. + +One way: find min,max of the number, and set up a long array. That will be waste of space, not applicable. + +Instead: use a set and put all numbers in. + +Iterate all nums: if one num exist, find all of the num++, num-- and track the length +*/ +public class Solution { + /** + * @param num: A list of integers + * @return: An integer + */ + public int longestConsecutive(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + Set set = new HashSet<>(); + for (int num : nums) { + set.add(num); + } + + int longest = 0; + for (int num : nums) { + int start = num; + while (set.contains(start - 1)) { + start--; + set.remove(start); + } + + int end = num; + while (set.contains(end + 1)) { + end++; + set.remove(end); + } + longest = Math.max(longest, end - start + 1); + } + return longest; + } +} + + + +// Union Find +class Solution { + public int longestConsecutive(int[] nums) { + // edge case + if (nums == null || nums.length == 0) { + return 0; + } + + // init union find and union all numbers + int n = nums.length; + Map visited = new HashMap(); + UnionFind uf = new UnionFind(n); + for (int i = 0; i < n; i++) { + if(visited.containsKey(nums[i])) { + continue; + } + visited.put(nums[i], i); + if (visited.containsKey(nums[i] - 1)) { + uf.union(i, visited.get(nums[i] - 1)); + } + if (visited.containsKey(nums[i] + 1)) { + uf.union(i, visited.get(nums[i] + 1)); + } + } + + // use returned uf.map to find # of duplicated parent + return uf.maxUnion(); + } +} + +class UnionFind { + int[] parent; + int[] size; + // constructor + public UnionFind(int n) { + parent = new int[n]; + size = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + size[i] = 1; + } + } + + // find parent function + public int find(int x) { + if (parent[x] == x) { + return x; + } + + return parent[x] = find(parent[x]); + } + + // union function + public void union(int a, int b) { + a = find(a); + b = find(b); + if (size[a] > size[b]) { + parent[b] = a; + size[a] += size[b]; + } else { + parent[a] = b; + size[b] += size[a]; + } + } + + public int maxUnion() { + int max = 1; + for (int i = 0; i < size.length; i++) { + max = Math.max(max, size[i]); + } + return max; + } +} + +/** +Previous notes: Thinking process: 0. This problem can be done using sorting, but time complexity of sorting is O(nlogn). This problem requires O(n). 1. Want to check if a number's left and right is consecutive to itself, but cannot do it due to the given unsorted array: think about a Hashmap. @@ -18,8 +172,7 @@ Your algorithm should run in O(n) complexity. If a number exist in the hashmap and its value is 'true', then we need to skip this number beacuse it has been checked. 4. Track the total number consecutives of 1 perticular number, compare it with the maxL. Save the Math.max to maxL. 5. Depending on the problem, we can store a consecutive sequence or simply just its length: maxL. This problem wants the maxL. -*/ - + */ public class Solution { public int longestConsecutive(int[] num) { if (num == null || num.length == 0) { @@ -97,3 +250,5 @@ public int longestConsecutive(int[] num) { return max; } } + +``` \ No newline at end of file diff --git a/Java/Longest Continuous Increasing Subsequence.java b/Java/Longest Continuous Increasing Subsequence.java new file mode 100644 index 0000000..43255f9 --- /dev/null +++ b/Java/Longest Continuous Increasing Subsequence.java @@ -0,0 +1,133 @@ +E +1522812393 +tags: Array, DP, Coordinate DP + +找连续的持续上升子序列的长度. + +#### Coordinate DP +- 1D coordinate, dp 的角标, 就是代表 index i 的状态 +- 求最值, dp[i] = 在index i位置的最长子序列 +- 如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1 +- 如果没有持续上升, 那么dp[i] = 1, 重头来过 +- maintain max + +#### Basic +- 用一个数存current count, maintain max + +``` +/* +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ +与LintCode的 LICS类似. + +Given an unsorted array of integers, find the length of longest continuous increasing subsequence (subarray). + +Example 1: +Input: [1,3,5,4,7] +Output: 3 +Explanation: The longest continuous increasing subsequence is [1,3,5], its length is 3. +Even though [1,3,5,7] is also an increasing subsequence, it's not a continuous one where 5 and 7 are separated by 4. +Example 2: +Input: [2,2,2,2,2] +Output: 1 +Explanation: The longest continuous increasing subsequence is [2], its length is 1. +Note: Length of the array will not exceed 10,000. + */ + +// simply dp[i] = 1; dp[i] += dp[i - 1] if nums[i] > nums[i-1] +class Solution { + public int findLengthOfLCIS(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] dp = new int[2]; + dp[0] = 1; + int max = 1; + for (int i = 1; i < n; i++) { + dp[i % 2] = 1; + if (nums[i] > nums[i - 1]) { + dp[i % 2] += dp[(i - 1) % 2]; + } + max = Math.max(max, dp[i % 2]); + } + return max; + } +} + +/* +Thoughts: +'longest' => DP +dp[i]: represents the #of the on-going continuous increasing digits up to index i. +if (nums[i] > nums[i-1]), dp[i] = dp[i-1] + 1 +init: dp[0] = 1. If just given 1 digit, itself will count a sequence, so dp[i] = 1; + +initialize: dp[0~n] = 1. +maintain a max. + +Time, space: O(n) +*/ +class Solution { + public int findLengthOfLCIS(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] dp = new int[n]; + dp[0] = 1; + int max = 1; + for (int i = 1; i < n; i++) { + dp[i] = 1; + if (nums[i] > nums[i - 1]) { + // Below can just be dp[i] = dp[i - 1] + 1; since dp[i - 1] + 1 is always greater than 1 + dp[i] = Math.max(dp[i], dp[i - 1] + 1); + } + max = Math.max(max, dp[i]); + } + return max; + } +} + + +// Rolling Array +// Spce O(1) +class Solution { + public int findLengthOfLCIS(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] dp = new int[2]; + dp[0] = 1; + int max = 1; + for (int i = 1; i < n; i++) { + dp[i % 2] = 1; + if (nums[i] > nums[i - 1]) { + dp[i % 2] = Math.max(dp[i % 2], dp[(i - 1) % 2] + 1); + } + max = Math.max(max, dp[i % 2]); + } + return max; + } +} + + +/* +Thoughts: +Simplify dp solution: using just 1 integer to hold current count +*/ +class Solution { + public int findLengthOfLCIS(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int max = 0; + int curr = 0; + for (int i = 1; i < nums.length; i++) { + curr = nums[i] > nums[i - 1] ? curr + 1 : 0; + max = Math.max(curr, max); + } + return max + 1; + } +} + +``` diff --git a/Java/Longest Increasing Continuous subsequence II.java b/Java/Longest Increasing Continuous subsequence II.java old mode 100644 new mode 100755 index f8b6ecf..92aa6de --- a/Java/Longest Increasing Continuous subsequence II.java +++ b/Java/Longest Increasing Continuous subsequence II.java @@ -1,9 +1,30 @@ -O(mn) space for dp and flag. -O(mn) runtime because each spot will be marked once visited. -这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 +M +1525245193 +tags: Array, DP, Coordinate DP, Memoization + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + ``` /* -Give you an integer matrix (with row size n, column size m),find the longest increasing continuous subsequence in this matrix. (The definition of the longest increasing continuous subsequence here can start at any row or column and go up/down/right/left any direction). +LintCode, only available in algorithm2. + +Give you an integer matrix (with row size n, column size m), +find the longest increasing continuous subsequence in this matrix. + +(The definition of the longest increasing continuous subsequence here +can start at any row or column and go up/down/right/left any direction). Example Given a matrix: diff --git a/Java/Longest Increasing Continuous subsequence.java b/Java/Longest Increasing Continuous subsequence.java old mode 100644 new mode 100755 index 24c7255..e1b8f31 --- a/Java/Longest Increasing Continuous subsequence.java +++ b/Java/Longest Increasing Continuous subsequence.java @@ -1,9 +1,21 @@ +E +1525244964 +tags: Array, DP, Coordinate DP + +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + O(n)跑2遍for. O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. 特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 + ``` +//LintCode +//跟LeetCode的 LCIS 几乎一样. https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + /* -Give you an integer array (index from 0 to n-1, where n is the size of this array),find the longest increasing continuous subsequence in this array. (The definition of the longest increasing continuous subsequence here can be from right to left or from left to right) +Give you an integer array (index from 0 to n-1, where n is the size of this array), +find the longest increasing continuous subsequence in this array. +(The definition of the longest increasing continuous subsequence here can be from right to left or from left to right) Example For [5, 4, 2, 1, 3], the LICS is [5, 4, 2, 1], return 4. For [5, 1, 2, 3, 4], the LICS is [1, 2, 3, 4], return 4. @@ -47,13 +59,14 @@ public int longestIncreasingContinuousSubsequence(int[] A) { return ans; } } -``` +/** 九章的DP,没有用O(1)space,但是应该是为这道题的followup做准备的模式。 用dp和dfs. 每次dfs左右时都要mark一下flag,防止重跑 -``` +*/ + /* Thoughts: JiuZhang's DP. dp[i]: longest increasing continous subsequence on i, regardless if the sequence is incresing from left or right. @@ -104,17 +117,7 @@ public int dfs(int[] A, int[] dp, int[] flag, int i){ - -``` - - - - - - - -老码 -``` +// Previous /* Older approach. Longer code :( Thoughts: diff --git a/Java/Longest Increasing Path in a Matrix.java b/Java/Longest Increasing Path in a Matrix.java new file mode 100644 index 0000000..00fd1ad --- /dev/null +++ b/Java/Longest Increasing Path in a Matrix.java @@ -0,0 +1,110 @@ +H +1521561205 +tags: DFS, Memoization, Topological Sort, DP, Coordinate DP + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + +``` +/* +Given an integer matrix, find the length of the longest increasing path. + +From each cell, you can either move to four directions: left, right, up or down. +You may NOT move diagonally or move outside of the boundary (i.e. wrap-around is not allowed). + +Example 1: + +nums = [ + [9,9,4], + [6,6,8], + [2,1,1] +] +Return 4 +The longest increasing path is [1, 2, 6, 9]. + +Example 2: + +nums = [ + [3,4,5], + [3,2,6], + [2,2,1] +] +Return 4 +The longest increasing path is [3, 4, 5, 6]. Moving diagonally is not allowed. +*/ + +/* +Thoughts: +1. Need to start from each position (x,y) to 4 directions: O(4 * m * n) +2. For each direction, if meet the requirement curr < neighbor, move to neighbor +3. We should avoid recalculating the longest path start from (i, j) in the board, should memorize it with dp[x][y] and flag them as visited. + +dp[x][y]: max increasing path length if starting form point (x, y). + +init: dp[x][y] = 1, any element by itself counts as 1 +*/ +class Solution { + boolean[][] visited; + int[][] dp; + int[] dx = {0, 0, 1, -1}; + int[] dy = {1, -1, 0, 0}; + public int longestIncreasingPath(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { + return 0; + } + int m = matrix.length; + int n = matrix[0].length; + visited = new boolean[m][n]; + dp = new int[m][n]; + int max = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + max = Math.max(max, dfs(matrix, i, j)); + } + } + return max; + } + + private int dfs(int[][] matrix, int x, int y) { + if (visited[x][y]) { + return dp[x][y]; + } + // Initialize: + dp[x][y] = 1; + + // Calculating, dfs + for (int i = 0; i < dx.length; i++) { + int nx = x + dx[i]; + int ny = y + dy[i]; + if (validateCoordinate(matrix, x, y, nx, ny)) { + dp[x][y] = Math.max(dp[x][y], dfs(matrix, nx, ny) + 1); + } + } + visited[x][y] = true; + return dp[x][y]; + } + + private boolean validateCoordinate(int[][] matrix, int x, int y, int nx, int ny) { + return nx >= 0 && nx < matrix.length && ny >= 0 && ny < matrix[0].length + && matrix[x][y] < matrix[nx][ny]; + } +} +``` \ No newline at end of file diff --git a/Java/Longest Increasing Subsequence.java b/Java/Longest Increasing Subsequence.java old mode 100644 new mode 100755 index 1299675..ff6d8a3 --- a/Java/Longest Increasing Subsequence.java +++ b/Java/Longest Increasing Subsequence.java @@ -1,90 +1,125 @@ -i位和之前的0~i-1 都远关系。复杂一点。 -每次都考虑o~i的所有情况。所以double for loop -``` -/* -Given a sequence of integers, find the longest increasing subsequence (LIS). - -You code should return the length of the LIS. - -Example -For [5, 4, 1, 2, 3], the LIS is [1, 2, 3], return 3 +M +1531724912 +tags: Binary Search, DP, Coordinate DP, Memoization +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. -For [4, 2, 4, 5, 3, 7], the LIS is [4, 4, 5, 7], return 4 - -Challenge -Time complexity O(n^2) or O(nlogn) +``` -Clarification -What's the definition of longest increasing subsequence? +/** +Given an unsorted array of integers, find the length of longest increasing subsequence. - * The longest increasing subsequence problem is to find a subsequence of a given sequence in which the subsequence's elements are in sorted order, lowest to highest, and in which the subsequence is as long as possible. This subsequence is not necessarily contiguous, or unique. +For example, +Given [10, 9, 2, 5, 3, 7, 101, 18], +The longest increasing subsequence is [2, 3, 7, 101], therefore the length is 4. +Note that there may be more than one LIS combination, +it is only necessary for you to return the length. - * https://en.wikipedia.org/wiki/Longest_common_subsequence_problem +Your algorithm should run in O(n2) complexity. -Tags Expand -Binary Search LintCode Copyright Dynamic Programming -*/ +Follow up: Could you improve it to O(n log n) time complexity? + */ /* - Thoughts: - dp[i] depends on not only dp[i-1], but also [i-1] ...[0]. - So it has to be double-for loop. - Each sub-for loop on i, traverse 0 ~ j(j<=i) to find largest number to put on dp[i] - fn: - dp[i] = dp[i] > dp[j] + 1 ? dp[i] : dp[j] + 1 - init: - dp[i] initally all = 1. (i = 0 ~ n). If no other number meets the requirement, at least it has itself. - Result: - dp[n - 1] +Thoughts: +Want to the max increasing sequence length. For example, if nums[i] > nums[i - 1], count++. +To find all count through [0 ~ i] history, need to calculate them all and count the maximum. +dp[i]: represents the max increasing sequence length for nums[i]. +Note: not 'up to i', it's just specifically calculated for index i: nums[i] - Note: nums[j] <= nums[i] is the 'increasing' requirement - dp[j] + 1 means: best we can do at dp[j] + 1, is this better than what we already have on dp[i]? +Trying to compare all prior values: +dp[i] = dp[i] > dp[j] + 1 ? dp[i] : dp[j] + 1, where j = [0, i). +dp[0]: max index count should = 0 since only 1 element in the race. */ - - -public class Solution { - /** - * @param nums: The integer array - * @return: The length of LIS (longest increasing subsequence) - */ - public int longestIncreasingSubsequence(int[] nums) { - if (nums == null || nums.length == 0) { - return 0; - } - int n = nums.length; - int[] dp = new int[n]; - int max = 0; - for (int i = 0; i < n; i++) { - dp[i] = 1; - for (int j = 0; j < i; j++) { - if (nums[j] <= nums[i]){ - dp[i] = dp[i] > dp[j] + 1 ? dp[i] : dp[j] + 1; - } - } - if (dp[i] > max) { - max = dp[i]; - } - } - return max; +class Solution { + public int lengthOfLIS(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] dp = new int[n]; // dp[0] = 0 + int max = 0; + + for (int i = 0; i < n; i++) { + for (int j = 0; j < i; j++) { + if (nums[i] > nums[j]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + max = Math.max(dp[i], max); + } + return max + 1; // 连续增长了max次, 序列长度为 max+1 } } - - - - - - - - - - - - - - - - +/* +Thoughts: +O(nLogN) using binary serach. +Maintain a list of nums in increasing order. +When considering new num: +- See if it can append (num > last-max-num from the list) +- If not, do binary search with the list and see where the number may fit. +- Every time, set num to where may fit in the list (find the smallest item from list which also > num) + +Why setting a number in the list? +The list works as a baseline, which adjusts dynamically: any number less than the baseline won't be able to append. +However, it can hellp refine (lower) the baseline, which potentially allow future number to append. + +In the end, return the size of list. +*/ +class Solution { + public int lengthOfLIS(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + List list = new ArrayList<>(); + for (int i = 0; i < n; i++) { + if (list.size() == 0 || nums[i] > list.get(list.size() - 1)) { + list.add(nums[i]); + } else { + int index = binarySearch(list, nums[i]); + list.set(index, nums[i]); + } + } + return list.size(); + } + + public int binarySearch(List list, int target) { + int start = 0; + int end = list.size() - 1; + while (start + 1 < end) { + int mid = start + (end - start) / 2; + if (list.get(mid) == target) { + return mid; + } else if (list.get(mid) < target) { + start = mid; + } else { + end = mid; + } + } + return list.get(start) >= target ? start : end; + } +} ``` \ No newline at end of file diff --git a/Java/Longest Palindromic Subsequence.java b/Java/Longest Palindromic Subsequence.java new file mode 100644 index 0000000..077db9f --- /dev/null +++ b/Java/Longest Palindromic Subsequence.java @@ -0,0 +1,217 @@ +M +1530034378 +tags: DP, Interval DP, Memoization, DFS + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + +``` +/* +Given a string s, find the longest palindromic subsequence's length in s. +You may assume that the maximum length of s is 1000. + +Example 1: +Input: + +"bbbab" +Output: +4 +One possible longest palindromic subsequence is "bbbb". +Example 2: +Input: + +"cbbd" +Output: +2 +One possible longest palindromic subsequence is "bb". + +*/ + +/* +Thougths: +1. 区间型. Consider dp[i][j] represent the status from s[i] ~ s[j]. +Iterate over the length between i and j: len = j - i + 1 => j = len + i - 1; +For all possible length, what try i and j. + +2. For a string s[i ~ j], it contains a palindrome if: + a. s[i, j] is a palindrome. dp value + 2 since the two indexes are counted, we need dive dpper into dp[i + 1][j - 1] + b. s[i + 1, j] is a palindrome + c. s[i, j - 1] is a palindrome + +3. Initialize for three different length: + a. len == 1 -> dp = 1 + b. len == 2 -> dp = 2 + c. len == 3 -> calculate + +4. return dp[0][n - 1]: whole string +*/ +class Solution { + public int longestPalindromeSubseq(String s) { + if (s == null || s.length() == 0) { + return 0; + } + char[] arr = s.toCharArray(); + int n = s.length(); + int[][] dp = new int[n][n]; + + // len == 1 + for (int i = 0; i < n; i++) { + dp[i][i] = 1; + } + + // len == 2 + for (int i = 0; i < n - 1; i++) { + dp[i][i + 1] = arr[i] == arr[i + 1] ? 2 : 1; + } + + // len == 3 + for (int len = 3; len <= n; len++) { + for (int i = 0; i <= n - len; i++) { // i cannot over step into s[i, j] + int j = len + i - 1; + dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]); + if (arr[i] == arr[j]) { // as long as s[i]==s[j], they'll build outter palindrome from inner subsequence + dp[i][j] = Math.max(dp[i][j], dp[i + 1][j - 1] + 2); + } + } + } + return dp[0][n - 1]; + } +} + + +/** +Memoization +dp[i][j]: max palindrom length in range [i, j] +Three conditions: +1. i == j, check [i + 1, j - 1] +2. i != j, check [i + 1, j] +3. i != j, check [i, j - 1] + +Init dp[i][j] = -1 to track the progress, memoization + +Space: O(n^2) +Time: O(n^2) +*/ + +class Solution { + int[][] dp = null; + public int longestPalindromeSubseq(String s) { + if (s == null || s.length() == 0) { + return 0; + } + if (s.length() == 1) { + return 1; + } + int n = s.length(); + dp = new int[n][n]; + // init + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = -1; + } + } + + // len = 1 + for (int i = 0; i < n; i++) { + dp[i][i] = 1; + } + + // len = 2 + for (int i = 0; i < n - 1; i++) { + dp[i][i + 1] = s.charAt(i) == s.charAt(i + 1) ? 2 : 1; + } + + return dfs(s, 0, n - 1); + } + + public int dfs(String s, int x, int y) { + if (dp[x][y] != -1) { + return dp[x][y]; + } + if (s.charAt(x) == s.charAt(y)) { + dfs(s, x + 1, y - 1); + if (dp[x + 1][y - 1] != -1) { + dp[x][y] = dp[x + 1][y - 1] + 2; + } + } else { + dp[x][y] = Math.max(dfs(s, x + 1, y), dfs(s, x, y - 1)); + } + return dp[x][y]; + } +} + +// Same memoization, where dfs does not return any value +class Solution { + int[][] dp = null; + public int longestPalindromeSubseq(String s) { + if (s == null || s.length() == 0) { + return 0; + } + if (s.length() == 1) { + return 1; + } + int n = s.length(); + dp = new int[n][n]; + // init + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = -1; + } + } + + // len = 1 + for (int i = 0; i < n; i++) { + dp[i][i] = 1; + } + + // len = 2 + for (int i = 0; i < n - 1; i++) { + dp[i][i + 1] = s.charAt(i) == s.charAt(i + 1) ? 2 : 1; + } + + dfs(s, 0, n - 1); + + return dp[0][n - 1]; + } + + public void dfs(String s, int x, int y) { + if (dp[x][y] != -1) { + return; + } + if (s.charAt(x) == s.charAt(y)) { + dfs(s, x + 1, y - 1); + if (dp[x + 1][y - 1] != -1) { + dp[x][y] = dp[x + 1][y - 1] + 2; + return; + } + } + + dfs(s, x + 1, y); + dfs(s, x, y - 1); + dp[x][y] = Math.max(dp[x + 1][y], dp[x][y - 1]); + } +} + + +``` diff --git a/Java/Longest Palindromic Substring.java b/Java/Longest Palindromic Substring.java old mode 100644 new mode 100755 index 9093384..c39ac1a --- a/Java/Longest Palindromic Substring.java +++ b/Java/Longest Palindromic Substring.java @@ -1,3 +1,30 @@ +M +1530027760 +tags: String, DP + +给一个string, 找到最长的palindrome substring. + +Related: Longest Palindromic Subsequence, Palindrome Partioning II + +O(n^2) is not too hard to think of. How about O(n)? + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + +#### DP: isPalin[][] +- 穷举double for loop. O(n^2) +- boolean isPalin[i][j], 每次确认有palindrome就记录下来true / false +- 穷举的for loop计算顺序: end point j, and stat point i = [0, j] +- 在计算 isPalin[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- double for loop: O(n^2). slower, because it guarantees O(n^2) due to the for loop + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + +``` /* Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring. @@ -7,26 +34,49 @@ Challenge O(n2) time is acceptable. Can you do it in O(n) time. -Tags Expand -String -*/ -/* - O(n) way, not done yet +Hide Company Tags Amazon Microsoft Bloomberg +Hide Tags String +Hide Similar Problems (H) Shortest Palindrome (E) Palindrome Permutation + + */ +// O(n^2) +public class Solution { + private int start, maxLen; + + public String longestPalindrome(String s) { + if (s == null || s.length() <= 1) { + return s; + } + for (int i = 0; i < s.length() - 1; i++) { + findMaxLen(s, i, i); // odd middle point i + findMaxLen(s, i, i + 1); // even s(i) == s(i+1) + } + return s.substring(start, start + maxLen); + } + + public void findMaxLen(String s, int i, int j) { + while (i >= 0 && j < s.length() && s.charAt(i) == s.charAt(j)) { + i--; + j++; + } + //Note: i and j has moved apart 1 extra step after while loop + if (maxLen < j - i - 1) { + maxLen = j - i - 1; + start = i + 1; + } + } +} /* - O(n^2) + O(n^2), but time exceeded Thoughts: Like Palindrome Partioning II, try to use isPal[i][j] to verify each string (i,j). If string(i,j) is valid, note down the (i,j) portion and find the longest. This is a standard O(n^2) process */ public class Solution { - /** - * @param s input string - * @return the longest palindromic substring - */ public String longestPalindrome(String s) { if (s == null || s.length() == 0) { return s; @@ -44,3 +94,5 @@ public String longestPalindrome(String s) { return maxStr; } } + +``` \ No newline at end of file diff --git a/Java/Longest Substring Without Repeating Characters.java b/Java/Longest Substring Without Repeating Characters.java old mode 100644 new mode 100755 index b1cc0cc..dcaaa9e --- a/Java/Longest Substring Without Repeating Characters.java +++ b/Java/Longest Substring Without Repeating Characters.java @@ -1,8 +1,107 @@ +M +1519971156 +tags: Hash Table, Two Pointers, String + +方法1: +Two Pointers +双指针: 从start开始遍历, 但是第一步是while loop来推进end; 直到推不动end, 然后start++ +巧妙点: 因为end是外围variable, 在start的loop上, end不会重置;[star ~ end] 中间不需要重复计算. +最终可以O(n); + +Previous verison of two pointers: +用两个pointer, head和i. +注意:head很可能被退回到很早的地方,比如abbbbbba,当遇到第二个a,head竟然变成了 head = 0+1 = 1. +当然这是不对的,所以head要确保一直增长,不回溯 + +方法2: + HashMap: + 一旦有重复, rest map. + 没有重复时候, 不断map.put(), 然后求max值 + +问题: 每次reset map之后就开始从新从一个最早的index计算, 最坏情况是O(n^2): +'abcdef....xyza' + ``` /* Given a string, find the length of the longest substring without repeating characters. +Examples: + +Given "abcabcbb", the answer is "abc", which the length is 3. + +Given "bbbbb", the answer is "b", with the length of 1. + +Given "pwwkew", the answer is "wke", with the length of 3. Note that the answer must be a substring, "pwke" is a subsequence and not a substring. + + + */ +/* +Thoughts: +two pointers +Find the end fisrt, then move the start. +O(n) +*/ +class Solution { + public int lengthOfLongestSubstring(String s) { + if (s == null || s.length() == 0) { + return 0; + } + boolean[] chars = new boolean[256]; // 256 ASCII code + int rst = 0; + int start = 0; + int end = 0; + while (start < s.length()) { + while (end < s.length() && !chars[s.charAt(end)]) { + chars[s.charAt(end)] = true; + rst = Math.max(rst, end - start + 1); + end++; + } + chars[s.charAt(start)] = false; + start++; + } + return rst; + } +} + +/* +Thoughts: +1. Use hashmap to mark indexes of chars. +2. When no duplicate, put in map, and compare Math.max(rst, map.size()) +3. If duplicated c appears, should ignore all index before the fist c, and start fresh: + reset i = map.get(c); map = new HashMap<>(), clean up the hash. + +Time: +O(n^2): when 'abcdefg.....xyza' happends +*/ +class Solution { + public int lengthOfLongestSubstring(String s) { + if (s == null || s.length() == 0) { + return 0; + } + HashMap map = new HashMap<>(); + char[] arr = s.toCharArray(); + int rst = Integer.MIN_VALUE; + for (int i = 0; i < arr.length; i++) { + char c = arr[i]; + if (map.containsKey(c)) { + i = map.get(c); // reset beginning + map = new HashMap<>(); + } else { + map.put(c, i); + } + rst = Math.max(rst, map.size()); + } + return rst == Integer.MIN_VALUE ? 0 : rst; + } +} + + + +/* +LintCode +Given a string, find the length of the longest substring without repeating characters. + Example For example, the longest substring without repeating letters for "abcabcbb" is "abc", which the length is 3. @@ -14,20 +113,45 @@ Tags Expand String Two Pointers Hash Table */ - /* -Attempt1: What are you 弄啥唻?我第一个attempt每次遇到duplicate都打碎重来,我期待着这个时间pass不了啊..虽然硬跑的逻辑是说得通的。 + 02.02.2016 + Attempt2, Thoughts: + HashMap map + When char re-appear in map, 1. move head to repeating char's index + 1, 2. renew map with current index -Thoughts: + Note: head could repeat in earlier index, so make sure head does not travel back +*/ + +public class Solution { + public int lengthOfLongestSubstring(String s) { + if (s == null || s.length() == 0) { + return 0; + } + HashMap map = new HashMap(); + int head = 0; + int max = 0; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (map.containsKey(c)) { + if (map.get(c) + 1 > head) {//make sure head does not travel back + head = map.get(c) + 1; + } + } + map.put(c, i); + String str = s.substring(head, i + 1); + max = Math.max(max, str.length()); + } + return max; + } +} + +/* +Attempt1, Thoughts: Loop the string, HashSet to store the Character, count++, and use a max to store longest length. Whenever a char exist in hashset, new hashset and count = 0, set i = 1st duplicate char, followed by i++, now start again. */ public class Solution { - /** - * @param s: a string - * @return: an integer - */ public int lengthOfLongestSubstring(String s) { if (s == null || s.length() == 0) { return 0; diff --git a/Java/Longest Substring with At Most K Distinct Characters.java b/Java/Longest Substring with At Most K Distinct Characters.java old mode 100644 new mode 100755 index acce12d..121a0fc --- a/Java/Longest Substring with At Most K Distinct Characters.java +++ b/Java/Longest Substring with At Most K Distinct Characters.java @@ -1,13 +1,16 @@ -大清洗。 -map.size一旦超标,要把longest string最开头(marked by pointer:start)的那个char抹掉,而且要把它所有的appearance都抹掉;这样还不够,它最后一次出现以前的其他所有chars,也都要抹掉。 -大清洗的原因是: 一旦某一个char要被清除,由于substring必须是连续的,所以在它之前的所有chars都要被清洗。 -我去,黑帮大哥除龙头啊。 -简直就是要消灭伏地魔的7个魂器。 +H +1520228394 +tags: Hash Table, String, Sliding Window + +大清洗 O(nk) +map.size一旦>k,要把longest string最开头(marked by pointer:start)的那个char抹掉 +一旦某一个char要被清除,所以在这个char 的1st and last appearance之间的char都要被清洗from map + + ``` /* Given a string s, find the length of the longest substring T that contains at most k distinct characters. -Have you met this question in a real interview? Yes Example For example, Given s = "eceba", k = 3, @@ -16,9 +19,53 @@ Challenge O(n), n is the size of the string s. -Tags Expand +Tags Expand String Two Pointers LintCode Copyright Hash Table + +*/ + +/* +Thoughts: +Two pointer, use HashMap to record the passed . +If map.size() == k, check string length. +If map.size() > k, start moving front index i until map.size() reduces [while] */ +class Solution { + public int lengthOfLongestSubstringKDistinct(String s, int k) { + if (s == null || s.length() == 0 || k <= 0) { + return 0; + } + int n = s.length(); + int start = 0, end = 0, rst = Integer.MIN_VALUE; + HashMap map = new HashMap<>(); + while (start < n) { // left window + while (end < n) { + char c = s.charAt(end); + if (map.containsKey(c)) { + map.put(c, map.get(c) + 1); + } else { + if (map.size() == k) break; // meet window size + map.put(c, 1); + } + end++; + } + // Calculate length when map.size() == k or end == n + rst = Math.max(rst, end - start); + + // move start forward and clean up map + char c = s.charAt(start); + int count = map.get(c); + if (count == 1) { + map.remove(c); + } else { + map.put(c, count - 1); + } + start++; + } + + return rst; + } +} /* Thoughts: @@ -33,11 +80,6 @@ Therefore, we need erase that particular char and all other chars before that particular char's last apperance. */ public class Solution { - /** - * @param s : A string - * @return : The length of the longest substring - * that contains at most k distinct characters. - */ public int lengthOfLongestSubstringKDistinct(String s, int k) { if (s == null || s.length() == 0 || k <= 0) { return 0; @@ -71,4 +113,16 @@ public int lengthOfLongestSubstringKDistinct(String s, int k) { } } + + +/* + 3.1.2016. + How about map to store last occurance of each char. + Use pointer head to show which char to cut off from string + + Not work: still has to clean up all chars that being cut off from the map, which is O(n) again. + So the solutoin has to be O(nk) +*/ + + ``` \ No newline at end of file diff --git a/Java/Longest Substring with At Most Two Distinct Characters.java b/Java/Longest Substring with At Most Two Distinct Characters.java new file mode 100644 index 0000000..e7ff69b --- /dev/null +++ b/Java/Longest Substring with At Most Two Distinct Characters.java @@ -0,0 +1,59 @@ +H +1533538848 +tags: Hash Table, Two Pointers, String, Sliding Window + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + +``` +/* +Given a string s , find the length of the longest substring t that contains at most 2 distinct characters. + +Example 1: + +Input: "eceba" +Output: 3 +Explanation: t is "ece" which its length is 3. +Example 2: + +Input: "ccaabbb" +Output: 5 +Explanation: t is "aabbb" which its length is 5. + +*/ + +/* +Map to map last occurrance of certain character. +if map.size() > 2, we'll remove trop off left-most char +maintain max based on curr right - left +*/ +class Solution { + public int lengthOfLongestSubstringTwoDistinct(String s) { + if (s == null || s.length() == 0) return 0; + int n = s.length(); + Map lastOccurMap = new HashMap<>(); + int left = 0, right = 0, max = 0; + + while (right < n) { + if (lastOccurMap.size() <= 2) { // add new char + lastOccurMap.put(s.charAt(right), right++); + } + if (lastOccurMap.size() > 2) { // clean up left-most char + int leftMost = right; + for (int index : lastOccurMap.values()) { + leftMost = Math.min(leftMost, index); + } + lastOccurMap.remove(s.charAt(leftMost)); + left = leftMost + 1; + } + max = Math.max(max, right - left); + } + + return max; + } +} +``` \ No newline at end of file diff --git a/Java/Longest Univalue Path.java b/Java/Longest Univalue Path.java new file mode 100644 index 0000000..086bc47 --- /dev/null +++ b/Java/Longest Univalue Path.java @@ -0,0 +1,74 @@ +E + +弄明白path的意义: 连接node的edge. +要找MAX, 可以在class scope里面定义一个max variable. + +用minimum amount of code来概括几种不同的情况: left == root, right == root, or left == root == right. + +``` +/* +Given a binary tree, find the length of the longest path where each node in the path has the same value. This path may or may not pass through the root. + +Note: The length of path between two nodes is represented by the number of edges between them. + +Example 1: + +Input: + + 5 + / \ + 4 5 + / \ \ + 1 1 5 +Output: + +2 +Example 2: + +Input: + + 1 + / \ + 4 5 + / \ \ + 4 4 5 +Output: + +2 +Note: The given binary tree has not more than 10000 nodes. The height of the tree is not more than 1000. +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + private int max = 0; + public int longestUnivaluePath(TreeNode root) { + countUnivaluePath(root); + return max; + } + + public int countUnivaluePath(TreeNode root) { + if (root == null) { + return 0; + } + final int rootValue = root.val; + final TreeNode leftNode = root.left; + final TreeNode rightNode = root.right; + int leftCount = countUnivaluePath(leftNode); + int rightCount = countUnivaluePath(rightNode); + int leftPathCount = (leftNode != null && leftNode.val == rootValue) ? leftCount + 1 : 0; + int rightPathCount = (rightNode != null && rightNode.val == rootValue) ? rightCount + 1 : 0; + // leftPathCount and rightPathCount both exist only when left == root == right. + max = Math.max(max, leftPathCount + rightPathCount); + // For upper level, only one path needs to be counted + return Math.max(leftPathCount, rightPathCount); + } +} +``` \ No newline at end of file diff --git a/Java/Longest Valid Parentheses.java b/Java/Longest Valid Parentheses.java new file mode 100644 index 0000000..96c47dc --- /dev/null +++ b/Java/Longest Valid Parentheses.java @@ -0,0 +1,140 @@ +H +1530168397 +tags: Coordinate DP, Stack, String + +给一串string, 里面只有`(`, `)`. 找最长valid parentheses 的长度. + +#### 1D Coordinate DP +- use dp[i] track local max, maintain global max +- int[] dp. dp[i]: longest valid string that ends on i. +- 结尾是 ')', 2种情况: 1. 刚好s[i-1]是'('; 2. s[i]的')'更前面的一个起始'(' 对应 +- 注意, 结尾如果是'('属于不合理情况, 忽略. +- init: dp[0] = 0, 单个char不可能成型. +- 计算顺序: 从左到右, 找local max, maintain global max +- O(n) space, O(n) runtime + +#### Stack +- Stack 里面存所有的open/close parentheses. +- 如果遇到stack.top()刚好开合结掉, 就stack.pop(). +- 剩下的都是不合理的elements. +- 有点像negatively找 solution: `endIndex - 最后一个failedIndex(stack.pop()) - 1`, 应该就是最后一个succeeded string的长度 +- 每次更新 endIndex 为stack.top(), 然后从stack继续找下一个failedIndex +- 所有的length作比较, 就可以找出最长length +- O(n) stack space, O(n) runtime. 应该比dp慢一点, 因为做了2遍O(n) + + +``` + +/** +Given a string containing just the characters '(' and ')', +find the length of the longest valid (well-formed) parentheses substring. + +Example 1: + +Input: "(()" +Output: 2 +Explanation: The longest valid parentheses substring is "()" +Example 2: + +Input: ")()())" +Output: 4 +Explanation: The longest valid parentheses substring is "()()" + +*/ + +// DP, 98% +class Solution { + public int longestValidParentheses(String s) { + if (s == null || s.length() == 0) return 0; + int n = s.length(); + + // build && init dp[i]: longest valid string that ends at index i + int[] dp = new int[n]; + dp[0] = 0; + + // calculate dp && track global max + int max = 0; + for (int i = 1; i < n; i++) { + if (s.charAt(i) == ')') { + if (s.charAt(i - 1) == '(') { + dp[i] = 2 + addPriorLength(i - 1, dp); + } else { + int priorIndex = i - dp[i - 1] - 1; // 1 spot ahead of the known start index of valid string with dp[i-1] + if (priorIndex >= 0 && s.charAt(priorIndex) == '(') { + dp[i] = dp[i - 1] + 2 + addPriorLength(priorIndex, dp); + } + } + max = Math.max(max, dp[i]); + } + } + + return max; + } + + private int addPriorLength(int priorIndex, int[] dp) { + return (priorIndex - 1 >= 0) ? dp[priorIndex - 1] : 0; + } +} + +// A bit more concise code, 90% +class Solution { + public int longestValidParentheses(String s) { + if (s == null || s.length() == 0) return 0; + + // build && init dp[i]: longest valid string that ends at index i + int n = s.length(); + int[] dp = new int[n]; + + // calculate dp && track global max + int max = 0; + for (int i = 1; i < n; i++) { + if (s.charAt(i) == '(') continue; + + boolean consecutiveEnd = s.charAt(i - 1) == '('; + int priorIndex = consecutiveEnd ? i - 1 : i - dp[i - 1] - 1; + if (priorIndex < 0 || s.charAt(priorIndex) != '(') continue; + + dp[i] = 2 + addPriorLength(priorIndex, dp); + dp[i] += consecutiveEnd ? 0 : dp[i - 1]; //add length from (priorIndex, i - 1] + max = Math.max(max, dp[i]); + } + + return max; + } + + private int addPriorLength(int priorIndex, int[] dp) { + return (priorIndex - 1 >= 0) ? dp[priorIndex - 1] : 0; + } +} + +// Stack +class Solution { + public int longestValidParentheses(String s) { + if (s == null || s.length() == 0) return 0; + int n = s.length(); + // build stack && remove valid pairs + Stack stack = new Stack<>(); + char[] arr = s.toCharArray(); + for (int i = 0; i < n; i++) { + if (arr[i] == ')' && !stack.isEmpty() && arr[stack.peek()] == '(') { + stack.pop(); + continue; + } + stack.push(i); + } + + // go over stack && negatively calculate the longest valid result + int max = stack.isEmpty() ? n : 0; + int end = n, start = 0; + while (!stack.isEmpty()) { + start = stack.pop(); + max = Math.max(max, end - start - 1); + end = start; + } + // compare very last range [0 ~ end] => length = end + return Math.max(max, end); + } +} + + +``` \ No newline at end of file diff --git a/Java/Longest Word in Dictionary.java b/Java/Longest Word in Dictionary.java new file mode 100644 index 0000000..e3f7d2f --- /dev/null +++ b/Java/Longest Word in Dictionary.java @@ -0,0 +1,245 @@ +E +1526368976 +tags: Hash Table, Trie + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序, 排序以后才能逐个看是否partial string已经存在 +- 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 +- 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: 1. 长 string 排在前; 2. 相等length, 按照dictionary order 排序 +- 全部放入Trie. Trie.insert() +- 针对sorted words array, 从最长的开始找 Trie.startWith. +- 一旦找到, 就是符合题意的, 直接return. +- 注意: startWith 必须每一个node都是 isEnd, 才满足'逐个字母拼出' 的条件. +- Time: build Trie O(mn) + sort:O(nlogn) => O(nlogn) +- Space: O(mn) + +#### +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + +``` +/* +Given a list of strings words representing an English Dictionary, +find the longest word in words that can be built one character at a time +by other words in words. + +If there is more than one possible answer, +return the longest word with the smallest lexicographical order. + +If there is no answer, return the empty string. +Example 1: +Input: +words = ["w","wo","wor","worl", "world"] +Output: "world" +Explanation: +The word "world" can be built one character at a time by "w", "wo", "wor", and "worl". +Example 2: +Input: +words = ["a", "banana", "app", "appl", "ap", "apply", "apple"] +Output: "apple" +Explanation: +Both "apply" and "apple" can be built from other words in the dictionary. However, "apple" is lexicographically smaller than "apply". +Note: + +All the strings in the input will only contain lowercase letters. +The length of words will be in the range [1, 1000]. +The length of words[i] will be in the range [1, 30]. + +*/ + +class Solution { + public String longestWord(String[] words) { + if (words == null || words.length == 0) { + return null; + } + String result = ""; + Arrays.sort(words); + + final Set set = new HashSet<>(); + set.add(result); + + for (String word : words) { + if (set.contains(word.substring(0, word.length() - 1))) { + if (word.length() > result.length()) { + result = word; + } + set.add(word); + } + } + + return result; + } +} + + + + +/* +Same as above +Thoughts: +1. Sort lexicographically +2. Any new word.substring(0, length-1) should appear before itself in the sorted list +3. save the ongoing matched string in set such that it can be used to do set.contains() +4. maintain a longest result. Go through entire words array. + +Note: bacause it's lexicographically sorted, the very first matched word will be the +exact one we want in this length range. The following step becomes: only look for matched +ones in larger length. +*/ + + + +/** +Trie Solution + + */ +class Solution { + class TrieNode { + public boolean isEnd; + public Map children; + public TrieNode() { + this.isEnd = false; + this.children = new HashMap<>(); + } + } + TrieNode root; + + public String longestWord(String[] words) { + if (words == null || words.length == 0) { + return null; + } + + // 1. Longer word is in front. 2. if same length, sort by lexicographically(directory order) + Arrays.sort(words, new Comparator() { + public int compare(String s1, String s2) { + if (s1.length() != s2.length()) { + return s1.length() > s2.length() ? -1 : 1; + } + return s1.compareTo(s2); + } + }); + + root = new TrieNode(); + for (String word : words) { + insert(word); + } + + for (String word: words) { + if (startsWith(word)) { + return word; + } + } + + return null; + } + + /** Inserts a word into the trie. */ + private void insert(String word) { + if (word == null || word.length() == 0) { + return; + } + + TrieNode node = root; + for (int i = 0; i < word.length(); i++) { + char c = word.charAt(i); + if (!node.children.containsKey(c)) { + node.children.put(c, new TrieNode()); + } + node = node.children.get(c); + if (i == word.length() - 1) { + node.isEnd = true; + } + } + } + + // skip the search function + + /** Returns if there is any word in the trie that starts with the given prefix. */ + private boolean startsWith(String prefix) { + if (prefix == null || prefix.length() == 0) { + return false; + } + TrieNode node = root; + for (int i = 0; i < prefix.length(); i++) { + char c = prefix.charAt(i); + if (!node.children.containsKey(c)) { + return false; + } + node = node.children.get(c); + // IMPORTANT: check if the input prefix is word, if not, fail to find word + if (!node.isEnd) { + return false; + } + } + return true; + } +} + + + +/* +竟然是 O(nlogn) + O(nm) + O(nlon), 最差, over complicated +Thoughts: +1. Put arrays in to ArrayList, and Collections.sort(..) by the length +2. Starting from last element && cut off last index && check for exisitance +3. Save result to ArrayList, and maintain the length of succeeded word as breaking point. +4. sort the result by it's lexicographically. +*/ +class Solution { + public String longestWord(String[] words) { + if (words == null || words.length == 0) { + return null; + } + final ArrayList wordList = new ArrayList<>(Arrays.asList(words)); + Collections.sort(wordList, new Comparator(){ + public int compare(String a, String b) { + return a.length() - b.length(); + } + }); + + final ArrayList result = matchWords(wordList); + Collections.sort(result); + + if (result.size() != 0) { + return result.get(0); + } + return null; + } + + private ArrayList matchWords(final ArrayList wordList) { + int maxWordLength = Integer.MIN_VALUE; + final ArrayList result = new ArrayList<>(); + for (int i = wordList.size() - 1; i >= 0; i--) { + String word = wordList.get(i); + // Validate if word can be built by with wordList + while(word.length() != 0) { + if (wordList.contains(word)) { + if (word.length() == 1) { + if (wordList.get(i).length() < maxWordLength) { + return result; + } else { + result.add(wordList.get(i)); + maxWordLength = wordList.get(i).length(); + } + } + word = word.substring(0, word.length() - 1); + } else { + break; + } + } + } + return result; + } +} + +``` \ No newline at end of file diff --git a/Java/Longest Words.java b/Java/Longest Words.java old mode 100644 new mode 100755 index d875ab6..51d33c2 --- a/Java/Longest Words.java +++ b/Java/Longest Words.java @@ -1,3 +1,14 @@ +E +1531457022 +tags: Hash Table, String + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + +``` /* Given a dictionary, find all of the longest words in the dictionary. @@ -36,36 +47,29 @@ the longest words are(is) ["internationalization"]. 1. Use hashmap: > 2. keep track of the longest length -Review: -Map: put, get -ArrayList: add -We can get a value from map, and change directly on it, if that's an object (basically refer to the original object) */ - -class Solution { - /** - * @param dictionary: an array of strings - * @return: an arraylist of strings - */ - ArrayList longestWords(String[] dictionary) { +/* +Thoughts: +1. one pass, save in . +2. Maintain max length +3. return +*/ +public class Solution { + public List longestWords(String[] dictionary) { if (dictionary == null || dictionary.length == 0) { return null; } - HashMap> map = new HashMap>(); - int longestLength = 0; + Map> map = new HashMap<>(); + int max = 0; - for (int i = 0; i < dictionary.length; i++) { - int strLength = dictionary[i].length(); - if (map.containsKey(strLength)) { - map.get(strLength).add(dictionary[i]); - } else { - ArrayList list = new ArrayList(); - list.add(dictionary[i]); - map.put(strLength, list); - } - longestLength = strLength > longestLength ? strLength : longestLength; + for (String word : dictionary) { + int length = word.length(); + map.putIfAbsent(length, new ArrayList<>()); + map.get(length).add(word); + max = Math.max(max, length); } - return map.get(longestLength); + return map.get(max); } -}; +} +``` \ No newline at end of file diff --git a/Java/Lowest Common Ancestor II.java b/Java/Lowest Common Ancestor II.java old mode 100644 new mode 100755 index a497eff..d32b7c1 --- a/Java/Lowest Common Ancestor II.java +++ b/Java/Lowest Common Ancestor II.java @@ -1,7 +1,22 @@ -1. 曾经做的hashset的优化,找到的都存hashset. exist就return那个duplicate +E +1525708351 +tags: Tree, Hash Table + +给一个Binary Tree root, 以及两个node A, B. 特点: node里面存了parent pointer. 找 lowest common ancestor + + +#### Hash Set +- 这个题有个奇葩的地方, 每个node还有一个parent, 所以可以自底向上. +- save visited in hashset. 第一个duplicate, 就是A B 的 lowest common ancestor + +#### Save in lists +- 自底向上。利用parent往root方向返回 +- 把所有parent存下来, 然后在两个list里面找最后一个 common node + +#### 注意 +- 无法从root去直接搜target node 而做成两个list. 因为根本不是Binary Search Tree! -2. 正常做法:2 lists ``` /* Lowest Common Ancestor II @@ -32,6 +47,51 @@ Find the lowest common ancestor(LCA) of the two nodes. LintCode Copyright Binary Tree */ + +/* + Thoughts: + Try to get upper-level parent, store in hashMap. + First time when the node duplicate in map, that will be the first common parent. +*/ + +/** + * Definition of ParentTreeNode: + * + * class ParentTreeNode { + * public ParentTreeNode parent, left, right; + * } + */ +public class Solution { + public ParentTreeNode lowestCommonAncestorII(ParentTreeNode root, + ParentTreeNode A, + ParentTreeNode B) { + if (root == null || (A == null && B == null)) { + return null; + } else if (A == null || B == null) { + return A == null ? B : A; + } + + HashSet set = new HashSet(); + while (A != null || B != null) { + if (A != null) { + if (set.contains(A)) { + return A; + } + set.add(A); + A = A.parent; + } + if (B != null) { + if (set.contains(B)) { + return B; + } + set.add(B); + B = B.parent; + } + } + return root; + } +} + /* Thoughts: 我之前的做法也是蛮彪悍的,HashSet只存所需要的parent, 其实算是一个优化,更节省空间。 @@ -46,11 +106,6 @@ Find the lowest common ancestor(LCA) of the two nodes. */ public class Solution { - /** - * @param root: The root of the tree - * @param A, B: Two node in the tree - * @return: The lowest common ancestor of A and B - */ public ParentTreeNode lowestCommonAncestorII(ParentTreeNode root, ParentTreeNode A,ParentTreeNode B) { if (root == null || (A == null && B == null)) { @@ -86,53 +141,5 @@ public ParentTreeNode lowestCommonAncestorII(ParentTreeNode root, } -/* - Thoughts: - Try to get upper-level parent, store in hashMap. - First time when the node duplicate in map, that will be the first common parent. -*/ - -/** - * Definition of ParentTreeNode: - * - * class ParentTreeNode { - * public ParentTreeNode parent, left, right; - * } - */ -public class Solution { - /** - * @param root: The root of the tree - * @param A, B: Two node in the tree - * @return: The lowest common ancestor of A and B - */ - public ParentTreeNode lowestCommonAncestorII(ParentTreeNode root, - ParentTreeNode A, - ParentTreeNode B) { - if (root == null || (A == null && B == null)) { - return null; - } else if (A == null || B == null) { - return A == null ? B : A; - } - - HashSet set = new HashSet(); - while (A != null || B != null) { - if (A != null) { - if (set.contains(A)) { - return A; - } - set.add(A); - A = A.parent; - } - if (B != null) { - if (set.contains(B)) { - return B; - } - set.add(B); - B = B.parent; - } - } - return root; - } -} ``` \ No newline at end of file diff --git a/Java/Lowest Common Ancestor of a Binary Search Tree.java b/Java/Lowest Common Ancestor of a Binary Search Tree.java new file mode 100755 index 0000000..b45f163 --- /dev/null +++ b/Java/Lowest Common Ancestor of a Binary Search Tree.java @@ -0,0 +1,130 @@ +M +1525708793 +tags: Tree, DFS, BST + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的list +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Brutly寻找p和q的common ancestor, 然后recursively drive left/right +- 非常巧妙, 但是也比较局限; 稍微变条件, 就很难recursive. +- 几种情况: +- 1. one of p, q 在leaf, 那么此时的root其实就是lowest common ancestor +- 2. 如果p, q 在root的左右两边, 这就是分叉口, 那么root就是lowest common ancestor +- 3. 如果p,q 在root的同一边 (左,右), 那么继续dfs +- O(1) extra space, O(logn) time + +``` +/* +Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST. + +According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined +between two nodes v and w as the lowest node in T that has both v and w as descendants +(where we allow a node to be a descendant of itself).” + + _______6______ + / \ + ___2__ ___8__ + / \ / \ + 0 _4 7 9 + / \ + 3 5 +For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. +Another example is LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition. + +Tags: Tree +Similar Problems: (M) Lowest Common Ancestor of a Binary Tree + +*/ + +/* +Thoughts: +Based on the value of p and q, use BST to find the node, and store the visited nodes in two separate lists. +Find last common item in the list to return. +*/ +class Solution { + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || p == null || q == null) { + return root; + } + final List listP = new ArrayList<>(); + final List listQ = new ArrayList<>(); + findNode(root, p, listP); + findNode(root, q, listQ); + int size = listP.size() > listQ.size() ? listQ.size() : listP.size(); + TreeNode parent = root; + for (int i = 0; i < size; i++) { + if (listP.get(i).val == listQ.get(i).val) { + parent = listP.get(i); + } else { + return parent; + } + } + return parent; + } + + private void findNode(TreeNode node, TreeNode target, List list) { + while (node != null) { + list.add(node); + if (node.val == target.val) { + return; + } + if (node.val > target.val) { + node = node.left; + } else { + node = node.right; + } + } + } +} + + +/* +Thoughts: +Besides the method of finding all ancestors, we can look at the problem in a greedy way. +Move both p and q to find the ancestor: +If the root is on the left of both p and q, that means the ancestor must be on right size of root; same applies to the other direction. + +This leads to a compact recursive solution. + +However, the iterative way might be more useful in real development where it utilize data struture +*/ +class Solution { + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || p == null || q == null) { + return root; + } + if (root.val < p.val && root.val < q.val) { + return lowestCommonAncestor(root.right, p, q); + } else if (root.val > p.val && root.val > q.val) { + return lowestCommonAncestor(root.left, p, q); + } + // root is between p and q. + return root; + } +} + +// Less intuitive way: +class Solution { + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || p == null || q == null) { + return root; + } + + // divergent point + if ((root.val >= p.val && root.val <= q.val) || (root.val <= p.val && root.val >= q.val)) { + return root; + } else if (root.val < p.val && root.val < q.val) { + return lowestCommonAncestor(root.right, p, q); + } else if (root.val > p.val && root.val > q.val) { + return lowestCommonAncestor(root.left, p, q); + } + return null; + } +} + +``` \ No newline at end of file diff --git a/Java/Lowest Common Ancestor of a Binary Tree.java b/Java/Lowest Common Ancestor of a Binary Tree.java new file mode 100755 index 0000000..df4b8ce --- /dev/null +++ b/Java/Lowest Common Ancestor of a Binary Tree.java @@ -0,0 +1,205 @@ +M +1525707446 +tags: Tree, DFS + +给一个Binary Tree root, 以及两个node p, q. 找 p 和 q 的 lowest common ancestor + +#### DFS +- 因为是 binary tree, 所以直接盲目搜索搜索path不efficient, use extra space and waste time +- 巧用DFS来找每一个node的common ancestor. +- Need the assumption: 1. unique nodes across tree; 2. must have a solution +- 当root == null或者 p q 任何一个在findLCA底部被找到了(root== A || root == B),那么就return 这个root. +- 三种情况: +- 1. A,B都找到,那么这个level的node就是其中一层的ancestor: 其实,最先recursively return到的那个,就是最底的LCA parent. +- 2. A 或者 B 找到,那就还没有公共parent, return 非null得那个。 +- 3. A B 都null, 那就找错了没有呗, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time/space O(n) + +``` +/** +LeetCode: Lowest Common Ancestor of a Binary Tree + +Given a binary tree, find the lowest common ancestor (LCA) of two given nodes in the tree. + +According to the definition of LCA on Wikipedia: +“The lowest common ancestor is defined between two nodes v and w +as the lowest node in T that has both v and w as descendants +(where we allow a node to be a descendant of itself).” + + _______3______ + / \ + ___5__ ___1__ + / \ / \ + 6 _2 0 8 + / \ + 7 4 +For example, the lowest common ancestor (LCA) of nodes 5 and 1 is 3. + +Another example is LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself +according to the LCA definition. + +*/ + +/* + +Given the root and two nodes in a Binary Tree. +Find the lowest common ancestor(LCA) of the two nodes. + +The lowest common ancestor is the node with largest depth which is the ancestor of both nodes. + +Example + 4 + + / \ + + 3 7 + + / \ + + 5 6 + +For 3 and 5, the LCA is 4. + +For 5 and 6, the LCA is 7. + +For 6 and 7, the LCA is 7. + +Tags Expand +Binary Tree LintCode Copyright + + +*/ + +/* +Thoughts: +Use the 'lowestCommonAncestor' to track the ancestor for at least 1 node. +At any node, if it's a ancestor, the ancestor of left node && ancestor of right node must both exist. +Only return root when p or q is matched; other cases, return null +*/ +class Solution { + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || q == root || p == root) { + return root; + } + TreeNode leftChildAncestor = lowestCommonAncestor(root.left, p, q); + TreeNode rightChildAncestor = lowestCommonAncestor(root.right, p, q); + + if (leftChildAncestor != null && rightChildAncestor != null) { + return root; + } else if (leftChildAncestor != null || rightChildAncestor != null) { // ancestor can be itself + return leftChildAncestor != null ? leftChildAncestor : rightChildAncestor; + } + return null; + } +} + +/* + Thoughts: + Revisit this on 12.11.2015. + To correctly understand this approach when there is not 'parent' atribute available in node. + + We divide and coquer (in this case DFS) into 2 branches, and we are actually asking each node to check: + Do I have a leaf child of nodeA (could be futher down in the tree)? + Do I have a leaf child of nodeB (could be futher down in the tree)? + 1. If I have leaf child of A && B, then i'm the deepest parent in line! Return. + 2. If I only have A, or B: mark myself as an ancestor of A or B. + 3. If I don't have leaf child of A nor B, I'm not an ancestor, failed, return null. + + After the common ancestor is found at any deep level, and returned itself to parent level, + we can assume other branches must be null (because they are not ancestor, since we are), + then the this common ancestor node will be passed to highest level. + + However, with one problem: + When review the problem, calling the recursive functions of the 'lowestCommonAncestor' is just + confusing. It's not easy to see the relationship between leef child and ancestor candidates. + + +*/ +public class Solution { + /** + * @param root: The root of the binary search tree. + * @param A and B: two nodes in a Binary. + * @return: Return the least common ancestor(LCA) of the two nodes. + */ + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode A, TreeNode B) { + if (root == null || root == A || root == B) { + return root; + } + TreeNode left = lowestCommonAncestor(root.left, A, B); + TreeNode right = lowestCommonAncestor(root.right, A, B); + + if (left != null && right != null) {//Found both A leaf and B leaf + return root; + } else if (left != null || right != null) { + return left != null ? left : right; + } else { + return null; + } + } +} + +/* +//Another way using regular way: see solution: +This is for the case that each node can reach its higher level parent.... +Basically put all parents into a list, +[root, next level, next level... parent, nodeA] +[root, next level, next level... parent, nodeB] +compare these 2 lists see when to have a different node. that node.parent is the parent + +Or, if the list is identical, then their parent might just be same right-above-level parent. + +http://www.ninechapter.com/solutions/lowest-common-ancestor/ +*/ + + +/* +Older same version +Think process: +Divide and conquer: +start from root, divide into 2 ways … +At the bottom, if it’s node1 or node2, send back. +1. If any line that’s not having node1 or node2 as leaf, they will become null. +2. At the ancestor spot: because node1!=null and node2!=null, the ancestor return itself. +3. From this point, any level higher, it will return the ancestor because the other child-line has been null at the time. +When it returns to the top, return solution : ancestor + +*/ + +public class Solution { + /** + * @param root: The root of the binary search tree. + * @param A and B: two nodes in a Binary. + * @return: Return the least common ancestor(LCA) of the two nodes. + */ + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode A, TreeNode B) { + if (root == null || root == A || root == B) { + return root; + } + TreeNode left = lowestCommonAncestor(root.left, A, B); + TreeNode right = lowestCommonAncestor(root.right, A, B); + + if (left == null && right == null) { + return null; + } else if (left == null) { + return right; + } else if (right == null) { + return left; + } else { + return root; + } + } +} + + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ +``` diff --git a/Java/Majority Element II.java b/Java/Majority Element II.java new file mode 100644 index 0000000..4f295fe --- /dev/null +++ b/Java/Majority Element II.java @@ -0,0 +1,111 @@ +M +1528045137 +tags: Array + +#### Sort + count +- O(nlogN) + +#### Two counters +- O(n), count and track valueA, valueB +- count overall apperance at the end for the two items +- save to result +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + +``` + +/* +LeetCode +Given an integer array of size n, find all elements that appear more than ⌊ n/3 ⌋ times. + +Note: The algorithm should run in linear time and in O(1) space. + +Example 1: + +Input: [3,2,3] +Output: [3] +Example 2: + +Input: [1,1,1,3,3,2,2,2] +Output: [1,2] + +Note: different from LintCode Majority Number(Element) II, where it returns integer. +*/ + +class Solution { + public List majorityElement(int[] nums) { + List result = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return result; + } else if (nums.length == 1) { + result.add(nums[0]); + return result; + } + Arrays.sort(nums); + int n = nums.length; + int count = 0; + int prev = nums[0]; + // count + for (int i = 0; i < n; i++) { + count += nums[i] == prev ? 1 : 0; + addResult(result, n, prev, count); + // Reset prev and count if different + if (nums[i] != prev) { + prev = nums[i]; + count = 1; + } + } + addResult(result, n, prev, count); + return result; + } + + private void addResult(List result, int n, int value, int count) { + if (!result.contains(value) && count > n / 3) { + result.add(value); + } + } +} + + +class Solution { + public List majorityElement(int[] nums) { + List result = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return result; + } + int valA = 0; + int valB = 0; + int countA = 0; + int countB = 0; + for (int num : nums) { + if (num == valA) { + countA++; + } else if (num == valB) { + countB++; + } else if (countA == 0) { + valA = num; + countA++; + } else if (countB == 0){ + valB = num; + countB++; + } else {//None of a || b matches + countA--; + countB--; + } + }//For + + countA = 0; + countB = 0; + for (int num : nums) { + if (num == valA) { + countA++; + } else if (num == valB) { + countB++; + } + } + if (countA > nums.length / 3) result.add(valA); + if (countB > nums.length / 3) result.add(valB); + return result; + } +} + +``` \ No newline at end of file diff --git a/Java/Majority Element.java b/Java/Majority Element.java new file mode 100755 index 0000000..20e475c --- /dev/null +++ b/Java/Majority Element.java @@ -0,0 +1,133 @@ +E +1519318460 +tags: Array, Divide and Conquer, Bit Manipulation + +#### Vote 计数 +- vote++, vote--到最后剩下的就是winner. Time O(n), Space O(1) +- Majority Number是指超半数. 超半数的数字, 最后都会至少有vote>=1: match current majority number,vote++;if not, vote--. +- 注意:assume valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 + +#### HashMap count occurance +- Time, Space: O(n) + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + +``` +/* +LeetCode: Majority Element +Given an array of size n, find the majority element. The majority element is the element that appears more than ⌊ n/2 ⌋ times. + +You may assume that the array is non-empty and the majority element always exist in the array. + */ + +/* +Thoughts: +Vote each element and record majorityNumber. +Whenever not matching majorityNumber, vote--. +Whoever holds the majorityNumber in the end, will be the majority number. + +Reverse thinking: +Imagine the majority number does exist, and has n/2 + 1 occurances. +If let all numbers vote++ or vote--, in the end the vote will equal to 1. +*/ +class Solution { + public int majorityElement(int[] nums) { + if (nums == null || nums.length == 0) { + return -1; + } + int majorityNum = nums[0]; + int vote = 0; + for (int num : nums) { + vote += num == majorityNum ? 1 : -1; + if (vote == 0) { + majorityNum = num; + vote = 1; + } + } + return majorityNum; + } +} + +/* +Thoughts: +Save the element into hashmap and count. O(n) space, O(n) time +*/ +class Solution { + public int majorityElement(int[] nums) { + if (nums == null || nums.length == 0) { + return -1; + } + Map map = new HashMap<>(); + for (int num : nums) { + if (!map.containsKey(num)) { + map.put(num, 0); + } + map.put(num, map.get(num) + 1); + } + + int halfLen = nums.length / 2; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() > halfLen) { + return entry.getKey(); + } + } + return -1; + } +} + + +/* +Given an array of integers, the majority number is the number that occurs more than half of the size of the array. Find it. + +Example +For [1, 1, 1, 1, 2, 2, 2], return 1 + +Challenge +O(n) time and O(1) space + +Tag: Enumeration + + +*/ + +/* +Thinking process: +Natural thinking process: count how many you have for 1st element, if next one is the same, count++, if next one is not the same, count- -. +When count ==0, that means other types of element has same amount as the 1st majority number, they are even. +From this point, count the value at current position as the majority number, keep the loop rolling. +Note: this solutions works only when the given array has a valid solution. +CounterCase:[111223], with actually return 3 as the majority number. But again, this is not a valid input in this case. +*/ +public class Solution { + /** + * @param nums: a list of integers + * @return: find a majority number + */ + public int majorityNumber(ArrayList nums) { + if (nums == null || nums.size() == 0) { + return -1; + } + int majorNum = nums.get(0); + int count = 1; + for (int i = 1; i < nums.size(); i++) { + if (majorNum == nums.get(i)) { + count++; + } else { + count--; + } + if (count == 0) { + majorNum = nums.get(i); + count = 1; + } + } + return majorNum; + } +} + + +``` \ No newline at end of file diff --git a/Java/Majority Number II.java b/Java/Majority Number II.java old mode 100644 new mode 100755 index 91b1d96..8ffe840 --- a/Java/Majority Number II.java +++ b/Java/Majority Number II.java @@ -1,34 +1,39 @@ +M +tags:Greedy, Enumeration + +#### Array +- 分三份:a b c考虑 +- 若a: countA++; 或b: countB++ +- 或c:countA--, countB-- +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 +- 最后出现的两个count>0的a和b,自然是potentially大于1/3的。其中有一个大于1/3. +- 比较countA和countB哪个大,就return哪一个。 + +``` + /* -Given an array of integers, the majority number is the number that occurs more than 1/3 of the size of the array. +LintCode: +Given an array of integers, the majority number is the number that +occurs more than 1/3 of the size of the array. Find it. -Note -There is only one majority number in the array - Example -For [1, 2, 1, 2, 1, 3, 3] return 1 +Given [1, 2, 1, 2, 1, 3, 3], return 1. Challenge -O(n) time and O(1) space +O(n) time and O(1) extra space. + + */ +/* Thinking process: Need to think the relations of 3 parts of the array: -1. Assume a > 1/3, which is the candidate were are looking for - However, only konwing a appears more than 1/3 of the array, does not mean there is no other element appears more than 1/3, for example, aaaaabcccccc, a = 5/12, b = 6/12. The majority is b. -2. Consider another element b, which is a different element rather than a. Discuss the 2 conditions of b. -3. Consider the rest of the array is in set c, which can contain all different elements. +1. Consider a and b. +2. Consider the rest of the array is in set c, which can contain all different elements. +3. Two if statement makes sure a and b fall into 1/3 of the conditions. Discuss relations between a, b, c -Assume a > 1/3 -Case1: b < 1/3 - given: a > 1/3, means b + c < 2/3, known b < 1/3 - get: c < 1/3 - conclusion: a is the majority -Case2: b > 1/3 - given: a + b ? 2/3 - get: c < 1/3 - conclusion: return the greater element# of a or b Implementation: 1. Have valA and valB two pointers to represent a and between @@ -37,13 +42,13 @@ 4. Note: at each index i, only one of valA or valB is checked. That means, we evaluate a and b individually against the section c. 5. At the end, we found 2 candidates: a and b. Now compare the # of a and b to see which is greater. */ - +// Lintcode public class Solution { - /** - * @param nums: A list of integers + /* + * @param nums: a list of integers * @return: The majority number that occurs more than 1/3 */ - public int majorityNumber(ArrayList nums) { + public int majorityNumber(List nums) { if (nums == null || nums.size() == 0) { return -1; } @@ -51,33 +56,34 @@ public int majorityNumber(ArrayList nums) { int valB = 0; int countA = 0; int countB = 0; - for (int i = 0; i < nums.size(); i++) { - if (countA == 0 || nums.get(i) == valA) { - valA = nums.get(i); + for (int num : nums) { + if (num == valA) { + countA++; + } else if (num == valB) { + countB++; + } else if (countA == 0) { + valA = num; countA++; - } else if (countB == 0 || nums.get(i) == valB) { - valB = nums.get(i); + } else if (countB == 0){ + valB = num; countB++; } else {//None of a || b matches countA--; countB--; - if (countA == 0) { - countA = 1; - valA = nums.get(i); - } else if (countB == 0) { - countB = 1; - valB = nums.get(i); - } } }//For countA = 0; countB = 0; for (int num : nums) { - countA += num == valA ? 1 : 0; - countB += num == valB ? 1 : 0; + if (num == valA) { + countA++; + } else if (num == valB) { + countB++; + } } return countA > countB ? valA : valB; } } +``` \ No newline at end of file diff --git a/Java/Majority Number III.java b/Java/Majority Number III.java old mode 100644 new mode 100755 index 70f58d0..152904f --- a/Java/Majority Number III.java +++ b/Java/Majority Number III.java @@ -1,5 +1,22 @@ +M +tags: Linked List, Hash Table + +TODO: +1. hash table solution not passing +2. Find O(n) solution + +#### Hash Table +- 与其他Majority Number一样。 +- 出现次数多余1/k,就要分成k份count occurance.用HashMap。 存在的+1;不存在map里的,分情况: +- 若map.size() == k,说明candidate都满了,要在map里把所有现存的都-1; +- 若map.size() < k, 说明该加新candidate,那么map.put(xxx, 1); +- 最后在HashMap里找出所留下的occurance最大的那个数。 +- 但这样的worst case是 O(nk) + +``` /* -Given an array of integers and a number k, the majority number is the number that occurs more than 1/k of the size of the array. Find it. +Given an array of integers and a number k, the majority number is the number +that occurs more than 1/k of the size of the array. Find it. Note There is only one majority number in the array. @@ -10,6 +27,9 @@ Challenge O(n) time and O(k) extra space +*/ + +/* Thinking process: Very similar to Majority I, II, except we can use a HashMap to store information (value, count). Having a HashMap we have one advantage: when checking if current value matches any previously recorded val, just do a map.containsKey(val). @@ -17,15 +37,9 @@ Very similar to Majority I, II, except we can use a HashMap to store information Note, to learn how to use iterator in HashMap. Note: when map.containsKey(currVal) == false, the code checks map.size() == k before count-- perations. This is because: We first need to put k candidates into HashMap before we count-- from all of them. If map.size() < k, that means we still have free spot for candidate in the HashMap, so in this case we do: map.put(candidateKey, 1). -*/ - +*/ public class Solution { - /** - * @param nums: A list of integers - * @param k: As described - * @return: The majority number - */ public int majorityNumber(ArrayList nums, int k) { if (nums == null || nums.size() == 0) { return -1; @@ -35,7 +49,7 @@ public int majorityNumber(ArrayList nums, int k) { if (map.containsKey(num)) {//Found duplicates, count++ map.put(num, map.get(num) + 1); } else { - if (map.size() == k) {//All candidates added, do count-- + if (map.size() == k) {//Enough candidates added, do count-- Iterator> iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = iter.next(); @@ -63,3 +77,5 @@ public int majorityNumber(ArrayList nums, int k) { } } + +``` \ No newline at end of file diff --git a/Java/Matrix Zigzag Traversal.java b/Java/Matrix Zigzag Traversal.java old mode 100644 new mode 100755 index 629772f..8a3f6ce --- a/Java/Matrix Zigzag Traversal.java +++ b/Java/Matrix Zigzag Traversal.java @@ -1,5 +1,8 @@ -分析4个step. -小心走位。 +E + +分析4个step:right, left-bottom,down,right-up +implement时注意index.有点耐心 + ``` /* Matrix Zigzag Traversal diff --git a/Java/Max Area of Island.java b/Java/Max Area of Island.java new file mode 100644 index 0000000..1a64810 --- /dev/null +++ b/Java/Max Area of Island.java @@ -0,0 +1,88 @@ +E +1528956495 +tags: DFS, Array + +#### DFS +- 虽然Easy, 但用到DFS最基本的想法. +- 1. dive deep +- 2. mark VISITED +- 3. sum it up +- Time: worst O(mn), traverse all possible nodes + +- 更要注意, 要从符合条件value==1的地方开始dfs. +- 对于什么island都没有的情况,area应该位0, 而不是Integer.MIN_VALUE, 问清楚考官那小伙, 别写顺手。 + +``` +/* +Given a non-empty 2D array grid of 0's and 1's, an island is a group of 1's +(representing land) connected 4-directionally (horizontal or vertical.) +You may assume all four edges of the grid are surrounded by water. + +Find the maximum area of an island in the given 2D array. (If there is no island, the maximum area is 0.) + +Example 1: +[[0,0,1,0,0,0,0,1,0,0,0,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,1,1,0,1,0,0,0,0,0,0,0,0], + [0,1,0,0,1,1,0,0,1,0,1,0,0], + [0,1,0,0,1,1,0,0,1,1,1,0,0], + [0,0,0,0,0,0,0,0,0,0,1,0,0], + [0,0,0,0,0,0,0,1,1,1,0,0,0], + [0,0,0,0,0,0,0,1,1,0,0,0,0]] +Given the above grid, return 6. Note the answer is not 11, because the island must be connected 4-directionally. +Example 2: +[[0,0,0,0,0,0,0,0]] +Given the above grid, return 0. +Note: The length of each dimension in the given grid does not exceed 50. +*/ + +/* +Thoughts: + +DFS to all directions: +dx = {0, 1, 0, -1} +dy = {1, 0, -1, 0} +1. Each DFS deep dive returns the result area +2. Compare result with max area +Note: when island is found and counted into area, it needs to be flipped to other digits just to avoid revisiting. +*/ + +class Solution { + private int[] dx = {0, 1, 0, -1}; + private int[] dy = {1, 0, -1, 0}; + private int VISITED = -1; + + public int maxAreaOfIsland(int[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int maxArea = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == 1) { + maxArea = Math.max(maxArea, dfs(grid, i, j)); + } + } + } + return maxArea; + } + + private int dfs(int[][] grid, int x, int y) { + int currentLevelSum = 0; + if (validate(grid, x, y)) { + grid[x][y] = VISITED; + currentLevelSum++; + for (int i = 0; i < dx.length; i++) { + currentLevelSum += dfs(grid, x + dx[i], y + dy[i]); + } + } + return currentLevelSum; + } + + private boolean validate(int[][] grid, int x, int y) { + return x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && grid[x][y] == 1; + } +} + + +``` \ No newline at end of file diff --git a/Java/Max Consecutive Ones.java b/Java/Max Consecutive Ones.java new file mode 100644 index 0000000..4b71c32 --- /dev/null +++ b/Java/Max Consecutive Ones.java @@ -0,0 +1,48 @@ +E +1517548857 +tags: Array + +Basic. Math.max track结果。 +记得在有对外操作的loop后,一定要把result object清理干净。 + +``` +/* +Given a binary array, find the maximum number of consecutive 1s in this array. + +Example 1: +Input: [1,1,0,1,1,1] +Output: 3 +Explanation: The first two digits or the last three digits are consecutive 1s. + The maximum number of consecutive 1s is 3. +Note: + +The input array will only contain 0 and 1. +The length of input array is a positive integer and will not exceed 10,000 +*/ + +/* +Thoughts: +Maintain a min outsize, loop through all numbers. + +*/ + +class Solution { + public int findMaxConsecutiveOnes(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int maxCount = 0; + int count = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] == 0) { + maxCount = Math.max(count, maxCount); + count = 0; + } else { + count++; + } + } + return Math.max(count, maxCount); + } +} + +``` \ No newline at end of file diff --git a/Java/Max Points on a Line.java b/Java/Max Points on a Line.java new file mode 100644 index 0000000..fae6c91 --- /dev/null +++ b/Java/Max Points on a Line.java @@ -0,0 +1,121 @@ +H +1529471330 +tags: Array, Hash Table, Geometry, Math + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + +``` + +/* +Given n points on a 2D plane, find the maximum number of points that lie on the same straight line. + +Example 1: + +Input: [[1,1],[2,2],[3,3]] +Output: 3 +Explanation: +^ +| +| o +| o +| o ++-------------> +0 1 2 3 4 +Example 2: + +Input: [[1,1],[3,2],[5,3],[4,1],[2,3],[1,4]] +Output: 4 +Explanation: +^ +| +| o +| o o +| o +| o o ++-------------------> +0 1 2 3 4 5 6 + +*/ +/** + * Definition for a point. + * class Point { + * int x; + * int y; + * Point() { x = 0; y = 0; } + * Point(int a, int b) { x = a; y = b; } + * } + */ +class Solution { + public int maxPoints(Point[] points) { + if (points == null || points.length == 0) return 0; + int result = 0; + Map map = new HashMap<>(); + // double for loop to try all points. O(n^2) + for (int i = 0; i < points.length; i++) { + int max = 0, overlap = 0; + map.clear(); + for (int j = i + 1; j < points.length; j++) { + int x = points[j].x - points[i].x; + int y = points[j].y - points[i].y; + if (x == 0 && y == 0) { + overlap++; + continue; + } + int gcd = findGCD(x, y); + if (gcd != 0) { + x /= gcd; + y /= gcd; + } + String key = x + "@" + y; + if (map.containsKey(key)) { + map.put(key, map.get(key) + 1); + } else { + map.put(key, 1); + } + max = Math.max(max, map.get(key)); + } + result = Math.max(result, max + overlap + 1); + } + return result; + } + + private int findGCD(int a, int b) { + if (b == 0) return a; + return findGCD(b, a % b); + } +} + + + + +/* +DP not applicable: +IF it's a N x N board, with max value restricted by value n x n, dp approach is feasible, because the board is bounded. +However, this question does not specify the board, but rather a list of points, where the value can be extremely large (not feasible to define dp[n][n]) + +Thoughts about DP +- Build N x N boolean[][], and assign points +- dp[x][y]: max number of points to this point +- there can be 8 directions towards 1 (x,y), and half of direction overlaps +- Assume the 4 directions: right-diagonal, DOWN, left-diagonal, right +- add status to dp[x][y][directionStatus]: on spot (x,y), what's the max # points coming from this direction? +- function: dp[x][y][status] = board[x][y] ? dp[lastX][lastY][status] + 1 : 0; +where lastX = x - dx[status]; +- init: +dp[0][y][any status] = board[0][y] ? 1 : 0; +dp[x][0][any status] = board[x][0] ? 1 : 0; +*/ + +``` \ No newline at end of file diff --git a/Java/Max Sum of Rectangle No Larger Than K.java b/Java/Max Sum of Rectangle No Larger Than K.java new file mode 100644 index 0000000..0b8c550 --- /dev/null +++ b/Java/Max Sum of Rectangle No Larger Than K.java @@ -0,0 +1,122 @@ +H +1528567587 +tags: Binary Search, BST, TreeSet, DP, Queue, Array + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + +``` +/* +Given a non-empty 2D matrix matrix and an integer k, +find the max sum of a rectangle in the matrix such that its sum is no larger than k. + +Example: +Given matrix = [ + [1, 0, 1], + [0, -2, 3] +] +k = 2 +The answer is 2. Because the sum of rectangle [[0, 1], [-2, 3]] is 2 and 2 +is the max number no larger than k (k = 2). + +Note: +The rectangle inside the matrix must have an area > 0. +What if the number of rows is much larger than the number of columns? + +thanks to: http://bookshadow.com/weblog/2016/06/22/leetcode-max-sum-of-sub-matrix-no-larger-than-k/ +*/ + +/* +Thoughts: +Start from (0,0), move -> right, down, circle rectangle and compare area. +- start row shifts from [0 ~ m - 1], since rectangle may start from any row +- start column can shift from [0 ~ n - 1], since rectangle may start from any col. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: when it happens, just need to find a new starting row or col, where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +*/ + +class Solution { + public int maxSumSubmatrix(int[][] matrix, int k) { + // check input + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { + return -1; + } + // since O(row^2), make sure m represents the smaller length + int m = Math.min(matrix.length, matrix[0].length); + int n = Math.max(matrix.length, matrix[0].length); + boolean rowFirst = matrix.length < matrix[0].length; + int max = Integer.MIN_VALUE; + // 3 loop layers: + // 1. int[] preSum; 2. TreeSet, store all area for curr iteration, 3. newSum in new row + for (int row = 0; row < m; row++) { + int[] preSum = new int[n]; + for (int i = row; i < m; i++) { // iteration + TreeSet areaSet = new TreeSet<>(); + int newSum = 0; // adding up with preSum[j] + for (int j = 0; j < n; j++) { + preSum[j] += rowFirst? matrix[i][j] : matrix[j][i]; + newSum += preSum[j]; + if (newSum <= k) max = Math.max(max, newSum); + Integer extraArea = areaSet.ceiling(newSum - k); + if (extraArea != null) { + max = Math.max(max, newSum - extraArea); + } + areaSet.add(newSum); + } + } + } + return max == Integer.MIN_VALUE ? -1 : max; + } +} + + +// Simpler, without considering iterating m^2 on the smaller side +class Solution { + public int maxSumSubmatrix(int[][] matrix, int k) { + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { + return -1; + } + int m = matrix.length, n = matrix[0].length; + int max = Integer.MIN_VALUE; + // 3 loop layers: + // 1. int[] preSum; 2. TreeSet, store all area for curr iteration, 3. newSum in new row + for (int row = 0; row < m; row++) { + int[] preSum = new int[n]; + for (int i = row; i < m; i++) { // iteration + TreeSet areaSet = new TreeSet<>(); + int newSum = 0; // adding up with preSum[j] + for (int j = 0; j < n; j++) { + preSum[j] += matrix[i][j]; + newSum += preSum[j]; + if (newSum <= k) max = Math.max(max, newSum); + Integer extraArea = areaSet.ceiling(newSum - k); + if (extraArea != null) { + max = Math.max(max, newSum - extraArea); + } + areaSet.add(newSum); + } + } + } + return max == Integer.MIN_VALUE ? -1 : max; + } +} + +``` \ No newline at end of file diff --git a/Java/Maximal Rectangle.java b/Java/Maximal Rectangle.java new file mode 100644 index 0000000..72b82b3 --- /dev/null +++ b/Java/Maximal Rectangle.java @@ -0,0 +1,83 @@ +H +1521321013 +tags: Array, Hash Table, DP, Stack + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + +``` +/* +Given a 2D binary matrix filled with 0's and 1's, find the largest rectangle containing only 1's and return its area. + +For example, given the following matrix: + +1 0 1 0 0 +1 0 1 1 1 +1 1 1 1 1 +1 0 0 1 0 +Return 6. +*/ + + +/* +Thoughts: +Different form of 'Largest Rectangle in Histogram': +break the problem down then we are dealing with m list of histogram. +Each row (from top to bottom), can be modeled as histogram: sum 1's from top to the current row. + +After that point, just calculate largest rectangle in each modelded histogram row, using stack. +*/ +class Solution { + public int maximalRectangle(char[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { + return 0; + } + int m = matrix.length; + int n = matrix[0].length; + int[][] heightMap = new int[m][n]; + + // Prepare height map + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (matrix[i][j] == '0') { + heightMap[i][j] = 0; + } else { + heightMap[i][j] = i == 0 ? 1 : heightMap[i - 1][j] + 1; + } + } + } + + int maxArea = 0; + for (int i = 0; i < m; i++) { + maxArea = Math.max(maxArea, findLargestRectInHistogram(heightMap[i])); + } + + return maxArea; + } + + private int findLargestRectInHistogram(int[] height) { + Stack stack = new Stack<>(); + int m = height.length; + int max = 0; + for (int i = 0; i <= m; i++) { + int currHeight = i == m ? -1 : height[i]; + while (!stack.isEmpty() && currHeight <= height[stack.peek()]) { + int peekHeight = height[stack.pop()]; + int width = stack.isEmpty() ? i : i - stack.peek() - 1; + max = Math.max(max, peekHeight * width); + } + stack.push(i); + } + return max; + } +} +``` \ No newline at end of file diff --git a/Java/Maximal Square.java b/Java/Maximal Square.java old mode 100644 new mode 100755 index c3048fa..2248562 --- a/Java/Maximal Square.java +++ b/Java/Maximal Square.java @@ -1,8 +1,25 @@ -从边长为2的正方形看起,看左上角的那个点。 -如何确定是个正方形?首先看左上点是不是1,然后看右边,右下,下面的点是不是1. -DP就是根据这个特征想出来。dp[i,j]代表从右下推上来,包括当前点的,所积累的最长边。 -注意dp[i,j]被右,右下,下三点的最短点所限制。这就是fn. +M +1521323988 +tags: DP, Coordinate DP + +只能往右边,下面走, 找面积最大的 square. 也就是找到变最长的 square. + +#### DP +- 正方形, 需要每条边都是一样长度. +- 以右下角为考虑点, 必须满足条件: left/up/diagonal的点都是1 +- 并且, 如果三个点分别都衍生向三个方向, 那么最长的 square 边就是他们之中的最短边 (受最短边限制) +- dp[i][j]: max square length when reached at (i, j), from the 3 possible directions +- dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; +- Space, time O(mn) + +##### init +每个点都可能是边长1, 如果 matrix[i][j] == '1' + +##### 滚动数组 +[i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + ``` + /* Given a 2D binary matrix filled with 0's and 1's, find the largest square containing all 1's and return its area. @@ -20,16 +37,94 @@ Dynamic Programming */ +/* +Thoughts: +Square: each border has same length. +Matrix && square problem: consider right-bottom corner. +Let dp[i][j] be the max border length, limited by points up/left/diagnal +currLength = Math.min(up, left, diagnal) + 1. + +init: i = 0 row, has length = 1; j = 0 col, has length = 1. +*/ +class Solution { + public int maximalSquare(char[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { + return 0; + } + int m = matrix.length; + int n = matrix[0].length; + int[][] dp = new int[m][n]; + int maxLen = 0; + // Init: + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = matrix[i][j] == '1' ? 1 : 0; + maxLen = Math.max(dp[i][j], maxLen); + } + } + + // calculation + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + if (matrix[i][j] == '1') { + dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; + maxLen = Math.max(dp[i][j], maxLen); + } + } + } + return maxLen * maxLen; + } +} + + +/* +Thoughts: +[i] and [i - 1] can be used to reduce space with rolling array +*/ +class Solution { + public int maximalSquare(char[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { + return 0; + } + int m = matrix.length; + int n = matrix[0].length; + int[][] dp = new int[2][n]; + int maxLen = 0; + int curr = 0; + int prev = 0; + + // calculation + for (int i = 0; i < m; i++) { + curr = prev; + prev = 1 - curr; + for (int j = 0; j < n; j++) { + if (i == 0 || j == 0) { + dp[curr][j] = matrix[i][j] == '1' ? 1 : 0; + maxLen = Math.max(dp[curr][j], maxLen); + continue; + } + if (matrix[i][j] == '1') { + dp[curr][j] = Math.min(Math.min(dp[prev][j], dp[curr][j - 1]), dp[prev][j - 1]) + 1; + } else { + dp[curr][j] = 0; + } + maxLen = Math.max(dp[curr][j], maxLen); + } + } + return maxLen * maxLen; + } +} + /* Thoughts: Seem that we need to check on right and bottom spots for 2x2 1's. If all size spots are clean, len++, square = newLen ^ 2. DP[i,j]: the longest square lengh that this matrix[i][j] reach. - dp[i,j] = Math.min(dp[i][j+1], dp[i+1][j], dp[i][j]) + dp[i,j] = Math.min(dp[i][j+1], dp[i+1][j], dp[i+1][j+1]) init: dp[n-1][0 ~ m-1] = matrix[n-1][0 ~ m-1] == 0 ? 0 : 1; dp[0 ~ n-1][m-1] = matrix[0 ~ n-1][m-1] == 0 ? 0 : 1; for from rigt-bottom conor. - Maintain a max are + Maintain a max area */ public class Solution { diff --git a/Java/Maximize Distance to Closest Person.java b/Java/Maximize Distance to Closest Person.java new file mode 100644 index 0000000..ae4ded7 --- /dev/null +++ b/Java/Maximize Distance to Closest Person.java @@ -0,0 +1,75 @@ +E +1531627559 +tags: Array +time: O(n) +space: O(1) + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, track start/end +- start/end point, 然后比较大小记录dist +- 注意1: 如果第一个座位没有人, 特殊处理, dist = [0 ~ end] +- 注意2: 如果最后一个座位没有人, 特殊处理: dist = [n - 1 - start]; +- 其余: `dist = Math.max(dist, (end - start) / 2)` +- 相关题目: 几乎同样概念 `Binary Gap`, 升级复杂版`Exam Room` + + +``` +/* +In a row of seats, 1 represents a person sitting in that seat, +and 0 represents that the seat is empty. + +There is at least one empty seat, and at least one person sitting. + +Alex wants to sit in the seat such that the distance between him +and the closest person to him is maximized. + +Return that maximum distance to closest person. + +Example 1: + +Input: [1,0,0,0,1,0,1] +Output: 2 +Explanation: +If Alex sits in the second open seat (seats[2]), then the closest person has distance 2. +If Alex sits in any other open seat, the closest person has distance 1. +Thus, the maximum distance to the closest person is 2. +Example 2: + +Input: [1,0,0,0] +Output: 3 +Explanation: +If Alex sits in the last seat, the closest person is 3 seats away. +This is the maximum distance possible, so the answer is 3. +Note: + +1 <= seats.length <= 20000 +seats contains only 0s or 1s, at least one 0, and at least one 1. + + */ + +// similar to Binary gap: find largest gap and sit in the middle +class Solution { + public int maxDistToClosest(int[] seats) { + if (seats == null || seats.length == 0) return -1; + + int n = seats.length, dist = 0; + int start = seats[0] == 1 ? 0 : -1; // check start bound: -1, imaginary boundary + + for (int end = 1; end < n; end++) { + if (seats[end] == 1) { + if (start == -1) dist = end; + else dist = Math.max(dist, (end - start) / 2); + start = end; + } + } + + // end bound + if (seats[n - 1] == 0) dist = Math.max(dist, n - 1 - start); + + return dist; + } +} +``` \ No newline at end of file diff --git a/Java/Maximum Average Subarray I.java b/Java/Maximum Average Subarray I.java new file mode 100644 index 0000000..d13a394 --- /dev/null +++ b/Java/Maximum Average Subarray I.java @@ -0,0 +1,52 @@ +E +1517552169 +tags: Array, Subarray +time: O(n) +space: O(1) + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + +``` +/* +Given an array consisting of n integers, find the contiguous subarray of given length k +that has the maximum average value. And you need to output the maximum average value. + +Example 1: +Input: [1,12,-5,-6,50,3], k = 4 +Output: 12.75 +Explanation: Maximum average is (12-5-6+50)/4 = 51/4 = 12.75 +Note: +1 <= k <= n <= 30,000. +Elements of the given array will be in the range [-10,000, 10,000]. +*/ + +/* +Thoughts: +Given a k as range, find the max sum. +Brutle: use the box, calculate sum one index at a time, and find the max. +No need to add 4 numbers all the time, just minus the leading one and add the new one. + +Note: use double to catch the sum. +*/ +class Solution { + public double findMaxAverage(int[] nums, int k) { + if (nums == null || nums.length == 0) { + return 0; + } + double sum = 0; + // init the sum of k items + for (int i = 0; i < k; i++) { + sum += nums[i]; + } + + // calculate + double max = sum; + for (int i = k; i < nums.length; i++) { + max = Math.max(sum, max); + sum = sum - nums[i - k] + nums[i]; + } + max = Math.max(sum, max); + return max / k; + } +} +``` \ No newline at end of file diff --git a/Java/Maximum Average Subarray II.java b/Java/Maximum Average Subarray II.java new file mode 100644 index 0000000..88144ac --- /dev/null +++ b/Java/Maximum Average Subarray II.java @@ -0,0 +1,112 @@ +R +1521096472 +tags: Array, Binary Search, PreSum + +给int[] nums 和 window min size k. window size可以大于K. 找最大的连续数列average value. + +- Binary Search的思想, 用在所要找的这个 average sum 上面. 大小是在[min, max]之中 +- 找k的时候, 是>=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + +``` + +/* +Given an array consisting of n integers, find the contiguous subarray +whose length is greater than or equal to k that has the maximum average value. +And you need to output the maximum average value. + +Example 1: +Input: [1,12,-5,-6,50,3], k = 4 +Output: 12.75 +Explanation: +when length is 5, maximum average value is 10.8, +when length is 6, maximum average value is 9.16667. +Thus return 12.75. +Note: +1 <= k <= n <= 10,000. +Elements of the given array will be in range [-10,000, 10,000]. +The answer with the calculation error less than 10-5 will be accepted. + +*/ + +/* +Thoughts: +If trying brutly, it'll be O(n^2). How about O(nLogN)? + +Try binary serach on the final result: the average number, which will range from [min, max] of nums. +Should recognize that when seeing '1e-15' +http://bookshadow.com/weblog/2017/07/16/leetcode-maximum-average-subarray-ii/ + +Key idea: +0. start=mid, end=max. +1. Give the midAvg[min, max] as the arbitrary avg, see if we can find possible k [k ~ n], that gives average sum >= midAvg +2. If it does exist, maybe we can find larger midAvg? so set start=midAvg +3. If not exist, we should set end=midAvg + +Problem: +How to effectively check we can beat given midAvg? Simply sum up k items => sum, then check sum/k >= midAvg. +Well, a bit more to it: +1. If using nums[0, k], it's easy: sum[0, k] / k will be it. +2. If using [x, i], where i - x = k. The sum will be sum[0, i] - preSum[0, x]. + Here is the core triky part: we don't have to try O(n^2) here to check all possible k in [k ~ n] + Rather, we record preSum as the minimal value of all possible preSum as k moves. + `sum[x, i] = sum[0 ~ i] - min(preSum's) >= 0` basically tells: for all possible k's, is there a split where preSum is smallest and makes the sum[x, i] greater than 0? + That's the beauty to the problem: no need to calculate all possible k's: just need 1 valid solution, and we don't care which k that will be. +*/ +class Solution { + public double findMaxAverage(int[] nums, int k) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + double min = Integer.MAX_VALUE; + double max = Integer.MIN_VALUE; + for (int i = 0; i < n; i++) { + min = Math.min(nums[i], min); + max = Math.max(nums[i], max); + } + + double start = min; + double end = max; + double midAvg = max; + while (end - start > 1e-5) { + midAvg = start + (end - start) / 2.0; + if (check(nums, k, midAvg)) { + start = midAvg; + } else { + end = midAvg; + } + } + return midAvg; + } + + /** + Return true, if there is a window with size >= k, + such that the average sum will >= given midAvg. + */ + private boolean check(int[] nums, int k, double midAvg) { + double preSum = 0; + double sum = 0; + double minPreSum = 0; + for (int i = 0; i < k; i++) { + sum += nums[i] - midAvg; + } + if (sum >= 0) { + return true; + } + for (int i = k; i < nums.length; i++) { + sum += nums[i] - midAvg; + preSum += nums[i - k] - midAvg; + minPreSum = Math.min(preSum, minPreSum); + if (sum >= minPreSum) { + return true; + } + } + return false; + } +} + + +``` \ No newline at end of file diff --git a/Java/Maximum Binary Tree.java b/Java/Maximum Binary Tree.java new file mode 100755 index 0000000..516ec1c --- /dev/null +++ b/Java/Maximum Binary Tree.java @@ -0,0 +1,196 @@ +M +1520828407 +tags: Tree, Stack + +给一串数字, 做一个 maximum binary tree: 最顶上的root最大; 左child也是一个max tree, 右child也必须是max tree. + +#### Monotonous Stack +- 用到bottom->top递减的stack: 最底下的root维持成最大的element. +- 过程当中, 一旦遇到currNode.val > stack.peek(), 就意味着需要把这个currNode放在 stack的底层位置. +- 也就是说, 遇到这个条件, process, pop()所有 currNode.val > stack.peek(), 最后把currNode加进去. + +- maxTree题目本身的要求是: 大的在最中间, 左右两边的subTree也要是maxTree: +- Monotonous Stack在这里帮助 keep/track of max value, 但是left/right tree的logic是MaxTree独有的. +- left/right node的assignment是根据题目要求: 中间最大值分开后, 左边的是左边subTree, 右边的作为右边subTree. + +#### Previous notes +- Should memorize MaxTree. 依次类推,会做Min-Tree, Expression Tree +- Stack里,最大的值在下面。利用此性质,有这样几个step: + +##### Step1 +- 把所有小于curr node的,全Pop出来, while loop, keep it going. +- 最后pop出的这个小于Curr的node:它同时也是stack里面pop出来小于curr的最大的一个,最接近curr大小。(因为这个stack最大值靠下面) +- 把这个最大的小于curr的node放在curr.left. + +##### Step2 +- 那么,接下去stack里面的一定是大于curr: +- 那就变成curr的left parent. set stack.peek().right = curr. + +##### Step3 +- 结尾:stack底部一定是最大的那个,也就是max tree的头。 + + + +``` +/* +LeetCode: Maximum Binary Tree +Given an integer array with no duplicates. +A maximum tree building on this array is defined as follow: + +The root is the maximum number in the array. +The left subtree is the maximum tree constructed from left part subarray divided by the maximum number. +The right subtree is the maximum tree constructed from right part subarray divided by the maximum number. +Construct the maximum tree by the given array and output the root node of this tree. + +Example 1: +Input: [3,2,1,6,0,5] +Output: return the tree root node representing the following tree: + + 6 + / \ + 3 5 + \ / + 2 0 + \ + 1 +Note: +The size of the given array will be in the range [1,1000]. +*/ + +/* +Thoughts: +Goal: find the max value and save it at last, as top of the tree. Find max, could take O(n) for unsorted list. +Here, we use a monotonous descending stack: if any current value < stack.peek(), save it in the peek. + +- If any current value > stack.peek(), we'll save the stack.pop() as left child of curr node. (while loop) +- If fact, we'll do while loop until the largest candidate (less than current node) shows up. +- For anything left in the stack, it must be larger than curr node, so set curr node as right child of stack.peek() +- In the end, we'll pop() all nodes off from the monotonous dscending stack, and use the bottom node as root. + +*/ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public TreeNode constructMaximumBinaryTree(int[] nums) { + if (nums == null || nums.length == 0) { + return null; + } + + Stack stack = new Stack<>(); + for (int num : nums) { + TreeNode node = new TreeNode(num); + // Pop the smaller node in the stack and set as left child of currNode. + // Loop until the largest candidate is found, and only then, settle on it. + while (!stack.isEmpty() && num >= stack.peek().val) { + node.left = stack.pop(); + } + + // Set node as right children, if stack is not empty: there must be larger node. + if (!stack.isEmpty()) { + stack.peek().right = node; + } + + // Push node, as the being largest of all, into the stack. + stack.push(node); + } + + // Find root + TreeNode root = stack.pop(); + while (!stack.isEmpty()) { + root = stack.pop(); + } + return root; + } +} + + +/* +Given an integer array with no duplicates. A max tree building on this array is defined as follow: + +The root is the maximum number in the array +The left subtree and right subtree are the max trees of the subarray divided by the root number. +Construct the max tree by the given array. + +Example +Given [2, 5, 6, 0, 3, 1], the max tree constructed by this array is: + + 6 + / \ + 5 3 + / / \ +2 0 1 +Challenge +O(n) time and memory. + +Tags Expand +LintCode Copyright Stack Cartesian Tree +*/ + +/* +Can't fingure express tree, so look at Max Tree problem first +Good explain here:http://blog.welkinlan.com/2015/06/29/max-tree-lintcode-java/ + +Iterate from left of array to right. So, every element we take, it will be the current right-most element. +Goal: find left-child, and find its left-parent. +Do for loop on all nodes.Remember to push the stack on every for iteration. +1. Left-child: use while loop to find the very last smaller node(comparing with curr Node), and set that as left-child. +2. Then, of course, the node left in stack (if still existing a node), will be the first larger node. That, will become curr + node's left parent. +3. At the end, we need to return largest element, which is root. Just by poping off all nodes will give us the bottom-largest-node + +Note: Interesting behavior: +Stack: always stores the largest value at bottom. In above example, when 6 gets in stack, it will never come back. +All smaller element (smaller than current point) will be poped out, +and of course, the last-possible-smaller element will be the largest smaller point on stack, then we attach it to current node. + +These behavior keeps larger value on upper level of the tree + +*/ + +public class Solution { + public TreeNode maxTree(int[] A) { + if (A == null || A.length == 0) { + return null; + } + Stack stack = new Stack(); + for (int i = 0; i < A.length; i++) { + TreeNode node = new TreeNode(A[i]); + while (!stack.isEmpty() && node.val >= stack.peek().val) { + node.left = stack.pop(); + } + if (!stack.isEmpty()) { + stack.peek().right = node; + } + stack.push(node); + } + + TreeNode rst = stack.pop(); + while(!stack.isEmpty()) { + rst = stack.pop(); + } + return rst; + } +} + + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + + +``` \ No newline at end of file diff --git a/Java/Maximum Depth of Binary Tree.java b/Java/Maximum Depth of Binary Tree.java old mode 100644 new mode 100755 index 6e01816..b132f76 --- a/Java/Maximum Depth of Binary Tree.java +++ b/Java/Maximum Depth of Binary Tree.java @@ -1,11 +1,25 @@ -DFS -Divide and conquer +E +1525668591 +tags: DFS, Tree + +给一个binary tree, 找最深depth + +#### DFS +- 这里要走过所有的node, 所以dfs非常合适 +- Divide and conquer. +- 维持一个最大值: Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +- 注意check root == null + +#### Note +- BFS is doable as well, but a bit more code to write: tracks largest level we reach + ``` /* 71% Accepted Given a binary tree, find its maximum depth. -The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. +The maximum depth is the number of nodes along the longest path +from the root node down to the farthest leaf node. Example Given a binary tree as follow: @@ -25,12 +39,24 @@ Tags Expand Tree Binary Tree Depth First Search -Thinking process: -check if root is null, return 0 if so. -Divide and return integer as the depth -Conquer: find the max and return depth + 1. */ + +/* +Thoughts: +DFS. Find all depth and return max alone the way +Check leaf child: (null, null) condition -> return 1 +Then upper parent = Math.max(DFS(left), DFS(right)) + 1 +*/ +public class Solution { + public int maxDepth(TreeNode root) { + if (root == null) { + return 0; + } + return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; + } +} + /** * Definition of TreeNode: * public class TreeNode { @@ -42,6 +68,12 @@ * } * } */ +/** +Thinking process: +check if root is null, return 0 if so. +Divide and return integer as the depth +Conquer: find the max and return depth + 1. + */ public class Solution { /** * @param root: The root of binary tree. diff --git a/Java/Maximum Product Subarray.java b/Java/Maximum Product Subarray.java old mode 100644 new mode 100755 index 7f2409d..677570d --- a/Java/Maximum Product Subarray.java +++ b/Java/Maximum Product Subarray.java @@ -1,3 +1,22 @@ +M +1516608238 +tags: Array, DP, Subarray + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### DP +- 求最值, 想到DP. Time/Space O (n) +- 两个特别处: +- 1. 正负数情况, 需要用两个DP array. +- 2. continuous prodct 这个条件决定了在Math.min, Math.max的时候, +- 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. +- 这也就注定了需要一个global variable 来hold result. + +#### Space optimization, rolling array +- maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins +- Time: O(n), space: O(1) + +``` /* Find the contiguous subarray within an array (containing at least one number) which has the largest product. @@ -8,6 +27,72 @@ Find the contiguous subarray within an array (containing at least one number) wh Dynamic Programming Subarray */ /* +Thoughts: +'Largest', DP. +Consider positivie/Negative numbers. +f[x] = largest continuous product at index x. +NOTE: it's not entire array's largest, need a stand-along variable to hold global max. +if nums[x] < 0, want (min of f[x-1]) * nums[x] +if nums[x] > 0, want (max of f[x-1]) * nums[x] +Consider two different arrays. +f[x] = Math.max( min(f[x-1]) * nums[x] if nums[x]<0, or max(f[x-1])*nums[x] if nums[x]>0) +initial condition: +x = 0 -> nums[0] + +*/ +class Solution { + public int maxProduct(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + final int[] maxProduct = new int[nums.length]; + final int[] minProduct = new int[nums.length]; + maxProduct[0] = nums[0]; + minProduct[0] = nums[0]; + int result = nums[0]; + for (int i = 1; i < nums.length; i++) { + if (nums[i] > 0) { + maxProduct[i] = Math.max(nums[i], maxProduct[i - 1] * nums[i]); + minProduct[i] = Math.min(nums[i], minProduct[i - 1] * nums[i]); + } else { + maxProduct[i] = Math.max(nums[i], minProduct[i - 1] * nums[i]); + minProduct[i] = Math.min(nums[i], maxProduct[i - 1] * nums[i]); + } + result = Math.max(result, maxProduct[i]); + } + return result; + } +} + +/* +Rolling array + */ +class Solution { + public int maxProduct(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + final int[] maxProduct = new int[2]; + final int[] minProduct = new int[2]; + maxProduct[0] = nums[0]; + minProduct[0] = nums[0]; + int result = nums[0]; + for (int i = 1; i < nums.length; i++) { + if (nums[i] > 0) { + maxProduct[i % 2] = Math.max(nums[i], maxProduct[(i - 1) % 2] * nums[i]); + minProduct[i % 2] = Math.min(nums[i], minProduct[(i - 1) % 2] * nums[i]); + } else { + maxProduct[i % 2] = Math.max(nums[i], minProduct[(i - 1) % 2] * nums[i]); + minProduct[i % 2] = Math.min(nums[i], maxProduct[(i - 1) % 2] * nums[i]); + } + result = Math.max(result, maxProduct[i % 2]); + } + return result; + } +} + +/* +Previous notes Attempt2: Use a max array and a min array. (http://www.jiuzhang.com/solutions/maximum-product-subarray/) This is similar to my original attempt1, but saves a lot memory space. @@ -20,33 +105,6 @@ Find the contiguous subarray within an array (containing at least one number) wh Trick: depending on nums[i] is positive or negative, calculate differently ... */ - -public class Solution { - public int maxProduct(int[] nums) { - if (nums == null || nums.length == 0) { - return 0; - } - int[] max = new int[nums.length]; - int[] min = new int[nums.length]; - max[0] = nums[0]; - min[0] = nums[0]; - int rst = max[0]; - for (int i = 1; i < nums.length; i++) { - if (nums[i] > 0) { - max[i] = Math.max(nums[i], max[i - 1] * nums[i]);//the nums[i] could just be the best option - min[i] = Math.min(nums[i], min[i - 1] * nums[i]); - } else { - max[i] = Math.max(nums[i], min[i - 1] * nums[i]); - min[i] = Math.min(nums[i], max[i - 1] * nums[i]); - } - rst = Math.max(rst, max[i]); - } - return rst; - } -} - - - /* Attempt1 thoughts: 97% correct. However, this exceeds memory, basically the DP[][] is too large. @@ -101,4 +159,5 @@ public int maxProduct(int[] nums) { } return max; } -} \ No newline at end of file +} +``` \ No newline at end of file diff --git a/Java/Maximum Size Subarray Sum Equals k.java b/Java/Maximum Size Subarray Sum Equals k.java new file mode 100644 index 0000000..a6c90b9 --- /dev/null +++ b/Java/Maximum Size Subarray Sum Equals k.java @@ -0,0 +1,63 @@ +M +1531896141 +tags: Hash Table, PreSum, Subarray +time: O(n) +space: O(n) + +#### Map +- use `Map` to store inline preSum and its index. +- 1. Build presum incline +- 2. Use map to cache current preSum value and its index: `Map` +- 3. Each iteration: calculate possible preSum candidate that prior target sequence. ex: `(preSum - k)` +- 4. Use the calculated preSum candidate to find index +- 5. Use found index to calculate for result. ex: calculate range. + +``` +/* +Given an array nums and a target value k, find the maximum length of a subarray that sums to k. +If there isn't one, return 0 instead. + +Note: +The sum of the entire nums array is guaranteed to fit within the 32-bit signed integer range. + +Example 1: + +Input: nums = [1, -1, 5, -2, 3], k = 3 +Output: 4 +Explanation: The subarray [1, -1, 5, -2] sums to 3 and is the longest. +Example 2: + +Input: nums = [-2, -1, 2, 1], k = 1 +Output: 2 +Explanation: The subarray [-1, 2] sums to 1 and is the longest. +Follow Up: +Can you do it in O(n) time? + +*/ + +/* +Same concept as 2 sum +1. calcualte preSum as we go +2. store presum and the index in map +3. if @ any index: map.containsKey(presum - k), then the starting index was available: [startIndex, i] +4. maintain global variable +*/ +class Solution { + public int maxSubArrayLen(int[] nums, int k) { + if (nums == null || nums.length == 0) return 0; + Map map = new HashMap<>(); + map.put(0, -1); + int preSum = 0, max = 0; + for (int i = 0; i < nums.length; i++) { + preSum += nums[i]; + if (map.containsKey(preSum - k)) { + max = Math.max(max, i - map.get(preSum - k)); + } + if (!map.containsKey(preSum)) { + map.put(preSum, i); + } + } + return max; + } +} +``` \ No newline at end of file diff --git a/Java/Maximum Subarray II.java b/Java/Maximum Subarray II.java new file mode 100755 index 0000000..c243322 --- /dev/null +++ b/Java/Maximum Subarray II.java @@ -0,0 +1,317 @@ +M +1525363049 +tags: Greedy, Array, DP, Sequence DP, PreSum, Subarray + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + +``` +/* +LintCode: https://lintcode.com/en/problem/maximum-subarray-ii/# +Given an array of integers, find two non-overlapping subarrays which have the largest sum. + +The number in each subarray should be contiguous. + +Return the largest sum. + +Note +The subarray should contain at least one number + +Example +For given [1, 3, -1, 2, -1, 2], +the two subarrays are [1, 3] and [2, -1, 2] +or [1, 3, -1, 2] and [2], they both have the largest sum 7. + +Challenge +Can you do it in time complexity O(n) ? + +Tags Expand +Greedy Enumeration Array LintCode Copyright SubArray Forward-Backward Traversal + +*/ + +/* +Thoughts: +Similar to Maximum Subarray, from one side: +dp[i]: for first i items, the max subarray sum containing nums[i - 1] + +Should process the dp from both left and right side, +with index i being the division point + +Note that we need to track the max for left and right, +so we need maxLeft[], maxRight[] +*/ +public class Solution { + /* + * @param nums: A list of integers + * @return: An integer denotes the sum of max two non-overlapping subarrays + */ + public int maxTwoSubArrays(List nums) { + if (nums == null || nums.size() == 0) { + return 0; + } + int n = nums.size(); + int[] dpLeft = new int[n + 1]; + int[] dpRight = new int[n + 1]; + dpLeft[0] = 0; + dpRight[n] = 0; + + int[] maxLeft = new int[n + 1];; + int[] maxRight = new int[n + 1]; + maxLeft[0] = Integer.MIN_VALUE; + maxRight[n] = Integer.MIN_VALUE; + + // Left + for (int i = 1; i <= n; i++) { + dpLeft[i] = Math.max(dpLeft[i - 1] + nums.get(i - 1), nums.get(i - 1)); + maxLeft[i] = Math.max(maxLeft[i - 1], dpLeft[i]); + } + + // Right + for (int j = n - 1; j >= 0; j--) { + dpRight[j] = Math.max(dpRight[j + 1] + nums.get(j), nums.get(j)); + maxRight[j] = Math.max(maxRight[j + 1], dpRight[j]); + } + + // Combine + int max = Integer.MIN_VALUE; + for (int i = 1; i < n; i++) { + max = Math.max(max, maxLeft[i] + maxRight[i]); + } + + return max; + } +} + + +// Rolling array +public class Solution { + /* + * @param nums: A list of integers + * @return: An integer denotes the sum of max two non-overlapping subarrays + */ + public int maxTwoSubArrays(List nums) { + if (nums == null || nums.size() == 0) { + return 0; + } + int n = nums.size(); + int[] dpLeft = new int[2]; + int[] dpRight = new int[2]; + dpLeft[0] = 0; + dpRight[n % 2] = 0; + + int[] maxLeft = new int[n + 1]; + int[] maxRight = new int[n + 1]; + maxLeft[0] = Integer.MIN_VALUE; + maxRight[n] = Integer.MIN_VALUE; + + // Left + for (int i = 1; i <= n; i++) { + dpLeft[i % 2] = Math.max(dpLeft[(i - 1) % 2] + nums.get(i - 1), nums.get(i - 1)); + maxLeft[i] = Math.max(maxLeft[i - 1], dpLeft[i % 2]); + } + + // Right + for (int j = n - 1; j >= 0; j--) { + dpRight[j % 2] = Math.max(dpRight[(j + 1) % 2] + nums.get(j), nums.get(j)); + maxRight[j] = Math.max(maxRight[j + 1], dpRight[j % 2]); + } + + // Combine + int max = Integer.MIN_VALUE; + for (int i = 1; i < n; i++) { + max = Math.max(max, maxLeft[i] + maxRight[i]); + } + + return max; + } +} + +// Futher simplify: +// use 1 for loop for both left and right +public class Solution { + /* + * @param nums: A list of integers + * @return: An integer denotes the sum of max two non-overlapping subarrays + */ + public int maxTwoSubArrays(List nums) { + if (nums == null || nums.size() == 0) { + return 0; + } + int n = nums.size(); + int[] dpLeft = new int[2]; + int[] dpRight = new int[2]; + dpLeft[0] = 0; + dpRight[n % 2] = 0; + + int[] maxLeft = new int[n + 1];; + int[] maxRight = new int[n + 1]; + maxLeft[0] = Integer.MIN_VALUE; + maxRight[n] = Integer.MIN_VALUE; + + + for (int i = 1; i <= n; i++) { + // Left + dpLeft[i % 2] = Math.max(dpLeft[(i - 1) % 2] + nums.get(i - 1), nums.get(i - 1)); + maxLeft[i] = Math.max(maxLeft[i - 1], dpLeft[i % 2]); + + // Right + int j = n - i; + dpRight[j % 2] = Math.max(dpRight[(j + 1) % 2] + nums.get(j), nums.get(j)); + maxRight[j] = Math.max(maxRight[j + 1], dpRight[j % 2]); + } + + // Combine + int max = Integer.MIN_VALUE; + for (int i = 1; i < n; i++) { + max = Math.max(max, maxLeft[i] + maxRight[i]); + } + + return max; + } +} + + +/* + Thoughts: 11.23.2015 + Similar to Maximum Subarray。 Now just try to build 2 maximum subbary, from left/right. + Meetpoint i, will give largest possible sum +*/ +public class Solution { + /** + * @param nums: A list of integers + * @return: An integer denotes the sum of max two non-overlapping subarrays + */ + public int maxTwoSubArrays(ArrayList nums) { + if (nums == null || nums.size() == 0) { + return 0; + } + + int preSum = 0; + int minPreSum = 0; + int max = Integer.MIN_VALUE; + int n = nums.size(); + int[] left = new int[n]; + int[] right = new int[n]; + for (int i = 0; i < n; i++) { + preSum += nums.get(i); + max = Math.max(max, preSum - minPreSum); + minPreSum = Math.min(minPreSum, preSum); + left[i] = max; + } + preSum = 0; + minPreSum = 0; + max = Integer.MIN_VALUE; + for (int i = n - 1; i >= 0; i--) { + preSum += nums.get(i); + max = Math.max(max, preSum - minPreSum); + minPreSum = Math.min(minPreSum, preSum); + right[i] = max; + } + max = Integer.MIN_VALUE; + for (int i = 0; i < n - 1; i++) { + int rst = left[i] + right[i + 1]; + max = Math.max(max, rst); + } + + return max; + } +} + + + + + +/* + +Thinking process: +Find frontSum: largest sum from index 0 till current at each index. +Find endSum: largest sum from end(endSum.length - 1) to current at each index. +Add them up: at any point i, leftSum + rightSum = largest 2 non-overlap sum. + i + i i + i i +i i + +*/ +public class Solution { + /** + * @param nums: A list of integers + * @return: An integer denotes the sum of max two non-overlapping subarrays + */ + public int maxTwoSubArrays(ArrayList nums) { + if (nums == null || nums.size() == 0) { + return 0; + } + int[] frontSum = new int[nums.size()]; + int[] endSum = new int[nums.size()]; + int maxSum = 0; + frontSum[0] = nums.get(0); + //Init frontSum + for (int i = 1; i < frontSum.length; i++) { + if (frontSum[i - 1] < 0) { + frontSum[i] = nums.get(i); + } else { + frontSum[i] = frontSum[i - 1] + nums.get(i); + } + } + maxSum = frontSum[0]; + //Find max + for (int i = 1; i < frontSum.length; i++) { + if (frontSum[i] < maxSum) { + frontSum[i] = maxSum; + } else { + maxSum = frontSum[i]; + } + } + + //Init endSum + endSum[endSum.length - 1] = nums.get(nums.size() - 1); + for (int i = endSum.length - 2; i >= 0; i--) { + if (endSum[i + 1] < 0) { + endSum[i] = nums.get(i); + } else { + endSum[i] = endSum[i + 1] + nums.get(i); + } + } + //Find max + maxSum = endSum[endSum.length - 1]; + for (int i = endSum.length - 2; i >= 0; i--) { + if (endSum[i] < maxSum) { + endSum[i] = maxSum; + } else { + maxSum = endSum[i]; + } + } + //Calculate max Sum + maxSum = Integer.MIN_VALUE; + for (int i = 0; i < nums.size() - 1; i++) { + maxSum = Math.max(maxSum, frontSum[i] + endSum[i + 1]); + } + return maxSum; + } +} + + + + + +``` \ No newline at end of file diff --git a/Java/Maximum Subarray III.java b/Java/Maximum Subarray III.java old mode 100644 new mode 100755 index 941e74b..fc041c6 --- a/Java/Maximum Subarray III.java +++ b/Java/Maximum Subarray III.java @@ -1,12 +1,16 @@ +R + +``` /* -Given an array of integers and a number k, find k non-overlapping subarrays which have the largest sum. +Given an array of integers and a number k, +find k non-overlapping subarrays which have the largest sum. The number in each subarray should be contiguous. Return the largest sum. -Have you met this question in a real interview? Yes + Example Given [-1,4,-2,3,-2,3], k=2, return 8 @@ -17,17 +21,39 @@ LintCode Copyright Dynamic Programming Subarray Array */ -/* - NOT DONE -*/ +// Should be partition DP +/** +dp[i][j]: max sum for first i items, divided into j parts +dp[n + 1][k + 1] +dp[0][0] = 0; + +dp[i][j] = Math.max(dp[x][j - 1] + maxSubArray(x+1,i)), x = 1 ~ n +http://www.cnblogs.com/lishiblog/p/4183917.html + */ public class Solution { - /** - * @param nums: A list of integers - * @param k: An integer denote to find k non-overlapping subarrays - * @return: An integer denote the sum of max k non-overlapping subarrays - */ - public int maxSubArray(ArrayList nums, int k) { - // write your code + public int maxSubArray(int[] nums, int k) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[][] dp = new int[n + 1][k + 1]; + // dp[i][0] = 0; + + for (int j = 1; j <= k; j++) { + for (int i = j; i <= n; i++) { // ??? why i = j + dp[i][j] = Integer.MIN_VALUE; + + int endMax = 0; + int max = Integer.MIN_VALUE; + for (int x = i - 1; x >= j - 1; x--) { // ??? why x = i-1, x >= j -1? + endMax = Math.max(nums[x], nums[x] + endMax); + max = Math.max(max, endMax); + dp[i][j] = dp[i][j] < dp[x][j - 1] + max ? dp[x][j - 1] + max : dp[i][j]; + } + } + } + return dp[n][k]; } } +``` \ No newline at end of file diff --git a/Java/Maximum Subarray.java b/Java/Maximum Subarray.java new file mode 100755 index 0000000..87cd19c --- /dev/null +++ b/Java/Maximum Subarray.java @@ -0,0 +1,371 @@ +E +1525331164 +tags: DP, Sequence DP, Array, Divide and Conquer, DFS, PreSum, Subarray +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: +``` +// handle cross-mid case +int tempSum = 0, continuousLeftSumMax = 0; +// find continuous max going towards left +for (int i = mid - 1; i >= left; i--) { + // always continous summing up + tempSum += nums[i]; + // from one direction, take the best + continuousLeftSumMax = Math.max(continuousLeftSumMax, tempSum); +} +``` +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + +#### Previous Notes +##### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + +``` +/** +LeetCode: +Given an integer array nums, find the contiguous subarray (containing at least one number) +which has the largest sum and return its sum. + +Example: + +Input: [-2,1,-3,4,-1,2,1,-5,4], +Output: 6 +Explanation: [4,-1,2,1] has the largest sum = 6. +Follow up: + +If you have figured out the O(n) solution, +try coding another solution using the divide and conquer approach, +which is more subtle. + + */ + +// Despte the detailed dp[] solution, we have the light version: +public class Solution { + public int maxSubArray(int[] nums) { + if (nums == null || nums.length == 0) return Integer.MIN_VALUE; + int preMaxSum = 0, max = Integer.MIN_VALUE; + for (int num : nums) { + preMaxSum = Math.max(num, preMaxSum + num); + max = Math.max(max, preMaxSum); + } + return max; + } +} + +/* +Thoughts: +sequence dp +continous subarray: cannot skip element +dp[i]: for first i items, what's the largest sum that containts nums[i]? +dp[i] = Math.max(dp[i - 1] + nums[i - 1], nums[i - 1]) + +record max globally + +dp[i]: 0 items, max = 0 +*/ +class Solution { + public int maxSubArray(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] dp = new int[n + 1]; + dp[0] = 0; + int max = Integer.MIN_VALUE; + for (int i = 1; i <= n; i++) { + dp[i] = Math.max(dp[i - 1] + nums[i - 1], nums[i - 1]); // contious, or start from nums[i-1] + max = Math.max(max, dp[i]); + } + + return max; + } +} + +// Rolling array: +class Solution { + public int maxSubArray(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] dp = new int[2]; + dp[0] = 0; + int max = Integer.MIN_VALUE; + for (int i = 1; i <= n; i++) { + dp[i % 2] = Math.max(dp[(i - 1) % 2] + nums[i - 1], nums[i - 1]); + max = Math.max(max, dp[i % 2]); + } + + return max; + } +} + + + +// checking dp[i-1] >= 0. Same as Math.max(dp[i - 1] + nums[i - 1], nums[i - 1]); +class Solution { + public int maxSubArray(int[] nums) { + //check edge case + if (nums == null || nums.length == 0) { + return 0; + } + + // init dp, global max + int n = nums.length; + int[] dp = new int[n + 1]; + dp[0] = 0; + + // calculate dp over for loop + int max = Integer.MIN_VALUE; + for (int i = 1; i <= n; i++) { + dp[i] = nums[i - 1] + (dp[i - 1] >= 0 ? dp[i - 1] : 0); + max = Math.max(max, dp[i]); + } + return max; + } +} +// Rolling array +class Solution { + public int maxSubArray(int[] nums) { + //check edge case + if (nums == null || nums.length == 0) { + return 0; + } + + // init dp, global max + int n = nums.length; + int[] dp = new int[2]; + dp[0] = 0; + + // calculate dp over for loop + int max = Integer.MIN_VALUE; + for (int i = 1; i <= n; i++) { + dp[i % 2] = nums[i - 1] + (dp[(i - 1) % 2] >= 0 ? dp[(i - 1) % 2] : 0); + max = Math.max(max, dp[i % 2]); + } + return max; + } +} + + +/* +divide and conquer, dfs +3 conditions: left of mid, right of mid, or cross-mid +use dfs to calculate left case, right case +carefully handle cross-mid case +*/ + +class Solution { + public int maxSubArray(int[] nums) { + //check edge case + if (nums == null || nums.length == 0) { + return 0; + } + + return dfs(nums, 0, nums.length - 1, Integer.MIN_VALUE); + } + + private int dfs(int[] nums, int left, int right, int sum) { + if (left > right) return Integer.MIN_VALUE; + + // dfs on left, right range. Mid point is skipped + int mid = left + (right - left) / 2; + int leftMax = dfs(nums, left, mid - 1, sum); + int rightMax = dfs(nums, mid + 1, right, sum); + int maxSum = Math.max(sum, Math.max(leftMax, rightMax)); + + // handle cross-mid case + int tempSum = 0, continuousLeftSumMax = 0, continuousRightSumMax = 0; + // find continuous max going towards left + for (int i = mid - 1; i >= left; i--) { + tempSum += nums[i]; + continuousLeftSumMax = Math.max(continuousLeftSumMax, tempSum); + } + // find continuous max going towards right + tempSum = 0; + for (int i = mid + 1; i <= right; i++) { + tempSum += nums[i]; + continuousRightSumMax = Math.max(continuousRightSumMax, tempSum); + } + maxSum = Math.max(maxSum, nums[mid] + continuousLeftSumMax + continuousRightSumMax); + return maxSum; + } +} + + + +/* +LintCode +Maximum Subarray Show Result My Submissions + +35% Accepted +Given an array of integers, find a contiguous subarray which has the largest sum. + +Note +The subarray should contain at least one number + +Example +For example, given the array [−2,2,−3,4,−1,2,1,−5,3], the contiguous subarray [4,−1,2,1] has the largest sum = 6. + +Challenge +Can you do it in time complexity O(n)? + +Tags Expand +Array SubArray Greedy Enumeration LintCode Copyright + + +*/ +/* +Thoughts: +1. Move end see how far it can go which keeps sum increasing +2. sum[i] = sum[i - 1] + nums[i]. We need to decide if sum[i] will take sum[i-1] depending on if sum[i-1] is positive or negative. +3. maintain a maxSum +*/ +class Solution { + public int maxSubArray(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + final int[] sums = new int[nums.length]; + sums[0] = nums[0]; + int maxSum = sums[0]; + + for (int i = 1; i < nums.length; i++) { + if (sums[i - 1] < 0) {// sums[i-1] only reduces maxSum, therefore skip it in sums[i] + sums[i] = nums[i]; + } else { + sums[i] = sums[i - 1] + nums[i]; + } + maxSum = Math.max(maxSum, sums[i]); + } + return maxSum; + } +} + +/* + Same as above, but with list as input + Thinking proces: + Store the sum in a array. + Normally, sum[i] = sum[i - 1] + nums[i]. + However, if sum[i - 1] is a nagetive number, that means sum[i - 1] won't do any good for later sum but only decrease the sum. + In this case, when sums[i - 1] < 0, we don't add it. + + When sum[i-1], it actaully starts from nums.get(i) again. +*/ + +public class Solution { + /** + * @param nums: A list of integers + * @return: A integer indicate the sum of max subarray + */ + public int maxSubArray(ArrayList nums) { + if (nums == null || nums.size() == 0) { + return 0; + } + int[] sums = new int[nums.size()]; + sums[0] = nums.get(0); + int maxSum = sums[0]; + for (int i = 1; i < sums.length; i++) { + if (sums[i - 1] < 0) { + sums[i] = nums.get(i); + } else { + sums[i] = sums[i - 1] + nums.get(i); + } + maxSum = Math.max(maxSum, sums[i]); + } + return maxSum; + } +} + +/* + Thoughts: 11.23.2015 + Originally, the method is:When sum[i-1], it actaully starts from nums.get(i) again. + Calculate the max + New way of using preSum. + sum[i ~ j] = preSum[j] - preSum[i - 1]; + Calculate the max + + Though this helps further problems, but it's actaully O(n^2) +*/ + + +public class Solution { + public int maxSubArray(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + + int n = nums.length; + int[] preSum = new int[n]; + preSum[0] = nums[0]; + int max = Math.max(Integer.MIN_VALUE, preSum[0]); + for (int i = 1; i < n; i++) { + preSum[i] = preSum[i - 1] + nums[i]; + } + for (int j = n - 1; j > 0; j--) { + for (int i = 0; i <= j; i++) { + int sum = 0; + if (i == 0) { + sum = preSum[j]; + } else { + sum = preSum[j] - preSum[i - 1]; + } + max = Math.max(sum, max); + } + }//end for + return max; + } +} + + + +/* + To further extend the prefix sum idea, we are really trying: + Use regular preSum - smallest preSum in earlier spots, which gives largest value. + So maintain a regular preSum, and maintain a minPreSum. + Maintain a max to mark the difference between (preSum - minPreSum) + Also, here, we dont' really care about index.So just skip index. + O(n) here +*/ + +public class Solution { + /** + * @param nums: A list of integers + * @return: A integer indicate the sum of max subarray + */ + public int maxSubArray(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int preSum = 0; + int minPreSum = 0; + int max = Integer.MIN_VALUE; + for (int i = 0; i < nums.length; i++) { + preSum += nums[i]; + max = Math.max(max, preSum - minPreSum); + minPreSum = Math.min(minPreSum, preSum); + } + return max; + } +} + +``` \ No newline at end of file diff --git a/Java/Maximum Vacation Days.java b/Java/Maximum Vacation Days.java new file mode 100644 index 0000000..ce85696 --- /dev/null +++ b/Java/Maximum Vacation Days.java @@ -0,0 +1,84 @@ +H +1534400155 +tags: DP + +``` +/* +LeetCode wants to give one of its best employees the option to travel among N cities to collect algorithm problems. But all work and no play makes Jack a dull boy, you could take vacations in some particular cities and weeks. Your job is to schedule the traveling to maximize the number of vacation days you could take, but there are certain rules and restrictions you need to follow. + +Rules and restrictions: +You can only travel among N cities, represented by indexes from 0 to N-1. Initially, you are in the city indexed 0 on Monday. +The cities are connected by flights. The flights are represented as a N*N matrix (not necessary symmetrical), called flights representing the airline status from the city i to the city j. If there is no flight from the city i to the city j, flights[i][j] = 0; Otherwise, flights[i][j] = 1. Also, flights[i][i] = 0 for all i. +You totally have K weeks (each week has 7 days) to travel. You can only take flights at most once per day and can only take flights on each week's Monday morning. Since flight time is so short, we don't consider the impact of flight time. +For each city, you can only have restricted vacation days in different weeks, given an N*K matrix called days representing this relationship. For the value of days[i][j], it represents the maximum days you could take vacation in the city i in the week j. +You're given the flights matrix and days matrix, and you need to output the maximum vacation days you could take during K weeks. + +Example 1: +Input:flights = [[0,1,1],[1,0,1],[1,1,0]], days = [[1,3,1],[6,0,3],[3,3,3]] +Output: 12 +Explanation: +Ans = 6 + 3 + 3 = 12. + +One of the best strategies is: +1st week : fly from city 0 to city 1 on Monday, and play 6 days and work 1 day. +(Although you start at city 0, we could also fly to and start at other cities since it is Monday.) +2nd week : fly from city 1 to city 2 on Monday, and play 3 days and work 4 days. +3rd week : stay at city 2, and play 3 days and work 4 days. +Example 2: +Input:flights = [[0,0,0],[0,0,0],[0,0,0]], days = [[1,1,1],[7,7,7],[7,7,7]] +Output: 3 +Explanation: +Ans = 1 + 1 + 1 = 3. + +Since there is no flights enable you to move to another city, you have to stay at city 0 for the whole 3 weeks. +For each week, you only have one day to play and six days to work. +So the maximum number of vacation days is 3. +Example 3: +Input:flights = [[0,1,1],[1,0,1],[1,1,0]], days = [[7,0,0],[0,7,0],[0,0,7]] +Output: 21 +Explanation: +Ans = 7 + 7 + 7 = 21 + +One of the best strategies is: +1st week : stay at city 0, and play 7 days. +2nd week : fly from city 0 to city 1 on Monday, and play 7 days. +3rd week : fly from city 1 to city 2 on Monday, and play 7 days. +Note: +N and K are positive integers, which are in the range of [1, 100]. +In the matrix flights, all the values are integers in the range of [0, 1]. +In the matrix days, all the values are integers in the range [0, 7]. +You could stay at a city beyond the number of vacation days, but you should work on the extra days, which won't be counted as vacation days. +If you fly from the city A to the city B and take the vacation on that day, the deduction towards vacation days will count towards the vacation days of city B in that week. +We don't consider the impact of flight hours towards the calculation of vacation days. + +*/ + +class Solution { + public int maxVacationDays(int[][] flights, int[][] days) { + int m = days[0].length; // week count + int n = days.length; // city count + int[][] dp = new int[m][n]; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (i == 0) { + dp[i][j] = j == 0 || flights[0][j] != 0 ? days[j][i]: Integer.MIN_VALUE; + continue; + } + dp[i][j] = Integer.MIN_VALUE; + for (int k = 0; k < m; k++) { // last city + if ((k == j || flights[k][j] == 1) && dp[i - 1][k] != Integer.MIN_VALUE) { + dp[i][j] = Math.max(dp[i - 1][k] + days[j][i], dp[i][j]); + } + } + } + } + + int max = 0; + for (int i = 0; i < n; i++) { + max = Math.max(max, dp[m - 1][i]); + } + return max; + } +} +``` \ No newline at end of file diff --git a/Java/Maximum XOR of Two Numbers in an Array.java b/Java/Maximum XOR of Two Numbers in an Array.java new file mode 100644 index 0000000..30107e2 --- /dev/null +++ b/Java/Maximum XOR of Two Numbers in an Array.java @@ -0,0 +1,91 @@ +M +1517990589 +tags: Bit Manipulation, Trie + +比较难想到. 利用到XOR性质A^B=C, then A=B^C. +1. 枚举可能的A, 2. 然后一个个猜. + +1. 枚举A: 因为求MAX肯定是找leading-1最多的数字, 那么枚举A从(1000000...000)开始, +每次多一位取1或者0 +2. 因为枚举A的时候是按照每个bit来, 那么B和C也要以同样数位出现. +这里吧B和C变成了prefix的形式, 放在了set里面. +跟2sum用hashmap的思想类似, 每次用枚举的 A^B=C, 看看结果C是否已经在set里面. +如果在, 证明枚举的A可能被B^C得出, 那么就找到了一种情况. + +还用到一些技巧: +mask = (1 << i); // i位mask +mask = mask | (1 << i); // prefix mask + +``` +/* +Given a non-empty array of numbers, a0, a1, a2, … , an-1, where 0 ≤ ai < 231. + +Find the maximum result of ai XOR aj, where 0 ≤ i, j < n. + +Could you do t`his in O(n) runtime? + +Example: + +Input: [3, 10, 5, 25, 2, 8] + +Output: 28 + +Explanation: The maximum result is 5 ^ 25 = 28. +*/ + +/* +Thoughts: +Brutle: O(n^2) solution over nums. + +Bit Manipulation. +Write out: 5 = 0101, 25 = 16 + 8 + 1 = 00011001 => 00000101 ^ 00011001 = 00011100 = 28 + +To find maximum: we want the result to have as many leading '1' as possible. +One thing about XOR: if A^B=C, then A=B^C. +Let's say A is the max result we want, so: res ^ B = C, then res = B ^ C + +Introduce a concept of 'prefix' of the binary numbers: use a leading-1 mask to cut front part of the binary. +Imagine B and C are all prefixes in a set. +If res ^ B can be found from the set, that means it's possible to have B ^ C = res, where B and C are direclty picked from the set. +That is: there can be a result value, which can be generated with XOR by 2 numbers with prefix B and C. + +All we have to do now: +Generate all possibilities of res: each bit can be 0 or 1. +res should grow from 1000000.....00000 (32-bit) by OR with (1 << i) +save res as it grows. +next res candidate can be 1100000.....00000, next 1010000.....00000 or 1110000.....00000 + + +res generation refer to: +http://www.cnblogs.com/grandyang/p/5991530.html + +O(32 * n) = O(n) +*/ +class Solution { + public int findMaximumXOR(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int res = 0, mask = 0; + for (int i = 31; i >= 0; i--) { + // Prepare prefixes + mask = mask | (1 << i); + final Set set = new HashSet<>(); + for (int num : nums) { + set.add(num & mask); + } + + // Validate result ^ B = C? + int temp = res | (1 << i); + for (int prefix : set) { + if (set.contains(temp ^ prefix)) { + res = temp; + break; + } + } + } + return res; + } +} + +``` \ No newline at end of file diff --git a/Java/Median of two Sorted Arrays.java b/Java/Median of two Sorted Arrays.java old mode 100644 new mode 100755 index 09b843f..67e3e6c --- a/Java/Median of two Sorted Arrays.java +++ b/Java/Median of two Sorted Arrays.java @@ -1,39 +1,106 @@ +H +1533619954 +tags: Array, Binary Search, Divide and Conquer, DFS + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + +``` /* -Median of two Sorted Arrays +There are two sorted arrays nums1 and nums2 of size m and n respectively. -There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. +Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). -Example -Given A=[1,2,3,4,5,6] and B=[2,3,4,5], the median is 3.5. +Example 1: +nums1 = [1, 3] +nums2 = [2] +The median is 2.0 + +Example 2: +nums1 = [1, 2] +nums2 = [3, 4] +The median is (2 + 3)/2 = 2.5 +LintCode examples: +Given A=[1,2,3,4,5,6] and B=[2,3,4,5], the median is 3.5. Given A=[1,2,3] and B=[4,5], the median is 3. -Challenge -The overall run time complexity should be O(log (m+n)). +*/ + + +/* + Thoughts: + Trivial: merge and find median. NOPE: have to be in log(m+n) time + http://www.jiuzhang.com/solutions/median-of-two-sorted-arrays/ -Tags Expand -Sorted Array Divide and Conquer Array Zenefits Uber Google + http://articles.leetcode.com/find-k-th-smallest-element-in-union-of + + Good one: http://blog.csdn.net/yutianzuijin/article/details/11499917 + http://blog.csdn.net/zxzxy1988/article/details/8587244 */ /* - Thoughts: - Trivial: merge and find median. NOPE: have to be in log(m+n) time - http://www.jiuzhang.com/solutions/median-of-two-sorted-arrays/ +Thoughts: +A[1,2] +B[3,4,5] +Assume after merge: [1,2,3,4,5], we'll be looking for the item in the mid. +Math: consider A[mid/2] and B[mid/2]. If A[mid/2]B[mid/2] - http://fisherlei.blogspot.com/2012/12/leetcode-median-of-two-sorted-arrays.html - +Approach: find kth number of two sorted array, k ~= (m+n)/2 +- use startA, startB indexes to track the partition location +- Cut off half of one list at a time, recursively process the rest 3/4 of overall content. +- when k = 1, since A/B lists are sorted, should return the min +- Be careful with index: it's all 0 based */ - class Solution { - /** - * @param A: An integer array. - * @param B: An integer array. - * @return: a double whose format is *.5 or *.0 - */ - public double findMedianSortedArrays(int[] A, int[] B) { - // write your code here + public double findMedianSortedArrays(int[] numsA, int[] numsB) { + // Assume edge case is safe + int n = numsA.length + numsB.length; + + // Handle even/odd cases + if (n % 2 == 0) { + return ( + findKth(numsA, 0, numsB, 0, n / 2) + + findKth(numsA, 0, numsB, 0, n / 2 + 1) + ) / 2.0; + } + return (double)findKth(numsA, 0, numsB, 0, n / 2 + 1); + } + + // Find kth number in two sorted array. k is size + private int findKth(int[] numsA, int startA, int[] numsB, int startB, int k) { + // check edge case for startA/startB index + if (startA >= numsA.length) return numsB[startB + k - 1]; // A exhausted, take kth in B + if (startB >= numsB.length) return numsA[startA + k - 1]; // B exhausted, take kth in A + + // handle final condition k == 1. The two elements will be sorted and the smaller one is kth. + if (k == 1) return Math.min(numsA[startA], numsB[startB]); + + // compare and partition at each [x+(k/2-1)] position + int halfKthOfA = startA + k / 2 - 1 < numsA.length ? numsA[startA + k / 2 - 1] : Integer.MAX_VALUE; + int halfKthOfB = startB + k / 2 - 1 < numsB.length ? numsB[startB + k / 2 - 1] : Integer.MAX_VALUE; + if (halfKthOfA < halfKthOfB) { + return findKth(numsA, startA + k / 2, numsB, startB, k - k / 2); + } else { + return findKth(numsA, startA, numsB, startB + k / 2, k - k / 2); + } } } +``` \ No newline at end of file diff --git a/Java/Median.java b/Java/Median.java old mode 100644 new mode 100755 index 2dac899..fa65897 --- a/Java/Median.java +++ b/Java/Median.java @@ -1,3 +1,19 @@ +E +1525410641 +tags: Quick Sort, Quick Select, Array + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- quickSelect 可以找到 kth 最小的元素 +- 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- quick select 的template要熟悉一下, 一下子可能想得到, 但写不出来 +- 主要步骤: partition, dfs, only recur on one part of the array + + +``` /* Given a unsorted array with integers, find the median of it. @@ -18,6 +34,70 @@ */ + +/* +Thoughts: +Goal: Find the median, which is N/2-th. +Enumerate a few examples and find median index = (nums.length - 1) / 2 + +Quick sort has the nature of putting all smaller items on left, and larger numbers on right. +If that pivot point happends to hit the midian index, that's our target. +We don't necessarily need to sort all items, but just need to locate the median index. +*/ +public class Solution { + /** + * @param nums: A list of integers + * @return: An integer denotes the middle number of the array + */ + public int median(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + if (nums.length == 1) { + return nums[0]; + } + int n = nums.length; + return quickSelect(nums, 0, n - 1, (n - 1)/ 2); + } + + private int quickSelect(int[] nums, int start, int end, int target) { + int index = partition(nums, start, end); + if (index == target) { + return nums[index]; + } else if (index < target) { + return quickSelect(nums, index + 1, end, target); + } else { + return quickSelect(nums, start, index - 1, target); + } + } + + private int partition(int[] nums, int low, int high) { + int pivot = high; // end + int pivotValue = nums[pivot]; + + while (low < high) { + while (low < high && nums[low] < pivotValue) { + low++; + } + while (low < high && nums[high] >= pivotValue) { + high--; + } + swap(nums, low, high); + } + + swap(nums, low, pivot); + + return low; + } + + private void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + + /* Recap 12.09.2015. O(n) means just run through it. It's similar to Partition array: it tries to split the list into 2 parts, and find the pivot. @@ -36,72 +116,52 @@ */ public class Solution { /** - * @param nums: A list of integers. - * @return: An integer denotes the middle number of the array. + * @param nums: A list of integers + * @return: An integer denotes the middle number of the array */ public int median(int[] nums) { if (nums == null || nums.length == 0) { return 0; } - if (nums.length % 2 == 0) { - return helper(nums, 0, nums.length - 1, nums.length/2 - 1); - } else { - return helper(nums, 0, nums.length - 1, nums.length/2); + if (nums.length == 1) { + return nums[0]; } + int n = nums.length; + return quickSort(nums, 0, n - 1, (n - 1)/ 2); } - public void swap(int[] nums, int x, int y){ - int temp = nums[x]; - nums[x] = nums[y]; - nums[y] = temp; - } - - public int helper(int[] nums, int start, int end, int mid) { + private int quickSort(int[] nums, int start, int end, int target) { int pivot = end; - int num = nums[pivot]; + int pivotValue = nums[pivot]; int low = start; int high = end; while (low < high) { - while(low < high && nums[low] < num) { + while (low < high && nums[low] < pivotValue) { low++; } - while(low < high && nums[high] >= num) { + while (low < high && nums[high] >= pivotValue) { high--; } swap(nums, low, high); } + swap(nums, low, pivot); - if (low == mid) { + // dfs + if (low == target) { return nums[low]; - } else if (low < mid) { - return helper(nums, low + 1, end, mid); + } else if (low < target) { + return quickSort(nums, low + 1, end, target); } else { - return helper(nums, start, low - 1, mid); + return quickSort(nums, start, low - 1, target); } } + + private void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } } - - - - - - - - - - - - - - - - - - - - - - - +``` \ No newline at end of file diff --git a/Java/Meeting Rooms II.java b/Java/Meeting Rooms II.java new file mode 100755 index 0000000..b7308af --- /dev/null +++ b/Java/Meeting Rooms II.java @@ -0,0 +1,135 @@ +M +1521167295 +tags: Heap, Greedy, Sort, Sweep Line, PriorityQueue + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + +``` +/* +Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), +find the minimum number of conference rooms required. + +For example +Given [[0, 30],[5, 10],[15, 20]], +return 2. + +Tags: Heap Greedy Sort +Similar Problems: (H) Merge Intervals, (E) Meeting Rooms +*/ + + +/** + * Definition for an interval. + * public class Interval { + * int start; + * int end; + * Interval() { start = 0; end = 0; } + * Interval(int s, int e) { start = s; end = e; } + * } + */ +/* +Thoughts: +Counts the num of concurrent meetings. +Use sweep line to modle the inverval into time Points, where 1 means start of meeting and -1 means end of meeting. +Sort all points by time stamp, and accumulate +1, -1 +Mark the max of count +*/ +class Solution { + class Point { + int pos, flag; + public Point(int pos, int flag) { + this.pos = pos; + this.flag = flag; + } + } + public int minMeetingRooms(Interval[] intervals) { + if (intervals == null || intervals.length == 0) return 0; + + int count = 0, max = 0; + // init + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(a -> a.pos)); + for (Interval interval: intervals) { + queue.offer(new Point(interval.start, 1)); + queue.offer(new Point(interval.end, -1)); + } + // process + while (!queue.isEmpty()) { + Point curr = queue.poll(); + count += curr.flag; + while (!queue.isEmpty() && curr.pos == queue.peek().pos) { + curr = queue.poll(); + count += curr.flag; + } + + max = Math.max(count, max); + } + return max; + } +} + +/* +Thoughts: This seems to be: how many concurrent meetings do we have? +Just return the count that we used in Meeting I. + +Though, instead of Prority queue + Point class, let me try to use just array and a hashmap. + +Using HashMap is tricky in this: +Overallpping spot will be put on the same hashmap key. Be careful with handling the overlapping. +Here, I decide to merge the edge when generate the map, so during the count check, need to skip +duplicated time spot to prevent incorrectly re-use of certain time spot. + + +*/ + +/** + * Definition for an interval. + * public class Interval { + * int start; + * int end; + * Interval() { start = 0; end = 0; } + * Interval(int s, int e) { start = s; end = e; } + * } + */ +public class Solution { + public int minMeetingRooms(Interval[] intervals) { + if (intervals == null || intervals.length == 0) { + return 0; + } + int[] timeSpot = new int[intervals.length * 2]; + HashMap map = new HashMap(); + for (int i = 0; i < intervals.length; i++) { + timeSpot[i] = intervals[i].start; + timeSpot[intervals.length + i] = intervals[i].end; + if (map.containsKey(intervals[i].start)) { + map.put(intervals[i].start, map.get(intervals[i].start) + 1); + } else { + map.put(intervals[i].start, 1); + } + if (map.containsKey(intervals[i].end)) { + map.put(intervals[i].end, map.get(intervals[i].end) - 1); + } else { + map.put(intervals[i].end, -1); + } + } + Arrays.sort(timeSpot); + int count = 0; + int max = 0; + for (int i = 0; i < timeSpot.length; i++) { + count += map.get(timeSpot[i]); + while (i + 1 < timeSpot.length && timeSpot[i] == timeSpot[i + 1]) { + i++; + } + max = Math.max(count, max); + } + return max; + } +} + +``` \ No newline at end of file diff --git a/Java/Meeting Rooms.java b/Java/Meeting Rooms.java new file mode 100755 index 0000000..8a8a458 --- /dev/null +++ b/Java/Meeting Rooms.java @@ -0,0 +1,132 @@ +E +1521097808 +tags: Sort, Sweep Line, PriorityQueue + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### 方法1: +找是否有overlap. priorityQueue 按照start time排序好以后, 比较current和peek: current.end > peek.start? + +#### 方法2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 + +``` +/* +Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), +determine if a person could attend all meetings. + +For example, +Given [[0, 30],[5, 10],[15, 20]], +return false. + +Hide Company Tags Facebook +Hide Tags Sort +Hide Similar Problems (H) Merge Intervals (M) Meeting Rooms II + +*/ + +/** + * Definition for an interval. + * public class Interval { + * int start; + * int end; + * Interval() { start = 0; end = 0; } + * Interval(int s, int e) { start = s; end = e; } + * } + */ +/* +Thoughts: +Cannot have overlap -> sort by the interval.start using priority queue +*/ +// Check over lap, sorting by priority queue +class Solution { + public boolean canAttendMeetings(Interval[] intervals) { + if (intervals == null || intervals.length == 0) { + return true; + } + PriorityQueue queue = new PriorityQueue(new Comparator() { + public int compare(Interval a, Interval b) { + return a.start - b.start; + } + }); + + // Sort + for (Interval interval: intervals) { + queue.offer(interval); + } + + // Compare tail to head + while (!queue.isEmpty()) { + Interval head = queue.poll(); + Interval next = queue.peek(); + if (next != null && head.end > next.start) { // overlap happens + return false; + } + } + + return true; + } +} + +/** + * Definition for an interval. + * public class Interval { + * int start; + * int end; + * Interval() { start = 0; end = 0; } + * Interval(int s, int e) { start = s; end = e; } + * } + */ + +/* +Thought: +Use Sweep Line. +Note: special care for edge point: make sure to process all connecting point before shuouting the result. +*/ + + //use Sweep Line +public class Solution { + class Point { + int pos, flag; + public Point(int pos, int flag) { + this.pos = pos; + this.flag = flag; + } + } + public boolean canAttendMeetings(Interval[] intervals) { + if (intervals == null || intervals.length == 0) { + return true; + } + + // Prepare sweep line points + PriorityQueue queue = new PriorityQueue(2, new Comparator() { + public int compare(Point p1, Point p2) { + return p1.pos - p2.pos; + } + }); + for (int i = 0; i < intervals.length; i++) { + queue.offer(new Point(intervals[i].start, 1)); + queue.offer(new Point(intervals[i].end, -1)); + } + + int count = 0; // count how many meeting happening concurrently + while (!queue.isEmpty()) { + Point p = queue.poll(); + count += p.flag; + + // For all point marked on x (overlap with point), check the flag status + while (!queue.isEmpty() && p.pos == queue.peek().pos) { + p = queue.poll(); + count += p.flag; + } + if (count > 1) { + return false; + } + } + + return true; + } +} +``` \ No newline at end of file diff --git a/Java/Merge Intervals.java b/Java/Merge Intervals.java old mode 100644 new mode 100755 index 518bf89..3e3cbc3 --- a/Java/Merge Intervals.java +++ b/Java/Merge Intervals.java @@ -1,73 +1,249 @@ +M +1531077570 +tags: Array, Sort, Sweep Line, PriorityQueue + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + /* + // old comparator + new Comparator(){ + public int compare(obj1, obj2) { + return obj1.x - obj2.x; + } + } + // simplier way to define comparator + Comparator.comparing(p -> p.val) + + // example + intervals.sort(Comparator.comparing(interval -> interval.start)); +*/ + +``` +/* Given a collection of intervals, merge all overlapping intervals. -Example -Given intervals => merged intervals: +For example, +Given [1,3],[2,6],[8,10],[15,18], +return [1,6],[8,10],[15,18]. -[ [ - [1, 3], [1, 6], - [2, 6], => [8, 10], - [8, 10], [15, 18] - [15, 18] ] -] -Challenge -O(n log n) time and O(1) extra space. +Tags: Array, Sort +Similar Problems: (H) Insert Interval, (E) Meeting Rooms (M) Meeting Rooms II -Tags Expand -Sort Array +*/ +// sweep line, extra space used +class Solution { + class Point { + int val, flag; + public Point(int val, int flag) { + this.val = val; + this.flag = flag; + } + } -Thoughts: -1. use comparator to sort list. Well, no need to create a new class. Just do it inline. -2. iterate through the list and merge (whenever there is overlap) + public List merge(List intervals) { + List rst = new ArrayList<>(); + if (intervals == null || intervals.size() == 0) { + return rst; + } + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(p -> p.val)); + + for (Interval interval : intervals) { + queue.offer(new Point(interval.start, 1)); + queue.offer(new Point(interval.end, -1)); + } + + int count = 0; + Interval interval = new Interval(); + while (!queue.isEmpty()) { + Point p = queue.poll(); + if (count == 0) {//detect start + interval.start = p.val; + } + count += p.flag; + //proces all points on same position p.val + while (!queue.isEmpty() && p.val == queue.peek().val) { + p = queue.poll(); + count += p.flag; + } + if (count == 0) {//detect end + interval.end = p.val; + rst.add(interval); + interval = new Interval(); + } + } + + return rst; + } +} +/* + Space O(1), time: O(nlogn) -Review: -List: size(), get(..), remove(..) -Comparator + Sort by start time. + then it overlaps: check on pre.end and curr.start. + if overlaps: curr.start will be overlapped; also check on curr.end and pre.end, decide who ends this interval + + border case: null, return itself; or length==1, return. */ +class Solution { + public List merge(List intervals) { + if (intervals == null || intervals.size() <= 1) { + return intervals; + } + intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn) + int i = 0; + while(i < intervals.size() - 1) { + Interval curr = intervals.get(i), next = intervals.get(i + 1); + if (curr.end >= next.start) { + curr.end = curr.end >= next.end ? curr.end : next.end; + intervals.remove(i + 1); + continue; + } + i++; + } + return intervals; + } +} + +/* +Thoughts: +Again use Sweep Line. Quite similar to Meeting Rooms, flight schedules... etc +This one: when count ==0, make sure to keep track start and end, add into the rst +When writing out example, whenever count==0, that indicates an end of a interval. + +HOWEVER, this uses O(n) space, while this problem requests O(1) space +*/ /** - * Definition of Interval: + * Definition for an interval. * public class Interval { - * int start, end; - * Interval(int start, int end) { - * this.start = start; - * this.end = end; - * } + * int start; + * int end; + * Interval() { start = 0; end = 0; } + * Interval(int s, int e) { start = s; end = e; } + * } */ +public class Solution { + class Point{ + int x, flag; + public Point(int x, int flag) { + this.x = x; + this.flag = flag; + } + } + public List merge(List intervals) { + List rst = new ArrayList(); + if (intervals == null || intervals.size() == 0) { + return rst; + } + PriorityQueue queue = new PriorityQueue(2, new Comparator() { + public int compare(Point p1, Point p2) { + return p1.x - p2.x; + } + }); + + for (Interval entry : intervals) { + queue.offer(new Point(entry.start, 1)); + queue.offer(new Point(entry.end, -1)); + } + + int count = 0; + int start = 0; + int end = 0; + while (!queue.isEmpty()) { + Point p = queue.poll(); + if (count == 0) {//detect start + start = p.x; + } + count += p.flag; + while (!queue.isEmpty() && p.x == queue.peek().x) {//proces all points on same position x + p = queue.poll(); + count += p.flag; + } + if (count == 0) {//detect end + end = p.x; + rst.add(new Interval(start, end)); + } + } + + return rst; + } +} +// extra space used for rst O(n) class Solution { - /** - * @param intervals: Sorted interval list. - * @return: A new sorted interval list. - */ public List merge(List intervals) { + List rst = new ArrayList<>(); if (intervals == null || intervals.size() == 0) { - return intervals; + return rst; + } + // Sort intervals + intervals.sort(Comparator.comparing(i -> i.start)); + + Interval last = null; + for (Interval item : intervals) { + if (last == null || last.end < item.start) {// Found on item, add. + rst.add(item); + last = item; + } else { + // Keep the last item to have longest end index possible, + // so to merge all visited index + last.end = Math.max(last.end, item.end); + } } - // intervals.sort(new CustomComparator()); - Collections.sort(intervals, new Comparator(){ - public int compare(Interval a, Interval b){ - return a.start - b.start; - } - }); - //Merge - Interval pre = intervals.get(0); - Interval curr = null; + return rst; + } +} + + +class Solution { + public List merge(List intervals) { + if (intervals == null || intervals.size() <= 1) { + return intervals; + } + + intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn) + Interval prev = intervals.get(0); + Interval curr; + for (int i = 1; i < intervals.size(); i++) { - curr = intervals.get(i); - if (pre.end >= curr.start) { - pre.end = pre.end > curr.end ? pre.end : curr.end; - intervals.remove(i); - i--; - } else { - pre = curr; - } + curr = intervals.get(i); + if (prev.end >= curr.start) { + prev.end = prev.end >= curr.end ? prev.end : curr.end; + intervals.remove(i); + i--; + } else { + prev = curr; + } } - return intervals; } } + + +``` diff --git a/Java/Merge Sorted Array II.java b/Java/Merge Sorted Array II.java old mode 100644 new mode 100755 index 24c2efe..adeb4fa --- a/Java/Merge Sorted Array II.java +++ b/Java/Merge Sorted Array II.java @@ -1,7 +1,15 @@ -长度已经固定。普通做法。 +E +1528179898 +tags: Array + +如题, merge two sorted array into 新的 sorted array + +- 长度已经固定. Basic Implementation +- 如果一个array足够大, merge into this array, 那么就是从末尾merge. + ``` /* -33% Accepted + Merge two given sorted integer array A and B into a new sorted integer array. Example @@ -26,10 +34,6 @@ */ class Solution { - /** - * @param A and B: sorted integer array A and B. - * @return: A new sorted integer array - */ public int[] mergeSortedArray(int[] A, int[] B) { if (A == null || B == null) { return A == null ? B : A; diff --git a/Java/Merge Sorted Array.java b/Java/Merge Sorted Array.java old mode 100644 new mode 100755 index 2fdf188..4e123ca --- a/Java/Merge Sorted Array.java +++ b/Java/Merge Sorted Array.java @@ -1,20 +1,121 @@ -A够长,那么可以从A的尾部开始加新元素。 -注意,从尾部,是大数字优先的。 +E +1533441519 +tags: Array, Two Pointers + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素。 +- 注意,从尾部,是大数字优先排末尾的. + ``` /* -Given two sorted integer arrays A and B, merge B into A as one sorted array. +Given two sorted integer arrays nums1 and nums2, +merge nums2 into nums1 as one sorted array. + +Note: +You may assume that nums1 has enough space (size that is greater or equal to m + n) +to hold additional elements from nums2. + +The number of elements initialized in nums1 and nums2 are m and n respectively. -Note -You may assume that A has enough space (size that is greater or equal to m + n) to hold additional elements from B. The number of elements initialized in A and B are mand n respectively. +Example: -Example -A = [1, 2, 3, empty, empty] B = [4,5] +Input: +nums1 = [1,2,3,0,0,0], m = 3 +nums2 = [2,5,6], n = 3 + +Output: [1,2,2,3,5,6] +Hide Company Tags Bloomberg Facebook +Hide Tags Array Two Pointers +Hide Similar Problems (E) Merge Two Sorted Lists + +*/ +// two pointer moving +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { + return; + } + m--; n--; + while (m >= 0 || n >= 0) { + if (m >= 0 && n >= 0) { + nums1[m + n + 1] = nums1[m] > nums2[n] ? nums1[m--] : nums2[n--]; + } else if (m < 0) { // n > 0 + nums1[n] = nums2[n--]; + } else if (n < 0) { + break; + } + } + } +} -After merge, A will be filled as [1,2,3,4,5] +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { + return; + } + int end = m + n - 1; + m--; + n--; + for (int i = end; i >= 0; i--) { + if (m >= 0 && n >= 0) { + nums1[i] = nums1[m] >= nums2[n] ? nums1[m--] : nums2[n--]; + } else { + nums1[i] = m >= 0 ? nums1[m--] : nums2[n--]; + } + } + } +} -Tags Expand -Array Sorted Array +/* +Given enough space in nums1, and given size of the two array, +we can start labling the last element, since it's empty-safe indexes anyway. */ +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { + return; + } + int pos1 = m - 1; + int pos2 = n - 1; + for (int i = m + n - 1; i >= 0; i--) { + //Handle remaining of nums1 or nums2 + if (pos1 < 0 || pos2 < 0) { + nums1[i] = pos1 < 0 ? nums2[pos2--] : nums1[pos1--]; + } else { + if (nums1[pos1] >= nums2[pos2]) { + nums1[i] = nums1[pos1--]; + } else { + nums1[i] = nums2[pos2--]; + } + } + } + } +} + +/*Recap 02.17.2015*/ +//merge from m + n position +public class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + if (nums1 == null || nums2 == null) { + return; + } + int ind1 = m - 1; + int ind2 = n - 1; + for (int i = m + n - 1; i >= 0; i--) { + if ((ind1 >= 0 && ind2 >= 0 && nums1[ind1] >= nums2[ind2]) + || (ind1 >= 0 && ind2 < 0)) { + nums1[i] = nums1[ind1]; + ind1--; + } else if ((ind1 >= 0 && ind2 >= 0 && nums1[ind1] < nums2[ind2]) + || (ind1 < 0 && ind2 >= 0)) { + nums1[i] = nums2[ind2]; + ind2--; + } + } + } +} /* Thinking process: @@ -23,12 +124,6 @@ You may assume that A has enough space (size that is greater or equal to m + n) 3. Make sure to clean up the second array B. */ class Solution { - /** - * @param A: sorted integer array A which has m elements, - * but size of A is m+n - * @param B: sorted integer array B which has n elements - * @return: void - */ public void mergeSortedArray(int[] A, int m, int[] B, int n) { // write your code here int index = m + n; @@ -47,4 +142,4 @@ public void mergeSortedArray(int[] A, int m, int[] B, int n) { } -``` \ No newline at end of file +``` diff --git a/Java/Merge Two Binary Trees.java b/Java/Merge Two Binary Trees.java new file mode 100644 index 0000000..f306c67 --- /dev/null +++ b/Java/Merge Two Binary Trees.java @@ -0,0 +1,95 @@ +E +1525670223 +tags: DFS, Tree + +#### DFS +- 基础binary tree traversal. 注意对null child的判断 + +``` +/* +Given two binary trees and imagine that when you put one of them to cover the other, +some nodes of the two trees are overlapped while the others are not. + +You need to merge them into a new binary tree. +The merge rule is that if two nodes overlap, +then sum node values up as the new value of the merged node. +Otherwise, the NOT null node will be used as the node of new tree. + +Example 1: +Input: + Tree 1 Tree 2 + 1 2 + / \ / \ + 3 2 1 3 + / \ \ + 5 4 7 +Output: +Merged tree: + 3 + / \ + 4 5 + / \ \ + 5 4 7 +Note: The merging process must start from the root nodes of both trees. +*/ + + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +// Same idea, differnet way to write +class Solution { + public TreeNode mergeTrees(TreeNode t1, TreeNode t2) { + if (t1 == null && t2 == null) { + return null; + } + TreeNode node = new TreeNode(0); + node.val += t1 == null ? 0 : t1.val; + node.val += t2 == null ? 0 : t2.val; + TreeNode t1LeftChild = t1 == null ? null : t1.left; + TreeNode t2LeftChild = t2 == null ? null : t2.left; + TreeNode t1RightChild = t1 == null ? null : t1.right; + TreeNode t2RightChild = t2 == null ? null : t2.right; + + node.left = mergeTrees(t1LeftChild, t2LeftChild); + node.right = mergeTrees(t1RightChild, t2RightChild); + + return node; + } +} + +/* +Thoughts: +1. Regular traversal. Always pass t1, t2. +2. Chech closing conditions: thinking from the leaf's perspective where it stops the resersive calls +*/ +class Solution { + public TreeNode mergeTrees(TreeNode t1, TreeNode t2) { + if (t1 == null && t2 == null) { + return null; + } + final TreeNode node = new TreeNode(0); + if (t2 == null) { + node.val += t1.val; + node.left = mergeTrees(t1.left, null); + node.right = mergeTrees(t1.right, null); + } else if (t1 == null) { + node.val += t2.val; + node.left = mergeTrees(null, t2.left); + node.right = mergeTrees(null, t2.right); + } else { + node.val = t1.val + t2.val; + node.left = mergeTrees(t1.left, t2.left); + node.right = mergeTrees(t1.right, t2.right); + } + return node; + } +} +``` \ No newline at end of file diff --git a/Java/Merge Two Sorted Lists.java b/Java/Merge Two Sorted Lists.java new file mode 100755 index 0000000..3e7d2b7 --- /dev/null +++ b/Java/Merge Two Sorted Lists.java @@ -0,0 +1,116 @@ +E +1525761649 +tags: Linked List + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + +``` +/* +Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. + +Example +Given 1->3->8->11->15->null, 2->null , return 1->2->3->8->11->15->null + +Tags Expand +Linked List + +Thinking process: +1. Merge sorted list, compare before add to node.next +2. when any of l1 or l2 is null, break out. +3. add the non-null list at the end of node. +4. return dummy.next. + +*/ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null || l2 == null) { + return l1 == null ? l2 : l1; + } + final ListNode head = new ListNode(0); + ListNode node = head; + + while (l1 != null && l2 != null) { + if (l1.val <= l2.val) { + node.next = l1; + l1 = l1.next; + } else { + node.next = l2; + l2 = l2.next; + } + node = node.next; + } + + if (l1 != null) { + node.next = l1; + } else if (l2 != null) { + node.next = l2; + } + return head.next; + } +} + +/* +Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. + +Tags: Linked List +Similar Problems: (H) Merge k Sorted Lists, (E) Merge Sorted Array, (M) Sort List, (M) Shortest Word Distance II + +*/ + +/* +Thouhts: +Loop throgh both list. Make sure to check the border cases +*/ + + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +public class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null && l2 == null) { + return null; + } + ListNode node = new ListNode(0); + ListNode dummy = node; + while (l1 != null || l2 != null) { + if (l1 == null) { + node.next = l2; + break; + } else if (l2 == null) { + node.next = l1; + break; + } else { + if (l1.val < l2.val) { + node.next = l1; + l1 = l1.next; + } else { + node.next = l2; + l2 = l2.next; + } + node = node.next; + } + }//end while + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Java/Merge k Sorted Arrays.java b/Java/Merge k Sorted Arrays.java old mode 100644 new mode 100755 index 2a64dcc..46f532a --- a/Java/Merge k Sorted Arrays.java +++ b/Java/Merge k Sorted Arrays.java @@ -1,10 +1,19 @@ -由Merge k linked list想到,有办法找到每个node的sibling, 而int[]又不行,所以: -自己建立一个class 来存放必要信息 +M +1533139060 +tags: Heap, PriorityQueue, MinHeap + +Same as merge k sorted list, use priorityQueue + +#### Priority Queue +- 由Merge k sorted list启发。用PriorityQueue,存那k个首发element +- PriorityQueue需要存储单位: 自己建一个Class Node 存val, x, y index. +- 因为array里没有 'next' pointer,只能存x,y来推next element +- Not sure why `new PriorityQueue<>(Comparator.comparing(a -> a.val));` is slower + ``` /* Given k sorted integer arrays, merge them into one sorted array. -Have you met this question in a real interview? Yes Example Given 3 sorted arrays: @@ -23,8 +32,57 @@ Do it in O(N log k). Tags Expand Heap Priority Queue */ +//LintCode, return int[] +public class Solution { + public class Node { + int val, x, y; + public Node(int val, int x, int y) { + this.val = val; + this.x = x; + this.y = y; + } + } + + public int[] mergekSortedArrays(int[][] arrays) { + List rst = new ArrayList<>(); + if (arrays == null || arrays.length == 0) return new int[0]; + + + // Faster + // Somehow, slower: PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(a -> a.val)); + PriorityQueue queue = new PriorityQueue<>(arrays.length, + new Comparator() { + public int compare(Node a, Node b){ + return a.val - b.val; + } + } + ); + + //init + for (int i = 0; i < arrays.length; i++) { + if (arrays[i].length != 0) { + queue.offer(new Node(arrays[i][0], i, 0)); + } + } + + while (!queue.isEmpty()) { + Node node = queue.poll(); + rst.add(node.val); + if (node.y < arrays[node.x].length - 1) { + queue.offer(new Node(arrays[node.x][node.y + 1], node.x, node.y + 1)); + } + } + + int[] arrayResult = new int[rst.size()]; + for (int i = 0; i < arrayResult.length; i++) { + arrayResult[i] = rst.get(i); + } + return arrayResult; + } +} /* +LintCode, return list Thoughts: 12.10.2015 Since we can't know the node's sibiling, as creating the prirority queue, let's create a Node{val, x, y} @@ -80,18 +138,4 @@ public int compare(Node a, Node b){ } - - - - - - - - - - - - - - ``` \ No newline at end of file diff --git a/Java/Merge k Sorted Lists.java b/Java/Merge k Sorted Lists.java old mode 100644 new mode 100755 index c1b2e19..5d691e4 --- a/Java/Merge k Sorted Lists.java +++ b/Java/Merge k Sorted Lists.java @@ -1,66 +1,141 @@ -用Priorityqueue来排列所有list的头头。 -非常正规的。 - -记得k lists 需要是已经sort好的。 - -时间:n*O(logk) -PriorityQueue: logk -这个题目可以有好几个衍生: - -比如,如果k很大,一个机器上放不下所有的k list怎么办? -比如,如果Merge起来的很长,一个机器上放不下怎么办? -``` -/* -22% 通过 -Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. - -样例 -标签 Expand -Linked List Divide and Conquer Heap - -*/ - -/* - 12.10.2015 recap - Use queue to store the head of k lists. - First init with all heads. - Because the ListNode always has a link to its next sibiling, so it's easy to add that sibling back to queue. -*/ -public class Solution { - public ListNode mergeKLists(List lists) { - if (lists == null || lists.size() == 0) { - return null; - } - PriorityQueue queue = - new PriorityQueue(lists.size(), new Comparator(){ - public int compare(ListNode a, ListNode b){ - return a.val - b.val; - } - }); - - //populate queue with k lists' header - for (int i = 0; i < lists.size(); i++) { - if (lists.get(i) != null) { - queue.offer(lists.get(i)); - } - } - - ListNode dummy = new ListNode(0); - ListNode node = dummy; - while (!queue.isEmpty()) { - ListNode curr = queue.poll(); - node.next = curr; - - if (curr.next != null) { - queue.offer(curr.next); - } - - node = node.next; - } - - return dummy.next; - } -} - - +M +1533138747 +tags: Linked List, Divide and Conquer, Heap, PriorityQueue + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- 记得k lists 需要是已经sort好的 +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: +- 1. 不要忘记customized priority需要一个customized new Comparator() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + +``` + +/* + +Merge k sorted linked lists and return it as one sorted list. +Analyze and describe its complexity. + + +Example +Given lists: + +[ + 2->4->null, + null, + -1->null +], +return -1->2->4->null. + +Input: +[ + 1->4->5, + 1->3->4, + 2->6 +] +Output: 1->1->2->3->4->4->5->6 + +Tags Expand + +*/ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +class Solution { + public ListNode mergeKLists(ListNode[] lists) { + if (lists == null || lists.length == 0) { + return null; + } + // Initialize the priority queue with customized comparator + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(node -> node.val)); + for (int i = 0; i < lists.length; i++) { + if (lists[i] != null) { + queue.offer(lists[i]); + } + } + if (queue.isEmpty()) return null; + + // Append the priority queue with all items + ListNode dummy = new ListNode(0); + ListNode head = dummy; + while (!queue.isEmpty()) { + ListNode node = queue.poll(); + if (node.next != null) { + queue.offer(node.next); + } + head.next = node; // link + head = head.next; + } + return dummy.next; + } +} + + +/* + 12.10.2015 recap + Use queue to store the head of k lists. + First init with all heads. + Because the ListNode always has a link to its next sibiling, so it's easy to add that sibling back to queue. + time: m * Log(k) +*/ +public class Solution { + public ListNode mergeKLists(List lists) { + if (lists == null || lists.size() == 0) { + return null; + } + PriorityQueue queue = + new PriorityQueue(lists.size(), new Comparator(){ + public int compare(ListNode a, ListNode b){ + return a.val - b.val; + } + }); + + //populate queue with k lists' header + for (int i = 0; i < lists.size(); i++) { + if (lists.get(i) != null) { + queue.offer(lists.get(i)); + } + } + + ListNode dummy = new ListNode(0); + ListNode node = dummy; + while (!queue.isEmpty()) { + ListNode curr = queue.poll(); + node.next = curr; + + if (curr.next != null) { + queue.offer(curr.next); + } + + node = node.next; + } + + return dummy.next; + } +} + + ``` \ No newline at end of file diff --git a/Java/MergeSort.java b/Java/MergeSort.java new file mode 100644 index 0000000..0677287 --- /dev/null +++ b/Java/MergeSort.java @@ -0,0 +1,100 @@ +M +1526363774 +tags: Sort, Merge Sort + +#### Merge Sort +- Divide and conquer, recursively +- 先从中间分段, merge sort 左边 (dfs), merge sort 右边 +- 最后merge起来 +- merge的时候因为是做int[], 所以没办法必须要O(n) space +- Time O(nlogn), Space O(n) + +``` +/* +Merge Sort +*/ + +public class Solution { + + private void mergeSort(int[] nums) { + if (nums == null || nums.length == 0) { + return; + } + mergeSort(nums, new int[nums.length], 0, nums.length - 1); + } + + private void mergeSort(int[] nums, int[] temp, int start, int end) { + if (start >= end) { + return; + } + int mid = start + (end - start) / 2; + mergeSort(nums, temp, start, mid); + mergeSort(nums, temp, mid + 1, end); + merge(nums, temp, start, end); + } + + private void merge(int[] nums, int[] temp, int start, int end) { + int leftEnd = start + (end - start) / 2; // mid point + int rightStart = leftEnd + 1; + int size = end - start + 1; + + int i = start; + int j = rightStart; + int index = start; + + while (i <= leftEnd && j <= end) { + if (nums[i] <= nums[j]) { + temp[index] = nums[i]; + i++; + } else { + temp[index] = nums[j]; + j++; + } + index++; + } + + // append the remaining array. + // arraycopy: copy from array[i] to temp[index] for (x) items + System.arraycopy(nums, i, temp, index, leftEnd - i + 1); + System.arraycopy(nums, j, temp, index, end - j + 1); + System.arraycopy(temp, start, nums, start, end - start + 1); + } + + private void printArray(int[] nums, String str) { + System.out.print(str); + if (nums == null || nums.length == 0) { + System.out.println(); + return; + } + for (int num : nums) { + System.out.print(num + ", "); + } + System.out.println(); + } + + public static void main(String[] args) { + Solution sol = new Solution(); + System.out.println("Implement and test quick sort"); + + int[] array = {5,3,2,7,5,4,1,1,2,9,5,3, -1}; + sol.mergeSort(array); + sol.printArray(array, "Return: "); + + array = null; + sol.mergeSort(array); + sol.printArray(array, "Return Empty: "); + + array = new int[]{ - 1, 2}; + sol.mergeSort(array); + sol.printArray(array, "Return: "); + + array = new int[]{ 1,1,1,1,1}; + sol.mergeSort(array); + sol.printArray(array, "Return: "); + + array = new int[]{ 19,8,7,6,5,4,3,2,1}; + sol.mergeSort(array); + sol.printArray(array, "Return: "); + } +} +``` \ No newline at end of file diff --git a/Java/Middle of Linked List.java b/Java/Middle of Linked List.java old mode 100644 new mode 100755 index 9ba77bd..2311345 --- a/Java/Middle of Linked List.java +++ b/Java/Middle of Linked List.java @@ -1,10 +1,13 @@ -快慢指针 +E +1525413850 +tags: Linked List -不在乎slow是不是到底,因为fast肯定先到。 -确保fast, fast.next不是Null就好 +找Linked List的中间node +- 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 -return slow ``` /* Find the middle node of a linked list. diff --git a/Java/Min Stack.java b/Java/Min Stack.java old mode 100644 new mode 100755 index 4dd69c5..721a318 --- a/Java/Min Stack.java +++ b/Java/Min Stack.java @@ -1,3 +1,12 @@ +E +1520797111 +tags: Stack, Design + +双Stack:一个正常stack,另一个minStack存当下level最小值. 注意维护minStack的变化 + +另外. 如果要maxStack,也是类似做法 + +``` /* Implement a stack with min() function, which will return the smallest number in the stack. @@ -28,26 +37,74 @@ public class MinStack { private Stack stack; private Stack minStack; public MinStack() { - stack = new Stack(); - minStack = new Stack(); + stack = new Stack(); + minStack = new Stack(); } public void push(int number) { - stack.push(number); - if (minStack.isEmpty()) { - minStack.push(number); - } else { - minStack.push(minStack.peek() >= number ? number : minStack.peek()); - } + stack.push(number); + if (minStack.isEmpty()) { + minStack.push(number); + } else { + minStack.push(minStack.peek() >= number ? number : minStack.peek()); + } } public int pop() { - minStack.pop(); - return stack.pop(); + minStack.pop(); + return stack.pop(); } public int min() { - return minStack.peek(); + return minStack.peek(); + } +} + +/* + Regular stack and minStack. + MinStack keeps the minnimum value at current time (there can be duplicates if min value does not change) +*/ +class MinStack { + Stack stack; + Stack minStack; + /** initialize your data structure here. */ + public MinStack() { + stack = new Stack(); + minStack = new Stack(); + } + + public void push(int x) { + if (minStack.isEmpty() || getMin() > x) { + minStack.push(x); + } else { + minStack.push(getMin()); + } + stack.push(x); + } + + public void pop() { + if (!stack.isEmpty()) { + stack.pop(); + minStack.pop(); + } + } + + public int top() { + return stack.peek(); + } + + public int getMin() { + return minStack.peek(); } } +/** + * Your MinStack object will be instantiated and called as such: + * MinStack obj = new MinStack(); + * obj.push(x); + * obj.pop(); + * int param_3 = obj.top(); + * int param_4 = obj.getMin(); + */ + +``` \ No newline at end of file diff --git a/Java/Minimum Absolute Difference in BST.java b/Java/Minimum Absolute Difference in BST.java new file mode 100644 index 0000000..809abfc --- /dev/null +++ b/Java/Minimum Absolute Difference in BST.java @@ -0,0 +1,77 @@ +E +tags: BST + +BST: inorder-traversal: 先left node(adding to stack till left leav), 再process stack.peek (mid node), 再 add rightNode && dive to rightNode.left leaf + +``` +/* +Given a binary search tree with non-negative values, find the minimum absolute difference between values of any two nodes. + +Example: + +Input: + + 1 + \ + 3 + / + 2 + +Output: +1 + +Explanation: +The minimum absolute difference is 1, which is the difference between 2 and 1 (or between 2 and 3). +Note: There are at least two nodes in this BST. +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Thoughts: +Given BST, we can in-order traverse the nodes and keep note of the minDiff. +*/ +class Solution { + public int getMinimumDifference(TreeNode root) { + if (root == null) { + return 0; + } + int minDiff = Integer.MAX_VALUE; + + final Stack stack = new Stack<>(); + TreeNode node = root; + //Initialize: dive deep to left leaf + while (node != null) { + stack.push(node); + node = node.left; + } + + node = stack.peek(); + TreeNode lastNode = node; + while (!stack.isEmpty()) { + // Mid: take mid, calculate diff based on lastNode + node = stack.pop(); + if (node.val != lastNode.val) { + minDiff = Math.min(minDiff, Math.abs(node.val - lastNode.val)); + } + lastNode = node; + // Right: stack right node, and attempt to all rightNode.left tll the leaf + if (node.right != null) { + node = node.right; + while (node != null) { + stack.push(node); + node = node.left; + } + } + } + return minDiff; + } +} +``` \ No newline at end of file diff --git a/Java/Minimum Depth of Binary Tree.java b/Java/Minimum Depth of Binary Tree.java new file mode 100755 index 0000000..42b0b85 --- /dev/null +++ b/Java/Minimum Depth of Binary Tree.java @@ -0,0 +1,134 @@ +E +1525669253 +tags: Tree, DFS, BFS + +#### BFS +- Shortest path; minimum depth: 想到BFS, check level by level, BFS更能确保更快找到结果 +- depth definition: reach to a leaf node, where node.left == null && node.right == null +- BFS using queue, track level. + + +#### DFS +- Divide and Conquery一个最小值. +- 注意处理Leaf的null: null leaf 出现的时候, 就忽略这个leaf, 直接return算有leaf +- 另一种count的方法: 用Integer.MAX_VALUE代替 null leaf,这样可以避免错误counting. (不能直接recursive) +- 这个无论如何都要走所有node, 所以dfs应该比较适合. + + +``` +/* +Given a binary tree, find its minimum depth. + +The minimum depth is the number of nodes along the shortest path +from the root node down to the nearest leaf node. + +Example +Given a binary tree as follow: + + 1 + + / \ + + 2 3 + + / \ + + 4 5 + +The minimum depth is 2 + +Tags Expand +Depth First Search +*/ + +/* +BFS, 99.67% +minimum depth, consider BFS, it reaches the termination point faster: +if any node has null left/right child, that indicates the first leaf +*/ +class Solution { + public int minDepth(TreeNode root) { + if (root == null) return 0; + Queue queue = new LinkedList<>(); + queue.offer(root); + int level = 0; + while (!queue.isEmpty()) { + level++; + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (node.left == null && node.right == null) return level; + if (node.left != null) queue.offer(node.left); + if (node.right != null) queue.offer(node.right); + } + } + return level; + } +} + + +// DFS +class Solution { + public int minDepth(TreeNode root) { + if (root == null) { + return 0; + } else if (root.left == null && root.right == null) { + return 1; + } + + int leftDepth = minDepth(root.left); + int rightDepth = minDepth(root.right); + + if (root.left == null || root.right == null) { + return (root.left == null ? rightDepth : leftDepth) + 1; + } + + return Math.min(leftDepth, rightDepth) + 1; + } +} + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + +/** +Previous notes: +Thinking Process: +Note: a little different from maxDepth: +If 1,#,2: the left route 1-# does not count as a path, so the minDepth is actually 2 in this example. +We need a helper function to calculate the minimum. + */ +public class Solution { + /** + * @param root: The root of binary tree. + * @return: An integer. + */ + //recursive: + public int minDepth(TreeNode root) { + if (root == null) { + return 0; + } + return getMin(root); + } + + public int getMin(TreeNode root) { + if (root == null) { + return Integer.MAX_VALUE; + } + if (root.left == null && root.right == null) { + return 1; + } + return Math.min(getMin(root.left), getMin(root.right)) + 1; + } +} + + +``` \ No newline at end of file diff --git a/Java/Minimum Height Trees.java b/Java/Minimum Height Trees.java new file mode 100755 index 0000000..d87e2e3 --- /dev/null +++ b/Java/Minimum Height Trees.java @@ -0,0 +1,143 @@ +M +tags: BFS, Graph + +#### Graph + BFS +- Build graph `map` +- BFS to find the shortest path: when the neibhbor has the curr node as the only one neighbor, it is leaf. +- record shortest path in Map> as result +- TODO: code it up. + +#### Previous Solution +- removing leaf && edge + +``` +/* +For a undirected graph with tree characteristics, we can choose any node as the root. +The result graph is then a rooted tree. Among all possible rooted trees, +those with minimum height are called minimum height trees (MHTs). +Given such a graph, write a function to find all the MHTs and return a list of their root labels. + +Format +The graph contains n nodes which are labeled from 0 to n - 1. +You will be given the number n and a list of undirected edges (each edge is a pair of labels). + +You can assume that no duplicate edges will appear in edges. +Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges. + +Example 1: + +Given n = 4, edges = [[1, 0], [1, 2], [1, 3]] + + 0 + | + 1 + / \ + 2 3 +return [1] + +Example 2: + +Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]] + + 0 1 2 + \ | / + 3 + | + 4 + | + 5 +return [3, 4] + +Show Hint +Note: + +(1) According to the definition of tree on Wikipedia: +“a tree is an undirected graph in which any two vertices are connected by exactly one path. +In other words, any connected graph without simple cycles is a tree.” + +(2) The height of a rooted tree is the number of edges on the +longest downward path between the root and a leaf. + +Credits: +Special thanks to @peisi for adding this problem and creating all test cases. + +Hide Company Tags Google +Hide Tags Breadth-first Search Graph +Hide Similar Problems (M) Course Schedule (M) Course Schedule II + +*/ + + +/* + Starting from leaf with depth == 1, + remove all leaf, and the edge + Till the end, whatever node left, should be the root. + + * WHen there is only 1,2 nodes remaining. that's the rst. + + Put Node in HahsMap + + Iterative over map till map.size() <= 2 + + border n == 2,1, just returl rst. + edges == null, return null. + edges.length == 1, reutrn list +*/ + +public class Solution { + public List findMinHeightTrees(int n, int[][] edges) { + List rst = new ArrayList(); + if (n == 1) { + rst.add(0); + return rst; + }else if (n == 0 || edges == null || edges.length == 0 || edges.length != n - 1) { + return rst; + } + + //populate map + boolean[] nodes = new boolean[n]; + HashMap> map = new HashMap>(); + for (int i = 0; i < n; i++) { + map.put(i, new ArrayList()); + nodes[i] = true; + } + for (int i = 0; i < edges.length; i++) { + if (!map.get(edges[i][0]).contains(edges[i][1])) { + map.get(edges[i][0]).add(edges[i][1]); + } + if (!map.get(edges[i][1]).contains(edges[i][0])) { + map.get(edges[i][1]).add(edges[i][0]); + } + } + + //Remove list with leng == 1 + Queue queue = new LinkedList(); + while (n > 2) { + for (Map.Entry> entry : map.entrySet()) { + if (entry.getValue().size() == 1) { + queue.offer(entry.getKey()); + } + } + while (!queue.isEmpty()) { + n--; + Integer key = queue.poll(); + nodes[key] = false; + int from = map.get(key).get(0); + map.get(from).remove(key); + map.get(key).remove(0); + + } + } + + //Put remaining into rst + for (int i = 0; i < nodes.length; i++) { + if (nodes[i]) { + rst.add(i); + } + + } + + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/Minimum Path Sum.java b/Java/Minimum Path Sum.java old mode 100644 new mode 100755 index a02831a..46b205b --- a/Java/Minimum Path Sum.java +++ b/Java/Minimum Path Sum.java @@ -1,5 +1,22 @@ +M +1522818163 +tags: Array, DP, Coordinate DP + +#### DP +- Time, Space O(MN) +- 往右下角走, 计算最短的 path sum. 典型的坐标型. +- 注意: init 第一行的时候, 要accumulate dp[0][j - 1] + grid[i][j], 而不是单纯assign grid[i][j] + +#### Rolling Array +- Time O(MN), Space O(1) +- 需要在同一个for loop里面完成initialization, 和使用dp[i][j] +- 原因: dp[i % 2][j] 在被计算出来的时候, 是几乎马上在下一轮是要被用的; 被覆盖前不备用,就白算 +- 如果按照第一种方法, 在开始initialize dp, 看起来固然简单, 但是不方便空间优化 + +``` /* -Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path. +Given a m x n grid filled with non-negative numbers, +find a path from top left to bottom right which minimizes the sum of all numbers along its path. Note You can only move either down or right at any point in time. @@ -7,49 +24,163 @@ Example Tags Expand Dynamic Programming +*/ + +/* +Thoughts: +Can only move down/right, means at any (i, j), the path comes from top/left. +say dp[i][j] is the min path sum, +so dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j] -Thinking process: -1. Check null, lenght == 0 -2. Min Sum = sum of array. Initialization is a bit different, for example: each row element is added up from previous elemenet. (Not simple value assign from given grid) - - Assign (0,0) to grid[0][0] - - Row 1st row and 1st col, add up values -3. f(x,y) = sum of path value. f(x,y) = Math.Min(f(x-1,y), f(x, y-1)) -4. return f(r-1)(c-1) +init: +dp[0][0] = grid[0][0] +dp[0 ~ m][0] and dp[0][0 ~ n] should be accumulated for all [0, 0~n], [0 ~ m, 0] */ +// Clean version: +class Solution { + public int minPathSum(int[][] grid) { + if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) { + return 0; + } + int m = grid.length; + int n = grid[0].length; + int[][] dp = new int[m][n]; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + // Initialize + if (i == 0 && j == 0) { + dp[i][j] = grid[i][j]; + continue; + } + // Calculate DP + int fromUp = i == 0 ? Integer.MAX_VALUE : dp[i - 1][j]; + int fromLeft = j == 0 ? Integer.MAX_VALUE : dp[i][j - 1]; + dp[i][j] = Math.min(fromUp, fromLeft) + grid[i][j]; + + } + } + + return dp[m - 1][n - 1]; + } +} +// Rolling array +class Solution { + public int minPathSum(int[][] grid) { + if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) { + return 0; + } + int m = grid.length; + int n = grid[0].length; + int[][] dp = new int[2][n]; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + // Initialize + if (i == 0 && j == 0) { + dp[i % 2][j] = grid[i][j]; + continue; + } + // Calculate DP + int fromUp = i == 0 ? Integer.MAX_VALUE : dp[(i - 1) % 2][j]; + int fromLeft = j == 0 ? Integer.MAX_VALUE : dp[i % 2][j - 1]; + dp[i % 2][j] = Math.min(fromUp, fromLeft) + grid[i][j]; + + } + } + + return dp[(m - 1) % 2][n - 1]; + } +} -public class Solution { - /** - * @param grid: a list of lists of integers. - * @return: An integer, minimizes the sum of all numbers along its path - */ +// V0: regular DP, cannot do rolling array +class Solution { public int minPathSum(int[][] grid) { + if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) { + return 0; + } + int m = grid.length; + int n = grid[0].length; + int[][] dp = new int[m][n]; + dp[0][0] = grid[0][0]; + // init: + for (int i = 1; i < m; i++) { + dp[i][0] = dp[i - 1][0] + grid[i][0]; + } + for (int j = 1; j < n; j++) { + dp[0][j] = dp[0][j - 1] + grid[0][j]; + } - if (grid == null || grid.length == 0 || grid[0].length == 0) { + // calculate + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[i][j] = Math.min(dp[i - 1][j], dp[i][j - 1]) + grid[i][j]; + } + } + + return dp[m - 1][n - 1]; + } +} + +// V1: Less optimal code, but can be optimized with rolling array +class Solution { + public int minPathSum(int[][] grid) { + if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) { return 0; } - int row = grid.length; - int col = grid[0].length; - int[][] matrix = new int[row][col]; - matrix[0][0] = grid[0][0]; - //Add up for 1st row && 1st col - for (int i = 1; i < row; i++) { - matrix[i][0] = matrix[i - 1][0] + grid[i][0]; - } - for (int j = 1; j < col; j++) { - matrix[0][j] = matrix[0][j - 1] + grid[0][j]; - } - //Evaluate - for (int i = 1; i < row; i++) { - for (int j = 1; j < col; j++) { - matrix[i][j] = Math.min(matrix[i - 1][j], matrix[i][j - 1]) - + grid[i][j]; + int m = grid.length; + int n = grid[0].length; + int[][] dp = new int[m][n]; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + // Initialize + dp[i][j] = grid[i][j]; + if (i == 0 && j == 0) { + continue; + } else if (i == 0 && j > 0) { + dp[i][j] += dp[i][j - 1]; + } else if (i > 0 && j == 0) { + dp[i][j] += dp[i - 1][j]; + } else { // Calculate DP + dp[i][j] += Math.min(dp[i - 1][j], dp[i][j - 1]); + } } } - return matrix[row - 1][col - 1]; + return dp[m - 1][n - 1]; } } +// V1: Rolling array, space: O(n) +class Solution { + public int minPathSum(int[][] grid) { + if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) { + return 0; + } + int m = grid.length; + int n = grid[0].length; + int[][] dp = new int[2][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + // Initialize + dp[i % 2][j] = grid[i][j]; + if (i == 0 && j == 0) { + continue; + } else if (i == 0 && j > 0) { + dp[i % 2][j] += dp[i % 2][j - 1]; + } else if (i > 0 && j == 0) { + dp[i % 2][j] += dp[(i - 1) % 2][j]; + } else { // Calculate DP + dp[i % 2][j] += Math.min(dp[(i - 1) % 2][j], dp[i % 2][j - 1]); + } + } + } + + return dp[(m - 1) % 2][n - 1]; + } +} +``` \ No newline at end of file diff --git a/Java/Minimum Size Subarray Sum.java b/Java/Minimum Size Subarray Sum.java old mode 100644 new mode 100755 index 4e81edd..4b2fc41 --- a/Java/Minimum Size Subarray Sum.java +++ b/Java/Minimum Size Subarray Sum.java @@ -1,11 +1,29 @@ -2 pointer: -一个做base, 每次动一格:i. -一个做前锋,加到满足条件为止。 -Note: 当sum >= s 条件在while里面满足时,end是多一个index的。所以result里面要处理好边缘情况:(end-1) 才是真的末尾位置,然后计算和开头的间隙: -(end - 1) - start + 1; +M +1519835840 +tags: Array, Two Pointers, Binary Search, Subarray +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + ``` /* -Given an array of n positive integers and a positive integer s, find the minimal length of a subarray of which the sum ≥ s. If there isn't one, return -1 instead. +Given an array of n positive integers and a positive integer s, find the minimal length of a subarray of which the sum ≥ s. +If there isn't one, return -1 instead. Example Given the array [2,3,1,2,4,3] and s = 7, the subarray [4,3] has the minimal length under the problem constraint. @@ -17,18 +35,93 @@ If you have figured out the O(n) solution, try coding another solution of which Two Pointers Array */ + /* Thoughts: +Two pointer. +1. Find the end which has sum >= s, move start forward as much as possible until sum < s. +2. Use new start and end to look for next new end. +*/ +public class Solution { + public int minSubArrayLen(int s, int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int start, end, sum; + start = end = sum = 0; + int min = Integer.MAX_VALUE; + while (end < nums.length) { + while (sum < s && end < nums.length) { + sum += nums[end]; + end++; + } + // move start and log any possible min + while (sum >= s && start >= 0) { + min = Math.min(min, end - start); + sum -= nums[start]; + start++; + } + } + return min == Integer.MAX_VALUE ? 0 : min; + } +} + + +/* +Previous notes on O(n) solution +1. Move 'end' for first possbile solution +2. Store it and remove one 'start' element from sum. Save any solution if occurs, in while loop. +3. Go back to step 1, until 'end' hits nums.length +*/ + +/* +Thoughts: +Brutly, two pointer checking. Record Min. +Optimize: if futher length > min, no need to move the right pointer +Worst case: n + (n-1) + (n-2) ....+ 1 = O(n^2) +*/ +class Solution { + public int minSubArrayLen(int s, int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int result = Integer.MAX_VALUE; + for (int i = 0; i < nums.length - 1; i++) { + int sum = nums[i]; + if (sum >= s) { + result = 1; + break; + } + for (int j = i + 1; j < nums.length; j++) { + if (j - i >= result) { + break; + } + sum += nums[j]; + if (sum >= s) { + result = Math.min(result, j - i + 1); + break; + } + } + } + return result == Integer.MAX_VALUE ? 0 : result; + } +} + +/* +Thoughts: old solution O(n), not prefered. Create a subarray range: (i,j). Use i as start and j as end. Check if within the range, sum >= s. Shift the range every time: i += 1. + +2 pointer: +一个做base, 每次动一格:i. +一个做前锋,加到满足条件为止。 +Note: 当sum >= s 条件在while里面满足时,end是多一个index的。所以result里面要处理好边缘情况:(end-1) 才是真的末尾位置,然后计算和开头的间隙: +(end - 1) - start + 1; + + */ public class Solution { - /** - * @param nums: an array of integers - * @param s: an integer - * @return: an integer representing the minimum size of subarray - */ public int minimumSize(int[] nums, int s) { if (nums == null || nums.length == 0) { return -1; diff --git a/Java/Minimum Subarray.java b/Java/Minimum Subarray.java old mode 100644 new mode 100755 index 10ed51f..c62c5a3 --- a/Java/Minimum Subarray.java +++ b/Java/Minimum Subarray.java @@ -1,3 +1,20 @@ +E +1532126087 +tags: Greedy, Array, DP, Sequence DP, Subarray +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + +``` + /* Given an array of integers, find the subarray with smallest sum. @@ -12,6 +29,28 @@ Tags Expand Greedy LintCode Copyright Subarray Array + +*/ + +/* +DP, Sequence DP +Consider last num: min sum will be (preMinSum + curr, or curr) +Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +Have a global min to track: because the preMinSum can be dis-continuous. +*/ +public class Solution { + public int minSubArray(List nums) { + if (nums == null || nums.size() == 0) return Integer.MAX_VALUE; + int preMinSum = 0, min = Integer.MAX_VALUE; + for (int num : nums) { + preMinSum = Math.min(num, preMinSum + num); + min = Math.min(min, preMinSum); + } + return min; + } +} + +/* Thoughts: Note: sub-array has order. It's not sub-set 1. On each index: decide to add with nums.get(i), to use the new lowest value nums.get(i). That means: @@ -21,8 +60,6 @@ If the new value is negative (it has decresing impact on sum) and the sum is lar Note: remember to pre-set init value for curMin, minRst. */ - - public class Solution { /** * @param nums: a list of integers @@ -40,4 +77,5 @@ public int minSubArray(ArrayList nums) { } return minRst; } -} \ No newline at end of file +} +``` \ No newline at end of file diff --git a/Java/Minimum Swaps To Make Sequences Increasing.java b/Java/Minimum Swaps To Make Sequences Increasing.java new file mode 100644 index 0000000..b9949d7 --- /dev/null +++ b/Java/Minimum Swaps To Make Sequences Increasing.java @@ -0,0 +1,76 @@ +M +1531982363 +tags: DP, Status DP, Coordinate DP + + +#### DP +- 特点: 上一步可能是swaped也可能是fixed +- 考虑A,B之间的现状: `A[i] > A[i - 1] && B[i] > B[i - 1]` 或者 `A[i] > B[i - 1] && B[i] > A[i - 1]` +- 问题: 如何把这个状态变成合理的strick-increasing状态? +- `A[i] > A[i - 1] && B[i] > B[i - 1]`: 1. 已经合理, 也不动. 2. [i], [i-1] 全部都swap +- `A[i] > B[i - 1] && B[i] > A[i - 1]`, 交错开来, 所以调换[i], 或者[i-1]: 1. 换[i-1]. 2. 换[i] +- 注意因为求min, 所以init value应该是 Integer.MAX_VALUE; + +``` +/* + +We have two integer sequences A and B of the same non-zero length. + +We are allowed to swap elements A[i] and B[i]. Note that both elements are in the same index position in their respective sequences. + +At the end of some number of swaps, A and B are both strictly increasing. (A sequence is strictly increasing if and only if A[0] < A[1] < A[2] < ... < A[A.length - 1].) + +Given A and B, return the minimum number of swaps to make both sequences strictly increasing. It is guaranteed that the given input always makes it possible. + +Example: +Input: A = [1,3,5,4], B = [1,2,3,7] +Output: 1 +Explanation: +Swap A[3] and B[3]. Then the sequences are: +A = [1, 3, 5, 7] and B = [1, 2, 3, 4] +which are both strictly increasing. +Note: + +A, B are arrays with the same length, and that length will be in the range [1, 1000]. +A[i], B[i] are integer values in the range [0, 2000]. + +*/ + +/* +dp[i]: # of swaps for first i items. +dp[0] = 0; +dp[i] = dp[i - 1] + 1 (if swap is needed) + +However, we cannot swap greedily. dp[i-1] can be a result of swapping, or fixed. +You should be able to swap or not swap, as long as we fit the strict-increasing rule + +Discuss happy case, and potential-swap case. + +Add status to dp[i][status]. status = 0: fixed; status = 1, swapped + +http://zxi.mytechroad.com/blog/dynamic-programming/leetcode-801-minimum-swaps-to-make-sequences-increasing/ +*/ +class Solution { + public int minSwap(int[] A, int[] B) { + if (A.length != B.length) return 0; + + int n = A.length; + int[][] dp = new int[n][2]; + dp[0][0] = 0; + dp[0][1] = 1; + + for (int i = 1; i < n; i++) { + dp[i][0] = dp[i][1] = Integer.MAX_VALUE; + if (A[i] > A[i - 1] && B[i] > B[i - 1]) { // happy case + dp[i][0] = dp[i - 1][0]; // no need to swap + dp[i][1] = dp[i - 1][1] + 1; // A[i-1], B[i-1] was swapped result + } + if (A[i] > B[i - 1] && B[i] > A[i - 1]) { + dp[i][0] = Math.min(dp[i][0], dp[i - 1][1]); // [i]fixed, swaped[i-1] + dp[i][1] = Math.min(dp[i][1], dp[i - 1][0] + 1); // [i]swaped, kept[i-1] + } + } + return Math.min(dp[n-1][0], dp[n-1][1]); + } +} +``` \ No newline at end of file diff --git a/Java/Minimum Window Substring.java b/Java/Minimum Window Substring.java old mode 100644 new mode 100755 index dfaabcb..46f54a5 --- a/Java/Minimum Window Substring.java +++ b/Java/Minimum Window Substring.java @@ -1,13 +1,26 @@ +H +1519980648 +tags: Hash Table, Two Pointers, String + +基本思想: 用个char[]存string的frequency. 然后2pointer, end走到底, 不断validate. +符合的就process as result candidate. + +HashMap的做法比char[]写起来要复杂一点, 但是更generic + +``` /* -Given a string source and a string target, find the minimum window in source which will contain all the characters in target. +Given a string S and a string T, find the minimum window in S which will contain all the characters in T in complexity O(n). -Example -source = "ADOBECODEBANC" target = "ABC" Minimum window is "BANC". +For example, +S = "ADOBECODEBANC" +T = "ABC" +Minimum window is "BANC". -Note -If there is no such window in source that covers all characters in target, return the emtpy string "". +Note: +If there is no such window in S that covers all characters in T, return the empty string "". + +If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in S. -If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in source. Challenge Can you do it in time complexity O(n) ? @@ -19,7 +32,219 @@ Can you do it in time complexity O(n) ? Tags Expand Hash Table +*/ +/* +Thoughts: +Use 2 pointer to track window. +Use set to track t's characters: charSet +end pionter: subtracting c from the charSet. +start pointer: if start++ skipping a candidate, we add it back to the charSet. +*/ + +class Solution { + public String minWindow(String s, String t) { + if (s == null || t == null || s.length() < t.length()) { + return ""; + } + int end = 0; + int length = Integer.MAX_VALUE; + String rst = ""; + + // Initialize source map for validation usage + int[] source = new int[256]; + int[] target = new int[256]; + for (char c : t.toCharArray()) { + target[c]++; + } + + for (int i = 0; i < s.length(); i++){ + while (end < s.length() && !valid(source, target)) { + source[s.charAt(end)]++; + end++; + } + if (valid(source, target)) { + if (end - i < length) { + length = Math.min(length, end - i); + rst = s.substring(i, end); + } + } + source[s.charAt(i)]--; + } + return rst; + } + + /* + Validate if the count of source map matches targetMap. + Use target as base, since it's substring. + */ + private boolean valid(int[] source, int[] target) { + for (int i = 0; i < 256; i++) { + if (source[i] < target[i]) { + return false; + } + } + return true; + } +} + +//Previous notes +/* +03.09.2016 +http://blog.sina.com.cn/s/blog_eb52001d0102v2il.html +http://www.rudy-yuan.net/archives/185/ + +只利用一个hashmap save frequency of target. +1. 从map里面 减去 source char match target char 的frequency. 如果发现frequency = 0, size++ + size == map.size,说明有一个candidate. +2. 维持left,right pointer。 +记住一个规则: 如果map里面在left index上面的frequency >=0, 也就是frequency 没有小于0,所以暂时没有多余的这个char on left index. +因此left不能动。这里要测试并且break。 +然而,当frequency < 0时候,说明在后续的String里面出现了多余的char,也就是frequency--使其小于0. 这里,我们就可以动left++了,并且frequency++。 +这个效果就等于,当matched char在后面有重复出现(多余)时,我们可以left++,安全移动。 + +另外. 其余那些不在target里面的char, 用left++过滤掉。 + +3. right pointer一直在按规律跑动。 + +4. 当size == map.size(), 截取string + +*/ +public class Solution { + public String minWindow(String s, String t) { + if (s == null || s.length() == 0 || t == null || t.length() == 0) { + return ""; + } + //Init map based on t + HashMap map = new HashMap(); + for (int i = 0; i < t.length(); i++) { + char c = t.charAt(i); + if (!map.containsKey(c)) { + map.put(c, 0); + } + map.put(c, map.get(c) + 1); + } + + int count = 0; + int start = 0; + int end = 0; + int lengS = s.length(); + int leng = Integer.MAX_VALUE; + String rst = ""; + //Iteratve through s + for (; end < lengS; end++) { + char c = s.charAt(end); + if (map.containsKey(c)) { + map.put(c, map.get(c) - 1); + if (map.get(c) == 0) { + count++; + } + } + + while (start < lengS) { + char cs = s.charAt(start); + if (map.containsKey(cs)) { + //If >= 0 still being used in map, not ready yet: can't move + if (map.get(cs) >= 0) { + break; + } else {//less than 0, so we can skip previous cs, move start++ + map.put(cs, map.get(cs) + 1); + } + } + //skip non-t chars + start++; + } + + if (count == map.size() && (end - start + 1) < leng) { + rst = s.substring(start, end + 1); + leng = rst.length(); + } + }//end for + + return rst; + } +} + + + +/* +Same as below solution, incorrect: only picks the first possible solution, not minimum window. +HOWEVER, it accidentally passed LintCode. + +只能找到第一个, 所以有错。 + +//HashT +//HashS: count the frequency of chars from source. +//once found one candidate, we may have a large window. Now move left-most char by comparison using HashS, to minimize the window + +*/ +public class Solution { + public String minWindow(String s, String t) { + if (s == null || s.length() == 0 || t == null || t.length() == 0) { + return ""; + } + + HashMap hashT = new HashMap(); + HashMap hashS = new HashMap(); + //init hashT + for (int i = 0; i < t.length(); i++) { + char c = t.charAt(i); + if (!hashT.containsKey(c)) { + hashT.put(c, 0); + } + hashT.put(c, hashT.get(c) + 1); + } + + //Check against S + int count = 0; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (!hashT.containsKey(c)) { + continue; + } + if (!hashS.containsKey(c)) { + hashS.put(c, 0); + } + hashS.put(c, hashS.get(c) + 1); + + if (hashS.get(c) <= hashT.get(c)) { + count++; + } + + //Found string + if (count == t.length()) { + return findStr(i, s, hashT, hashS); + } + } + return ""; + } + + public String findStr(int end, String s, + HashMap hashT, HashMap hashS) { + int start = 0; + while (start < s.length()) { + char c = s.charAt(start); + if (!hashS.containsKey(c)) { + start++; + continue; + } + if (hashS.get(c) > hashT.get(c)) { + hashS.put(c, hashS.get(c) - 1); + start++; + continue; + } + break; + }//end while + + return s.substring(start, end + 1); + } +} + + + + +/* +Not working: it only picks up the first possible solution, but not the shortest Thoughts: The idea was from jiuzhang.com. 1. count target Characters: store each Character with HashMap:tCounter @@ -38,12 +263,7 @@ srouce string and can also be used as head of the result string (with shorter le import java.util.*; public class Solution { - /** - * @param source: A string - * @param target: A string - * @return: A string denote the minimum window - * Return "" if there is no such a string - */ + public String minWindow(String source, String target) { if (source == null || source.length() == 0) { return source; @@ -110,3 +330,6 @@ public static void main(String[] args) { System.out.println("resutl is : " + rst); } } + + +``` \ No newline at end of file diff --git a/Java/Missing Number.java b/Java/Missing Number.java new file mode 100644 index 0000000..6756596 --- /dev/null +++ b/Java/Missing Number.java @@ -0,0 +1,131 @@ +E +1526012135 +tags: Array, Math, Bit Manipulation + +给一串unique数字, 数字取自 [0 ~ n], 无序, 找第一个skipped的数字. + +#### Swap +- 跟First Missing Positive 非常像, 只有一行代码的区别. +- swap 所有的数字, 到自己的correct position +- 最后一个for loop找到错位的index, 也就是缺的数字. + +#### Bit Manipulation +- XOR will only retain bits that are different 1 ^ 0 = 1, but 0^0, 1^1 == 0 +- Use that feature, 把所有value都和index XOR了 +- 剩下的多余的数字, 其实是那个index无法被XOR消掉, 也就是那个缺的number value. +- 注意: 题目告诉数字是 [0 ~ n], 然而缺一个数字, 那么在[0 ~ n - 1] 里面, 最大的数字(不管缺没缺), 一定是 n = nums.length. + +#### HastSet +- 全存, 找missing +- O(n) space, 不合题意 + +#### sorting +- sort, 找1st missing +- O(n log n) 太慢, 不合题意 + +``` +/* + +Given an array containing n distinct numbers taken from 0, 1, 2, ..., n, +find the one that is missing from the array. + +Example 1: + +Input: [3,0,1] +Output: 2 +Example 2: + +Input: [9,6,4,2,3,5,7,0,1] +Output: 8 +Note: +Your algorithm should run in linear runtime complexity. +Could you implement it using only constant extra space complexity? +*/ + +public class Solution { + public int missingNumber(int[] nums) { + // check input + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + + // 1st loop, swap to correct location + for (int i = 0; i < n; i++) { + int val = nums[i]; + while (val != i && val < n && val != nums[val]) { // val != nums[val], avoid infinitely loop + swap(nums, val, i); + val = nums[i]; + } + } + + // 2nd loop, find 1st missing + for (int i = 0; i < n; i++) { + if (nums[i] != i) { + return i; + } + } + + return n; + } + + private void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + + +public class Solution { + public int missingNumber(int[] nums) { + // check input + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + // 1st loop, swap to correct location + int i = 0; + while (i < n) { + int val = nums[i]; + if (val != i && val < n && val != nums[val]) { // val != nums[val], avoid infinitely loop + swap(nums, val, i); + } else { + i++; + } + } + + // 2nd loop, find 1st missing + for (i = 0; i < n; i++) { + if (nums[i] != i) { + return i; + } + } + + return n; + } + + private void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + +// Bit manipulation +class Solution { + public int missingNumber(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int result = nums.length; + for (int i = 0; i < nums.length; i++) { + result = result ^ nums[i] ^ i; + } + return result; + } +} + + + +``` \ No newline at end of file diff --git a/Java/Missing Ranges.java b/Java/Missing Ranges.java new file mode 100755 index 0000000..e3433f6 --- /dev/null +++ b/Java/Missing Ranges.java @@ -0,0 +1,62 @@ +M +tags: Array + +#### Basic Implementation +- O(n) +- 两个pointer, 每次计较prev和curr之间的部分. +- 然后prev = curr,向前移动一格 +- TODO: check the edge case and make sure max/min of int are checked + +``` +/* +Given a sorted integer array where the range of elements are [lower, upper] inclusive, return its missing ranges. + +For example, given [0, 1, 3, 50, 75], lower = 0 and upper = 99, return ["2", "4->49", "51->74", "76->99"]. + +Tags: Array +Similar Problems: (E) Summary Ranges + + +*/ +/* + +Attempt2, Thoughts: +Use two pointer to mark the prev and curr value, then verify the range in between. + +matching conditoin: prev +2 >= curr. +That is, +1,...,3 + +1. When print range: print the missing [x,y] +2. missing x = prev+1, missing y = curr - 1; +3. Make sure prev represents the consecutive integer before missing x. +*/ + +public class Solution { + public List findMissingRanges(int[] nums, int lower, int upper) { + List rst = new ArrayList(); + if (nums == null || nums.length == 0) {//Though, also covered in the for + rst.add(printRange(lower, upper)); + return rst; + } else if (lower > upper) { + return rst; + } + int prev = lower - 1; + int curr; + for (int i = 0; i <= nums.length; i++) { + curr = (i == nums.length) ? upper + 1 : nums[i]; + if (prev + 2 <= curr) { + rst.add(printRange(prev + 1, curr - 1)); + } + prev = curr; + } + return rst; + } + + public String printRange(int from, int to) { + return (from == to) ? String.valueOf(from) : from + "->" + to; + } +} + + +``` \ No newline at end of file diff --git a/Java/Move Zeroes.java b/Java/Move Zeroes.java new file mode 100644 index 0000000..3093ab4 --- /dev/null +++ b/Java/Move Zeroes.java @@ -0,0 +1,43 @@ +E +1533511284 +tags: Array, Two Pointers + +Move non-zero elements to front of array; preseve order. + +#### Two Pointers +- Outside pointer that moves in certain condition. +- Save appropirate elements + +``` +/* +Given an array nums, write a function to move all 0's to the end of it +while maintaining the relative order of the non-zero elements. + +Example: + +Input: [0,1,0,3,12] +Output: [1,3,12,0,0] +Note: + +You must do this in-place without making a copy of the array. +Minimize the total number of operations. +*/ +/* +- Use pointer to move all elements to non-zero index. +- set remaining list -> 0 +*/ +class Solution { + public void moveZeroes(int[] nums) { + if (nums == null || nums.length == 0) return; + int index = 0; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != 0) { + nums[index++] = nums[i]; + } + } + for (int i = index; i < nums.length; i++) { + nums[i] = 0; + } + } +} +``` \ No newline at end of file diff --git a/Java/Moving Average from Data Stream.java b/Java/Moving Average from Data Stream.java new file mode 100644 index 0000000..156211f --- /dev/null +++ b/Java/Moving Average from Data Stream.java @@ -0,0 +1,51 @@ +E +1533510925 +tags:Design, Queue, Sliding Window + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison + +``` +/** +Given a stream of integers and a window size, +calculate the moving average of all integers in the sliding window. + +For example, +MovingAverage m = new MovingAverage(3); +m.next(1) = 1 +m.next(10) = (1 + 10) / 2 +m.next(3) = (1 + 10 + 3) / 3 +m.next(5) = (10 + 3 + 5) / 3 + */ + +class MovingAverage { + double sum; + int size; + Queue queue; + + /** Initialize your data structure here. */ + public MovingAverage(int size) { + this.sum = 0.0; + this.size = size; + this.queue = new LinkedList<>(); + } + + public double next(int val) { + sum += val; + queue.offer(val); + if (queue.size() > size) { + sum -= queue.poll(); + } + return sum / queue.size(); + } +} + +/** + * Your MovingAverage object will be instantiated and called as such: + * MovingAverage obj = new MovingAverage(size); + * double param_1 = obj.next(val); + */ +``` \ No newline at end of file diff --git a/Java/Multiply Strings.java b/Java/Multiply Strings.java new file mode 100755 index 0000000..13b143d --- /dev/null +++ b/Java/Multiply Strings.java @@ -0,0 +1,153 @@ +M +1531497384 +tags: Math, String + +给两个integer String, 求乘积 + +#### String calculation, basic implementation +- let num1 = multipier, num2 = base. +- mutiply and save into int[m + n], without carry. Loop over num1, each row num1[x] * num2 +- move carry to the correct index and direclty save result +- calculate carry on rst[]: sb.insert(0, c) such that no need to reverse() later +- remove leading '0', but do not delete string "0" +- time,space O(mn) + +#### Previous notes. +- Bad solution: reversing makes it complicated, no need to reverse. +- 1. 数字‘123’, 在数组里面, index == 0 是 ‘1’。 但是我们平时习惯从最小位数开始乘积,就是末尾的'3'开始。 +- 所以!翻转两个数字先!我去。这个是个大坑。 +- 2. 乘积product,和移动Carrier都很普通。 +- 3. !!最后不能忘了再翻转。 +- 4. 最后一个看坑。要是乘积是0,就返回‘0’。 但是这个其实可以在开头catch到没必要做到结尾catch。 +- 用到几个StringBuffer的好东西: reverse(), sb.deleteCharAt(i) +- 找数字,或者26个字母,都可以: s.charAt(i) - '0'; s.charAt(i) - 'a'; + +``` +/* +Given two non-negative integers num1 and num2 represented as strings, +return the product of num1 and num2, also represented as a string. + +Example 1: + +Input: num1 = "2", num2 = "3" +Output: "6" +Example 2: + +Input: num1 = "123", num2 = "456" +Output: "56088" +Note: + +The length of both num1 and num2 is < 110. +Both num1 and num2 contain only digits 0-9. +Both num1 and num2 do not contain any leading zero, except the number 0 itself. +You must not use any built-in BigInteger library or convert the inputs to integer directly. +*/ + +/* +Hidden: can be different length +- let num1 = multipier, num2 = base. +- mutiply and save into int[m + n], without carry. Loop over num1, each row num1[x] * num2 +- calculate carry and reverse result (remove leading '0') +*/ +class Solution { + public String multiply(String num1, String num2) { + if (num1 == null || num1.length() == 0) return num2; + if (num2 == null || num2.length() == 0) return num1; + + // convert, reverse + int m = num1.length(), n = num2.length(); + int[] rst = new int[m + n]; + // multiply without carry handling + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) rst[i + j + 1] += toInt(num1, i) * toInt(num2, j); + } + + // calc carry, reverse and output + StringBuffer sb = new StringBuffer(); + for (int i = rst.length - 1; i > 0; i--) { + rst[i - 1] += rst[i] / 10; + rst[i] %= 10; + sb.insert(0, (char)(rst[i] + '0')); + } + sb.insert(0, (char)(rst[0] + '0')); + + while (sb.length() > 1) { + if (sb.charAt(0) != '0') break; + sb.deleteCharAt(0); + } + + return sb.toString(); + } + + private int toInt(String s, int x) { + return s.charAt(x) - '0'; + } +} + +/* +Previous: too complicated + Thoughts: + 1. too long to multiply int. so convert to int[] + 2. Multiply by definition: + a. create a product[] of num1.size() + num2.size() - 1 + b. catches each product[i + j] + 3. for loop on product array again, to carry over the carries + + if both null, return null. + if both "", return "" + + O(m + n) +*/ +public class Solution { + public String multiply(String num1, String num2) { + if (num1 == null || num2 == null) { + return ""; + } else if (num1.length() == 0 || num2.length() == 0) { + return num1.length() == 0 ? num2 : num1; + } else if (num1.equals("0") || num2.equals("0")) { + return "0"; + } + //reverse string, so to calculate from 0 base. easier to calculate + num1 = new StringBuffer(num1).reverse().toString(); + num2 = new StringBuffer(num2).reverse().toString(); + + //product array. extra leading space for carriers + //normally just need num1.length() + num2.length() -1 + int[] product = new int[num1.length() + num2.length()]; + + //Calculate the product normally + for (int i = 0; i < num1.length(); i++) { + int a = num1.charAt(i) - '0'; + for (int j = 0; j < num2.length(); j++) { + int b = num2.charAt(j) - '0'; + product[i + j] += a * b; + } + } + + //calcualte and output + //remember, now the string is reversed calculated. + //so every time, add to index 0. so it will all reverse back; OR, append, and reverse later. + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < product.length; i++) { + int number = product[i] % 10; + int carrier = product[i] / 10; + sb.append(number); + if (i < product.length - 1) { + product[i + 1] += carrier; + } + } + sb.reverse(); + //trim leading 0's + while (sb.length() > 0 && sb.charAt(0) == '0') { + sb.deleteCharAt(0); + } + + return sb.toString(); + } +} + + + + + +``` \ No newline at end of file diff --git a/Java/My Calendar I.java b/Java/My Calendar I.java new file mode 100644 index 0000000..212eb5d --- /dev/null +++ b/Java/My Calendar I.java @@ -0,0 +1,111 @@ +M +1533013643 +tags: Array, TreeMap + +Given a list of interval as calendar items. Check if newly added calendar item is overlapping. + +Understand it is only checking time, but not requiring to insert into right spot. No need to overthink. + +#### Simply O(n) check on array +- number of test cases is small, like 1000, so less concern about the time complexity +- simply loop over the list of intervals, and check if any overlapping. +- where to insert does not really matter: every time we are just checking for overlaopping, not merging any range +- **IMPORTANT**: if interval over lapping, they will have this property `Math.max(s1, s2) < Math.min(e1, e2)`. This will help detect the overlapping very easily. +- O(n^2) runtime, with simple code. But somehow this approach is faster than the TreeMap solution: maybe the test cause causes avg O(n)? + +#### TreeMap +- One constraint from the simply array solution: it always cost O(n) to find the potential overlapping interval +- We can manually sort and always manually try to find the correct element via binary search, or we could store the interval in a treeMap, where the intervals are sorted by `start`. +- As result, all we need to do for book(start, end) is to find the next element ceiling(start), last element floor(start), and check for overlapping +- This approach also saves the custom data structure +- Overall cost O(nlogn) + +##### About TreeMap +- always with key sorted ascendingly +- more costly than regular HashMap because of the sorting. Building treemap of n items: O(nlogn) + +#### Sweep line +- use `Point{int start, end; boolean start}` to mark start/end of class. Add to pq. +- Adding new item to pq, sort, and check if overlapping occurs by counting started classes +- If started classes > 1, that means we overlapped. +- Every time it could consume all classes to find the overlap, O(n^2). +- Not quite need to sort or insert at correct point, and this solution requires longer code. Not quite worthy it for a simple problem. + + +``` +/** +Implement a MyCalendar class to store your events. +A new event can be added if adding the event will not cause a double booking. + +Your class will have the method, book(int start, int end). +Formally, this represents a booking on the half open interval [start, end), +the range of real numbers x such that start <= x < end. + +A double booking happens when two events have some non-empty intersection +(ie., there is some time that is common to both events.) + +For each call to the method MyCalendar.book, return true if the event can be added to the calendar successfully +without causing a double booking. Otherwise, return false and do not add the event to the calendar. + +Your class will be called like this: MyCalendar cal = new MyCalendar(); MyCalendar.book(start, end) +Example 1: +MyCalendar(); +MyCalendar.book(10, 20); // returns true +MyCalendar.book(15, 25); // returns false +MyCalendar.book(20, 30); // returns true +Explanation: +The first event can be booked. The second can't because time 15 is already booked by another event. +The third event can be booked, as the first event takes every time less than 20, but not including 20. +Note: + +The number of calls to MyCalendar.book per test case will be at most 1000. +In calls to MyCalendar.book(start, end), start and end are integers in the range [0, 10^9]. + + */ + + +// array, using Math.max(s, start) < Math.min(e, end) +class MyCalendar { + class Interval { + int start, end; + public Interval(int start, int end) { + this.start = start; + this.end = end; + } + } + List intervals; + public MyCalendar() { + intervals = new ArrayList<>(); + } + + public boolean book(int start, int end) { + for (Interval interval : intervals) { + int s = interval.start, e = interval.end; + if (Math.max(s, start) < Math.min(e, end)) { + return false; + } + } + intervals.add(new Interval(start, end)); + return true; + } +} + +// tree map +class MyCalendar { + TreeMap map; + public MyCalendar() { + map = new TreeMap<>(); + } + + public boolean book(int start, int end) { + Integer floorKey = map.floorKey(start); + if (floorKey != null && map.get(floorKey) > start) return false; + Integer ceilingKey = map.ceilingKey(start); + if (ceilingKey != null && ceilingKey < end) return false; + + map.put(start, end); + return true; + } +} + +``` \ No newline at end of file diff --git a/Java/N-Queens II.java b/Java/N-Queens II.java new file mode 100755 index 0000000..c2557a2 --- /dev/null +++ b/Java/N-Queens II.java @@ -0,0 +1,122 @@ +H +1525324321 +tags: Backtracking + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + +``` +/* +Follow up for N-Queens problem. + +Now, instead outputting board configurations, return the total number of distinct solutions. + +Example +For n=4, there are 2 distinct solutions. + +Tags Expand +Recursion + +Thoughts: +Exact same as NQueens, except we don't print the map. Instead, simply add the record in rst. +At the end, return rst.size(), which would be unique answer. +*/ + +/* +Thougths: +Goal: dfs and count all solutions +1. dfs function (List, n) +2. validate function +*/ +class Solution { + public int totalNQueens(int n) { + if (n <= 0) { + return 0; + } + return dfs(new ArrayList<>(), n); + } + + private int dfs(List list, int n) { + if (list.size() == n) { + return 1; + } + int count = 0; + for (int col = 0; col < n; col++) { + if (validateBoard(list, col)) { + list.add(col); + count += dfs(list, n); + list.remove(list.size() - 1); + } + } + return count; + } + + private boolean validateBoard(List list, int newColNum) { + int newRowNum = list.size(); + for (int rowNum = 0; rowNum < list.size(); rowNum++) { + int colNum = list.get(rowNum); + if (colNum == newColNum || Math.abs(newColNum - colNum) == Math.abs(newRowNum - rowNum)) { + return false; + } + } + + return true; + } +} + + +class Solution { + /** + * Calculate the total number of distinct N-Queen solutions. + * @param n: The number of queens. + * @return: The total number of distinct solutions. + */ + public int totalNQueens(int n) { + ArrayList> rst = new ArrayList>(); + if (n <= 0) { + return 0; + } + search(n, new ArrayList(), rst); + return rst.size(); + } + + boolean isValid (ArrayList cols, int col) { + int row = cols.size(); + for (int i = 0; i < row; i++) { + if (cols.get(i) == col ) { + return false; + } + if (i - cols.get(i) == row - col) { + return false; + } + if (i + cols.get(i) == row + col) { + return false; + } + } + return true; + } + + void search(int n, ArrayList cols, ArrayList> rst) { + if (cols.size() == n) { + rst.add(cols); + return; + } + for (int i = 0; i < n; i++) { + if (!isValid(cols, i)) { + continue; + } + cols.add(i); + search(n, cols, rst); + cols.remove(cols.size() - 1); + } + } + +}; + + + +``` \ No newline at end of file diff --git a/Java/N-Queens.java b/Java/N-Queens.java new file mode 100755 index 0000000..b80286e --- /dev/null +++ b/Java/N-Queens.java @@ -0,0 +1,328 @@ +H +1525315201 +tags: Backtracking + +N-Queen 问题, 给数字n, 和 nxn board, 找到所有N-queens的答案. + +#### Backtracking +- 用dfs找所有情况, 每一个iteration, 从找一行里挑合适的点, dfs +- 选中的点加进candidate list 里面, 记得要backtracking. +- 每一个candidate都需要validation, 检查 row, col, 2 diagnal 有没有queen + +#### validate n queue at certain (x, y) +- 1. array 里面不能有 target row# +- 2. diagnal. 记得公式: +- row1 - row2 == col1 - col2. Diagnal elelment.fail +- row1 - row2 == - (col1 - col2). Diagnal element. fail +- Draw a 3x3 board to test the 2 scanarios: +- (0,0) and (3,3) are diagnal +- (0,2) and (2,0) are diagnal + + +``` +/* +The n-queens puzzle is the problem of placing n queens on an n×n chessboard such that +no two queens attack each other. + +Given an integer n, return all distinct solutions to the n-queens puzzle. + +Each solution contains a distinct board configuration of the n-queens' placement, +where 'Q' and '.' both indicate a queen and an empty space respectively. + +For example, +There exist two distinct solutions to the 4-queens puzzle: + +[ + [".Q..", // Solution 1 + "...Q", + "Q...", + "..Q."], + + ["..Q.", // Solution 2 + "Q...", + "...Q", + ".Q.."] +] +Hide Tags Backtracking + + +*/ + +class Solution { + public List> solveNQueens(int n) { + List> rst = new ArrayList<>(); + if (n <= 0) return rst; + + dfs(rst, new ArrayList<>(), n); + + return rst; + } + // build sequence row + private void dfs(List> rst, List list, int n){ + if (list.size() == n) { + rst.add(createBoard(list)); + return; + } + //For next row, which col to put queen? Now do recursive: + for (int i = 0; i < n; i++) { + if (validate(list, i)) { + list.add(i); + dfs(rst, list, n); + list.remove(list.size() - 1); + } + } + } + + /* + Validate the board with given input. + Draw a 3x3 board to test the 2 scanarios: + - (0,0) and (3,3) are diagnal + - (0,2) and (2,0) are diagnal + */ + private boolean validate(List list, int newColNum) { + int newRowNum = list.size(); // the new row that colNum is going to be put on + for (int rowNum = 0; rowNum < list.size(); rowNum++) { + //check row, check diagnal + int colNum = list.get(rowNum); + if (colNum == newColNum || Math.abs(rowNum - newRowNum) == Math.abs(colNum - newColNum)) { + return false; + } + } + return true; + } + /* + private boolean validate(List list, int newColNum) { + int newRowNum = list.size(); // the new row that colNum is going to be put on + for (int rowNum = 0; rowNum < list.size(); rowNum++) { + //check row + int colNum = list.get(rowNum); + if (colNum == newColNum) { + return false; + } + //check diagnal + //q1 row - newQ row == q1 col - newQ col + if (rowNum - newRowNum == colNum - newColNum) { + return false; + } + //q1 row - newQ row == -(q1 col - newQ col) + if (rowNum - newRowNum == - (colNum - newColNum)) { + return false; + } + } + return true; + }*/ + + private List createBoard(List list){ + List board = new ArrayList<>(); + for (int row = 0; row < list.size(); row++) { + StringBuffer sb = new StringBuffer(); + for (int col : list) { + if (row == col) { + sb.append("Q"); + } else { + sb.append("."); + } + } + board.add(sb.toString()); + } + return board; + } +} + + +/* + Recap: 12.08.2015 + NQueen: turns into a 1D array of row#'s. What are all of the possible combinations? + Validate the board: + With 1 potential canddate, a row# to put in the array + 1. the 1D array cannot have duplicate of the candidate + 2. check diagnals: + row1 - row2 == col1 - col2. Diagnal elelment.fail + row1 - row2 == -(col1 - col2). Diagnal element. fail + That is: delta_row = Q1 row - Q2 row + delta_col = Q1 col - Q2 col + Let delta_row = the difference in rows between the two queens, and delta_col = the difference in columns. + The two queens will be on the same diagonal if delta_row == delta_col or delta_row == -delta_col + + Create the board: + carete 2d arraylist based on the 1-D array of row#'s + + corner case: if n<=2, no solution +*/ + +class Solution { + + public ArrayList> solveNQueens(int n) { + ArrayList> rst = new ArrayList>(); + if (n <= 0) { + return rst; + } + ArrayList list = new ArrayList(); //1D array + helper(rst, list, n); + + return rst; + } + + /* + Validate the board with given input. + */ + public boolean validate(ArrayList list, int rowNum) { + int colNum = list.size(); // the column that rowNum is going to be put on + for (int col = 0; col < list.size(); col++) { + //check row + if (list.get(col) == rowNum) { + return false; + } + //check diagnal + //q1 col - newQ col == q1 row - newQ row + if (col - colNum == list.get(col) - rowNum) { + return false; + } + //q1 col - newQ col == -(q1 row - newQ row) + if (col - colNum == -(list.get(col) - rowNum)) { + return false; + } + } + return true; + } + + public ArrayList createBoard(ArrayList list){ + ArrayList board = new ArrayList(); + for (int row = 0; row < list.size(); row++) { + StringBuffer sb = new StringBuffer(); + for (int col : list) { + if (row == col) { + sb.append("Q"); + } else { + sb.append("."); + } + } + board.add(sb.toString()); + } + return board; + } + + + public void helper(ArrayList> rst, ArrayList list, int n){ + if (list.size() == n) { + rst.add(createBoard(list)); + return; + } + //For next Queen, which row to put? Now do recursive: + for (int i = 0; i < n; i++) { + if (validate(list, i)) { + list.add(i); + helper(rst, list, n); + list.remove(list.size() - 1); + } + } + } +}; + + + +//Older version: the naming in validate() is confusing +/* +Thinking process: +1. Choose / Not choose concept. +2. N-Queue facts: + Each column has 1 Q. Each row has 1 Q. + That is: each column has 1 Q, which can be present as a row number. + Use a 1-D array: index is column number, value is row number +3. When adding a new row Number into the 1-D array, validate it. +4. Use same procedure in 'permutaions' problem. + The 1-D array 'cols' will be filled with all kinds of combination from 1 ~ n. + Only when cols.size() == n, return a solution +5. When returnning the solution, return the format as a board. ArrayList +*/ +import java.util.*; +class NQueens { + /** + * Get all distinct N-Queen solutions + * @param n: The number of queens + * @return: All distinct solutions + * For example, A string '...Q' shows a queen on forth position + */ + ArrayList> solveNQueens(int n) { + ArrayList> rst = new ArrayList>(); + if (n <= 0) { + return rst; + } + search(n, new ArrayList(), rst); + return rst; + } + + ArrayList createBoard(ArrayList cols) { + ArrayList solution = new ArrayList(); + for (int i = 0; i < cols.size(); i++) { + StringBuffer sb = new StringBuffer(); + for (int j = 0; j < cols.size(); j++){ + if (j == cols.get(i)) { + sb.append( "Q"); + } else { + sb.append( "."); + } + } + solution.add(sb.toString()); + } + return solution; + } + + boolean isValid (ArrayList cols, int col) { + int row = cols.size(); + for (int i = 0; i < row; i++) { + if (cols.get(i) == col ) { + return false; + } + //Check diagnal: Q1_row - Q2_row == Q1_col - Q2_col + //In this case: + //col: the target queen's column# + //cols.get(i): the target queen's row# + //We compare them with (row: current max_column#) and (i: current row#), to check if valid + if (i - cols.get(i) == row - col) { + return false; + } + if (i + cols.get(i) == row + col) { + return false; + } + } + return true; + } + + void search(int n, ArrayList cols, ArrayList> rst) { + if (cols.size() == n) { + rst.add(createBoard(cols)); + return; + } + for (int i = 0; i < n; i++) { + if (!isValid(cols, i)) { + continue; + } + cols.add(i); + search(n, cols, rst); + cols.remove(cols.size() - 1); + } + } + + public static void main(String[] args){ + NQueens test = new NQueens(); + test.solveNQueens(4); + } + +}; + + + +/* +Thoughts: goal is to place all n queens on the board. DFS. +1. Save all nodes in set by sequence #, where each # can translate to x,y corrdinate +2. once confirm on a position, find the set of positions that removed by this queen, put in subset. +3. remove subset from set, and dfs(set, n-1, list, rst) +4. if n == 0, save list to rst (or check list.size(), or check set.size()) +5. Use a global boolean board[][] to track attempted/failed position. If position attempted, then there is no need to try again. + +On first row: attemp queen from [0 ~ n-1] to launch the dfs. Each row will have a queen. + +However, persist && find the neighbors to remove from the set, is a bit redundant code. Not going with this approach +*/ +``` \ No newline at end of file diff --git a/Java/Nested List Weight Sum.java b/Java/Nested List Weight Sum.java new file mode 100644 index 0000000..4ccae86 --- /dev/null +++ b/Java/Nested List Weight Sum.java @@ -0,0 +1,129 @@ +E +1519628010 +tags: DFS, BFS + +给一串integers, list里面可能有nest list. 算总的sum. 规则, 如果是nested list, 每深一个depth, sum要乘以depth. + +#### DFS +- New interface to understand: object contains integer or object +- Visit all && sum, consider dfs. +- bottom->up is easier: pick nested object and execute dfs, which returns sum of it, add with (level value * weight). +- 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) +- Note1: not multiplying on overall level sum. Only multiply level with single value at this level. +- Note2:top->bottom is not necessary: there is not need of passing added object into next level. + +#### BFS +- bfs, queue, 处理queue.size(). +- use a level variable to track levels + +``` +/* +Given a nested list of integers, return the sum of all integers in the list weighted by their depth. + +Each element is either an integer, or a list -- whose elements may also be integers or other lists. + +Example 1: +Given the list [[1,1],2,[1,1]], return 10. (four 1's at depth 2, one 2 at depth 1) + +Example 2: +Given the list [1,[4,[6]]], return 27. +(one 1 at depth 1, one 4 at depth 2, and one 6 at depth 3; 1 + 4*2 + 6*3 = 27) + +*/ + +/** + * // This is the interface that allows for creating nested lists. + * // You should not implement it, or speculate about its implementation + * public interface NestedInteger { + * // Constructor initializes an empty nested list. + * public NestedInteger(); + * + * // Constructor initializes a single integer. + * public NestedInteger(int value); + * + * // @return true if this NestedInteger holds a single integer, rather than a nested list. + * public boolean isInteger(); + * + * // @return the single integer that this NestedInteger holds, if it holds a single integer + * // Return null if this NestedInteger holds a nested list + * public Integer getInteger(); + * + * // Set this NestedInteger to hold a single integer. + * public void setInteger(int value); + * + * // Set this NestedInteger to hold a nested list and adds a nested integer to it. + * public void add(NestedInteger ni); + * + * // @return the nested list that this NestedInteger holds, if it holds a nested list + * // Return null if this NestedInteger holds a single integer + * public List getList(); + * } + */ +/* +Thoughts: +Helper function (NestedInteger, depth). +Handle Integer, List - for loop +*/ +class Solution { + public int depthSum(List nestedList) { //[[1,1],2,[1,1]] + if (nestedList == null || nestedList.size() == 0) { + return 0; + } + int sum = 0; + for (NestedInteger childInt: nestedList) { + sum += dfs(childInt, 1); + } + return sum; + } + + public int dfs(NestedInteger nestInt, int depth) { + if (nestInt.isInteger()) { + return nestInt.getInteger() * depth; + } else { + int sum = 0; + for (NestedInteger childInt: nestInt.getList()) { + sum += dfs(childInt, depth + 1); + } + return sum; + } + } +} + +/* +Thoughts: +Iteratively, breadth-first search +Use queue, track level, handle all available integers at each level, then append to the queue. +*/ +class Solution { + public int depthSum(List nestedList) { //[[1,1],2,[1,1]] + if (nestedList == null || nestedList.size() == 0) { + return 0; + } + + Queue queue = new LinkedList(); + for (NestedInteger childInt: nestedList) { + queue.offer(childInt); + } + + int level = 1; + int sum = 0; + + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + NestedInteger childInt = queue.poll(); + if (childInt.isInteger()) { + sum += childInt.getInteger() * level; + } else { + for (NestedInteger nextLevelChildInt: childInt.getList()) { + queue.offer(nextLevelChildInt); + } + } + } + level++; + } + return sum; + } +} +``` diff --git a/Java/Next Closest Time.java b/Java/Next Closest Time.java new file mode 100644 index 0000000..9345dd5 --- /dev/null +++ b/Java/Next Closest Time.java @@ -0,0 +1,103 @@ +M +1527751968 +tags: String, Basic Implementation, Enumeration + +给一个时间string"12:09", 用里面的4个integer组合成其他时间string, 目标找最小的next time. + +如果组合出的time string 在input time之前, 默认 + 24 hours. + +#### String +- enumerate all candidates and filter to keep the correct ones +- String.compareTo(string) -> gives lexicographical comparision + +``` + +/* +Given a time represented in the format "HH:MM", +form the next closest time by reusing the current digits. +There is no limit on how many times a digit can be reused. + +You may assume the given input string is always valid. +For example, "01:34", "12:09" are all valid. "1:34", "12:9" are all invalid. + +Example 1: + +Input: "19:34" +Output: "19:39" +Explanation: The next closest time choosing from digits 1, 9, 3, 4, is 19:39, which occurs 5 minutes later. +It is not 19:33, because this occurs 23 hours and 59 minutes later. +Example 2: + +Input: "23:59" +Output: "22:22" +Explanation: The next closest time choosing from digits 2, 3, 5, 9, is 22:22. +It may be assumed that the returned time is next day's time since it is smaller than the input time numerically. + +*/ + +/* +1. parse the input to digits, Build hh set, mm set. +2. cross match hh x mm +3. compare each valid time against input time, mark the closest +*/ +class Solution { + public String nextClosestTime(String time) { + // check edge + if (time == null || time.length() == 0) { + return time; + } + + // parse into 4 digits + Set nums = new HashSet<>(); + for (int i = 0; i < time.length(); i++) { + if (time.charAt(i) != ':') { + nums.add(time.charAt(i) - '0'); + } + } + + // use the 4 digits to build hh set, mm set: respect hh:mm limit + Set hh = new HashSet<>(); + for (int i : nums) { + for (int j : nums) { + int num = i * 10 + j; + if (num >= 0 && num < 24) { + hh.add(i + "" + j); + } + } + } + + Set mm = new HashSet<>(); + for (int i : nums) { + for (int j : nums) { + int num = i * 10 + j; + if (num >= 0 && num < 60) { + mm.add(i + "" + j); + } + } + } + + String result = time; + int timeValue = Integer.parseInt(time.substring(0, 2)) * 60 + Integer.parseInt(time.substring(3)); + int diff = Integer.MAX_VALUE; + // cross build hh x mm, and compare with time. +24 for smaller items + for (String h : hh) { + for (String m : mm) { + String newTime = h + ":" + m; + int value = 0; + if (newTime.compareTo(time) <= 0) { + value = Integer.parseInt(h) * 60 + Integer.parseInt(m) + 1440; + } else { + value = Integer.parseInt(h) * 60 + Integer.parseInt(m); + } + if (value - timeValue < diff) { + diff = value - timeValue; + result = newTime; + } + } + } + + return result; + } +} + +``` \ No newline at end of file diff --git a/Java/Next Greater Element I.java b/Java/Next Greater Element I.java new file mode 100644 index 0000000..0209996 --- /dev/null +++ b/Java/Next Greater Element I.java @@ -0,0 +1,52 @@ +E +1534309146 +tags: Stack, Hash Table + +#### stack? + +``` +/* +You are given two arrays (without duplicates) nums1 and nums2 where nums1’s elements are subset of nums2. Find all the next greater numbers for nums1's elements in the corresponding places of nums2. + +The Next Greater Number of a number x in nums1 is the first greater number to its right in nums2. If it does not exist, output -1 for this number. + +Example 1: +Input: nums1 = [4,1,2], nums2 = [1,3,4,2]. +Output: [-1,3,-1] +Explanation: + For number 4 in the first array, you cannot find the next greater number for it in the second array, so output -1. + For number 1 in the first array, the next greater number for it in the second array is 3. + For number 2 in the first array, there is no next greater number for it in the second array, so output -1. +Example 2: +Input: nums1 = [2,4], nums2 = [1,2,3,4]. +Output: [3,-1] +Explanation: + For number 2 in the first array, the next greater number for it in the second array is 3. + For number 4 in the first array, there is no next greater number for it in the second array, so output -1. +Note: +All elements in nums1 and nums2 are unique. +The length of both nums1 and nums2 would not exceed 1000. +*/ + +class Solution { + public int[] nextGreaterElement(int[] nums1, int[] nums2) { + int m = nums1.length, n = nums2.length; + int[] result = new int[m]; + Map pos = new HashMap<>(); + for (int i = 0; i < n; i++) { + pos.put(nums2[i], i); + } + for (int i = 0; i < m; i++) { + result[i] = check(nums2, pos.get(nums1[i]), nums1[i]); + } + return result; + } + + private int check(int[] nums, int index, int val) { + for (int i = index; i < nums.length; i++) { + if (nums[i] > val) return nums[i]; + } + return -1; + } +} +``` \ No newline at end of file diff --git a/Java/Next Permutation.java b/Java/Next Permutation.java old mode 100644 new mode 100755 index 3d4c64c..0bb916a --- a/Java/Next Permutation.java +++ b/Java/Next Permutation.java @@ -1,9 +1,25 @@ +M +tags: Array + +需斟酌: why reverse is need? why we are looking for k? + +Permutation的规律: +1. 从小的数字开始变化因为都是从小的数字开始recursive遍历。 +2. 正因为1的规律,所以找大的断点数字要从末尾开始: 确保swap过后的permutation依然是 前缀固定时 当下最小的。 + +steps: +1. 找到最后一个上升点,k +2. 从后往前,找到第一个比k大的点, bigIndex +3. swap k && bigIndex +4. 最后反转 (k+1,end) + + +``` /* Given a list of integers, which denote a permutation. Find the next permutation in ascending order. -Have you met this question in a real interview? Yes Example For [1,3,2,3], the next permutation is [1,3,3,2] @@ -15,6 +31,12 @@ Tags Expand LintCode Copyright Permutation + +*/ + +/* +http://fisherlei.blogspot.com/2012/12/leetcode-next-permutation.html?_sm_au_=isV7p50vt5RqMQ1Q + Thoughts: Not much info are given. Need to ask. It looks like: We are dong permutation on the given numbers, and find out what's next permutation array based on given order. @@ -25,63 +47,64 @@ To find the next smallest permutation. 1. Find the last increasing index (a peek before decresing): k 2. Find the first bigger permutation: Well, it turns out this first bigger index is always on right side of k. - Note: we are trying to get the least significant change on the given permuation. - Next Step: reverse (k+1, end). This is because: before the change, right side of K will be the largest possible combination. After swapping K, we need the right side to be the smallest combination. (Well, this is my understanding....Still a bit confused on why we take these steps in this problem) + Note: we are trying to get the least significant change on the given permuation. + Next Step: reverse (k+1, end). This is because: before the change, right side of K will be the largest possible combination. + After swapping K, we need the right side to be the smallest combination. (Well, this is my understanding.... + Still a bit confused on why we take these steps in this problem) */ - +/* +Thoughts: +1. 找到最后一个下降点,谷底, k +2. 从后往前,找到第一个比k大的点, bigIndex +3. swap k && bigIndex +4. 最后反转 (k+1,end) +*/ public class Solution { - /** - * @param nums: an array of integers - * @return: return nothing (void), do not return anything, modify nums in-place instead - */ - //Revers the given part of a int[] - public int[] reverse(int start, int end, int[] nums) { - for (int i = start, j = end; i < j; i++,j--) { - int temp = nums[i]; - nums[i] = nums[j]; - nums[j] = temp; - } - return nums; - } - public int[] nextPermutation(int[] nums) { - if (nums == null || nums.length == 0) { - return nums; - } - //Find last increasing point before decreasing. nums[k] < nums[k+1] - int k = -1; - for (int i = nums.length - 2; i >= 0; i--) { - if (nums[i] < nums[i + 1]) { - k = i; - break; - } - } - if (k == -1) { - return reverse(0, nums.length - 1, nums); - } - //Find first bigger point, from right to left - int bigIndex = -1; - for (int i = nums.length - 1; i >= 0; i--) { - if (nums[i] > nums[k]) { - bigIndex = i; - break; - } - } - //1. Swap bigger index with k; 2. Reverse the right side of k. [Try to make the smallest next permutation] - int temp = nums[k]; - nums[k] = nums[bigIndex]; - nums[bigIndex] = temp; - - return reverse(k + 1, nums.length - 1, nums); + if (nums == null || nums.length == 0) return nums; + + //Find the bottom: initial increasing point after decreasing. nums[k] < nums[k+1] + int k = -1; + for (int i = nums.length - 2; i >= 0; i--) { + if (nums[i] < nums[i + 1]) { + k = i; + break; + } + } + if (k == -1) { + return reverse(0, nums.length - 1, nums); + } + //Find the last point, from right to left + int bigIndex = -1; + for (int i = nums.length - 1; i >= 0; i--) { + if (nums[i] > nums[k]) { + bigIndex = i; + break; + } + } + //1. Swap bigger index with k; + swap(nums, k, bigIndex); + + //2. Reverse the right side of k to generate the smallest next permutation + return reverse(k + 1, nums.length - 1, nums); } + private void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } - + //Revers the given part of a int[] + private int[] reverse(int start, int end, int[] nums) { + for (int i = start, j = end; i < j; i++,j--) { + swap(nums, i, j); + } + return nums; + } } - - - +``` \ No newline at end of file diff --git a/Java/Nim Game.java b/Java/Nim Game.java new file mode 100755 index 0000000..2735f8e --- /dev/null +++ b/Java/Nim Game.java @@ -0,0 +1,130 @@ +E +1522047752 +tags: Brainteaser, DP, Game Theory + +#### Brainteaser +- 著名Nim游戏 +- 写一些,发现n=4,5,6,7,8...etc之后的情况有规律性: 谁先手拿到4就输了. +- 最终很简单n%4!=0就可以了, time, space O(1) + +#### DP +- 正规地找规律做, 就跟 coins in a line 一样, 按照先手后手来做 +- 可以rolling array 优化空间 +- Time O(n), 当然啦, 这个题目这样会timeout, 可以使用brainteaser的做法写出结果. + +``` +/* +You are playing the following Nim Game with your friend: +There is a heap of stones on the table, each time one of you take turns to remove 1 to 3 stones. +The one who removes the last stone will be the winner. +You will take the first turn to remove the stones. + +Both of you are very clever and have optimal strategies for the game. +Write a function to determine whether you can win the game given the number of stones in the heap. + +For example, if there are 4 stones in the heap, +then you will never win the game: no matter 1, 2, or 3 stones you remove, +the last stone will always be removed by your friend. + +Hint: + +If there are 5 stones in the heap, could you figure out a way to remove the stones +such that you will always be the winner? + + +Hide Similar Problems (M) Flip Game II + +*/ + +/* + Thoughts: + If n = 4, we can do the following: + 1 0 0 0 + 1 1 0 0 + 1 1 1 0 + But we'll fail. + + n = 5, we pick 1, 2nd player gets n = 4. + n = 6, we pick 2, 2nd player gets n = 4. + n = 7, we pick 3, 2nd player gets n = 4. + n = 8, regarless whatever we pick, the opponent can make 1st gets n = 4, we fail. + ... + ... + whenever n % 4 = 0, 1st player fail. + +*/ + +public class Solution { + public boolean canWinNim(int n) { + return n % 4 != 0; + } +} + +/* +Whether 1st hand could win or not, depend on what’s left on the opponent’s last move. +dp[i]: true/false if left with i stones. +dp[i] can win, if one of dp[i - 1], dp[i - 2] or dp[i - 3] could lose. +dp[0] = true. +*/ + +class Solution { + public boolean canWinNim(int n ) { + if (n <= 3) { + return true; + } + boolean[] dp = new boolean[n + 1]; + dp[0] = true; + dp[1] = dp[2] = dp[3] = true; + for (int i = 4; i <= n; i++) { + dp[i] = !(dp[i - 1] && dp[i - 2] && dp[i - 3]); + } + return dp[n]; + } +} + +// Rolling stone +class Solution { + public boolean canWinNim(int n ) { + if (n <= 3) { + return true; + } + boolean[] dp = new boolean[4]; + dp[0] = true; + dp[1] = dp[2] = dp[3] = true; + for (int i = 4; i <= n; i++) { + dp[i % 4] = !(dp[(i - 1) % 4] && dp[(i - 2) % 4] && dp[(i - 3) % 4]); + } + return dp[n % 4]; + } +} + + + + + +/* +Thoughts: +Game theory DP. Consider the last step: +for 1st player to win, the opponent needs to have the possibility to lose +(assume 1st player take the chance when seeing one) + +Make dp[i] represents true/false 1st will win, if given i stones. +dp[i] = !dp[i - 1] || !dp[i - 2] || !dp[i - 3]; + +return dp[n - 1] +*/ +class Solution { + public boolean canWinNim(int n) { + if (n <= 3) { + return true; + } + boolean[] dp = new boolean[3]; + dp[0] = dp[1] = dp[2] = true; + for (int i = 3; i < n; i++) { + dp[i % 3] = !dp[(i - 1) % 3] || !dp[(i - 2) % 3] || !dp[(i - 3) % 3]; + } + return dp[(n - 1) % 3]; + } +} + +``` \ No newline at end of file diff --git a/Java/Non-decreasing Array.java b/Java/Non-decreasing Array.java new file mode 100644 index 0000000..707f937 --- /dev/null +++ b/Java/Non-decreasing Array.java @@ -0,0 +1,66 @@ +E +1517548684 +tags: Array + +比较升序的时候, 必须要估计到 i-1, i, i+1三个数位. +写出来i-1, i+1之间的关系, 然后做合理的fix. + +需要真的fix数组, 因为loop through做比较时会用到fix后的数字. + +``` +/* +Given an array with n integers, your task is to check if it could become non-decreasing by modifying at most 1 element. + +We define an array is non-decreasing if array[i] <= array[i + 1] holds for every i (1 <= i < n). + +Example 1: +Input: [4,2,3] +Output: True +Explanation: You could modify the first 4 to 1 to get a non-decreasing array. +Example 2: +Input: [4,2,1] +Output: False +Explanation: You can't get a non-decreasing array by modify at most one element. +Note: The n belongs to [1, 10,000]. + +*/ +/* +Thoughts: +loop over all i, and count the exception. If there are more than 2, return false. +Try not be tricked by the condition that nums[i] < nums[i + 1]. +It actually implies that nums[i - 1] < nums[i] < nums[i + 1], so all 3 numbers need to meet the condition. +If the relationthips between nums[i - 1] && nums[i + 1] is wrong, fix them by chaning one of the 3 numbers: +1. nums[i] > nums[i + 1] && nums[i - 1] > nums[i + 1]: + because numbers ahead if i - 1 is correct, so lower the later, nums[i + 1] = nums[i] +2. nums[i] > nums[i + 1] && nums[i - 1] < nums[i + 1]: + raise nums[i] since it's already greater than nums[i - 1], nums[i] = nums[i + 1] +3. if i == 0 && nums[i] > nums[i + 1], there is no i-1 to worry about, directly count++; +Note: need to write some examples to figure out the edge case +*/ +class Solution { + public boolean checkPossibility(int[] nums) { + if (nums == null || nums.length < 0) { + return false; + } else if (nums.length <= 2) { + return true; + } + int count = 0; + for (int i = 0; i < nums.length - 1; i++) { + if (nums[i] > nums[i + 1]) { + if (i >= 1 && nums[i - 1] < nums[i + 1]) { + nums[i] = nums[i + 1]; + } else if (i >= 1 && nums[i - 1] > nums[i + 1]) { + nums[i + 1] = nums[i]; + } + count++; + } + if (count >= 2) { + return false; + } + } + return true; + } +} + + +``` \ No newline at end of file diff --git a/Java/Nth to Last Node in List.java b/Java/Nth to Last Node in List.java old mode 100644 new mode 100755 index daa135a..05b163d --- a/Java/Nth to Last Node in List.java +++ b/Java/Nth to Last Node in List.java @@ -1,7 +1,12 @@ -先找到nth node -然后head开始跑。 +E +1528180250 +tags: Linked List + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth -node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth ``` /* Find the nth to last element of a singly linked list. diff --git a/Java/Number Of Corner Rectangles.java b/Java/Number Of Corner Rectangles.java new file mode 100644 index 0000000..2d1012e --- /dev/null +++ b/Java/Number Of Corner Rectangles.java @@ -0,0 +1,125 @@ +M +1531465969 +tags: DP, Math + +具体看题目: count # of valid rectangles (four corner are 1) in a grid[][]. + +#### basic thinking + Math +- Fix two rows and count matching columns +- Calculate number rectangles with `combination` concept: +- total number of combinations of pick 2 points randomly: count * (count - 1) / 2 + +#### DP +- TODO. HOW? + +#### Brutle +- O(m^2 * n^2), times out + +``` +/* +Given a grid where each entry is only 0 or 1, find the number of corner rectangles. + +A corner rectangle is 4 distinct 1s on the grid that form an axis-aligned rectangle. +Note that only the corners need to have the value 1. Also, all four 1s used must be distinct. + + + +Example 1: + +Input: grid = +[[1, 0, 0, 1, 0], + [0, 0, 1, 0, 1], + [0, 0, 0, 1, 0], + [1, 0, 1, 0, 1]] +Output: 1 +Explanation: There is only one corner rectangle, with corners grid[1][2], grid[1][4], grid[3][2], grid[3][4]. + + +Example 2: + +Input: grid = +[[1, 1, 1], + [1, 1, 1], + [1, 1, 1]] +Output: 9 +Explanation: There are four 2x2 rectangles, four 2x3 and 3x2 rectangles, and one 3x3 rectangle. + + +Example 3: + +Input: grid = +[[1, 1, 1, 1]] +Output: 0 +Explanation: Rectangles must have four distinct corners. + + +Note: + +The number of rows and columns of grid will each be in the range [1, 200]. +Each grid[i][j] will be either 0 or 1. +The number of 1s in the grid will be at most 6000. +*/ + +// O(M^2 * N), math: Combination concept +class Solution { + public int countCornerRectangles(int[][] grid) { + int rst = 0; + if (validate(grid)) return rst; + int m = grid.length, n = grid[0].length; + + // find 2 rows O(M^2), and pick columns + for (int i = 0; i < m - 1; i++) { + for (int j = i + 1; j < m; j++) { + int count = 0; + for (int k = 0; k < n; k++) { + if (grid[i][k] == 1 && grid[j][k] == 1) count++; + } + if (count > 0) rst += count * (count - 1) / 2; // total # of combination of 2 items: n(n-1)/2 + } + } + return rst; + } + private boolean validate(int[][] grid) { + return grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0; + } +} + +/* +DP. TODO +If fixing a starting point A(0,0), the problem can be reduced to a dp problem +dp[i][j] represents the sum of rectangle from A(0,0) point to (i,j) + +https://leetcode.com/problems/number-of-corner-rectangles/discuss/110200/Summary-of-three-solutions-based-on-three-different-ideas + +*/ + + +// Brutle, timesout +class Solution { + public int countCornerRectangles(int[][] grid) { + int rst = 0; + if (validate(grid)) return rst; + int m = grid.length, n = grid[0].length; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { // grid[i][j], starting point + for (int h = i + 1; h < m; h++) { // pick right-end corner + for (int k = j + 1; k < n; k++) { + rst += validateRect(grid, i, j, h, k) ? 1 : 0; + } + } + } + } + return rst; + } + + private boolean validateRect(int[][] grid, int i, int j, int h, int k) { + return grid[i][j] == 1 && grid[h][k] == 1 && grid[i][k] == 1 && grid[h][j] == 1; + } + + private boolean validate(int[][] grid) { + return grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0; + } +} + +``` \ No newline at end of file diff --git a/Java/Number of Airplane in the sky.java b/Java/Number of Airplane in the sky.java old mode 100644 new mode 100755 index 32ffe56..544976d --- a/Java/Number of Airplane in the sky.java +++ b/Java/Number of Airplane in the sky.java @@ -1,6 +1,25 @@ +M +1521098587 +tags: Array, Interval, Sweep Line, Sort, PriorityQueue + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + +``` + + /* http://www.lintcode.com/en/problem/number-of-airplanes-in-the-sky/ -Given an interval list which are flying and landing time of the flight. How many airplanes are on the sky at most? +Given an interval list which are flying and landing time of the flight. +How many airplanes are on the sky at most? Example For interval list [[1,10],[2,3],[5,8],[4,7]], return 3 @@ -30,43 +49,42 @@ */ class Solution { - class Point { - int x; - int flag; - public Point(int x, int flag) { - this.x = x; - this.flag = flag; - } - } - /** - * @param intervals: An interval array - * @return: Count of airplanes are in the sky. - */ + class Point { + int x; + int flag; + public Point(int x, int flag) { + this.x = x; + this.flag = flag; + } + } + public int countOfAirplanes(List airplanes) { - if (airplanes == null || airplanes.size() == 0) { - return 0; - } - PriorityQueue queue = new PriorityQueue(10, - new Comparator(){ - public int compare(Point a, Point b) { - return a.x - b.x; - } - }); - for (Interval interval : airplanes) { - queue.offer(new Point(interval.start, 1)); - queue.offer(new Point(interval.end, -1)); - } - int count = 0; - int max = 0; - while (!queue.isEmpty()) { - Point p = queue.poll(); - count+= p.flag; - while (!queue.isEmpty() && queue.peek().x == p.x) { - p = queue.poll(); - count += p.flag; - } - max = Math.max(count, max); - } - return max; + if (airplanes == null || airplanes.size() == 0) { + return 0; + } + PriorityQueue queue = new PriorityQueue(10, + new Comparator(){ + public int compare(Point a, Point b) { + return a.x - b.x; + } + }); + for (Interval interval : airplanes) { + queue.offer(new Point(interval.start, 1)); + queue.offer(new Point(interval.end, -1)); + } + int count = 0; + int max = 0; + while (!queue.isEmpty()) { + Point p = queue.poll(); + count+= p.flag; + while (!queue.isEmpty() && queue.peek().x == p.x) {//It handles the case of fly&&land @ same time. Which result in 1 -1 = 0. + p = queue.poll(); + count += p.flag; + } + max = Math.max(count, max); + } + return max; } } + +``` \ No newline at end of file diff --git a/Java/Number of Connected Components in an Undirected Graph.java b/Java/Number of Connected Components in an Undirected Graph.java new file mode 100644 index 0000000..2390073 --- /dev/null +++ b/Java/Number of Connected Components in an Undirected Graph.java @@ -0,0 +1,158 @@ +M +1527744220 +tags: DFS, BFS, Union Find, Graph + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + +``` +/* +Given n nodes labeled from 0 to n - 1 and a list of undirected edges +(each edge is a pair of nodes), write a function to find +the number of connected components in an undirected graph. + +Example 1: + +Input: n = 5 and edges = [[0, 1], [1, 2], [3, 4]] + + 0 3 + | | + 1 --- 2 4 + +Output: 2 +Example 2: + +Input: n = 5 and edges = [[0, 1], [1, 2], [2, 3], [3, 4]] + + 0 4 + | | + 1 --- 2 --- 3 + +Output: 1 +Note: +You can assume that no duplicate edges will appear in edges. +Since all edges are undirected, [0, 1] is the same as [1, 0] and +thus will not appear together in edges. +*/ + +class Solution { + int[] parent; + int count; + // union function + private void union(int a, int b) { + int parentA = parent[a]; + int parentB = parent[b]; + if (parentA != parentB) { + parent[parentA] = parentB; + count--; + } + } + + // find function + private int find(int x) { + if (parent[x] == x) { + return x; + } + return parent[x] = find(parent[x]); + } + + // return count function + private int query() { + return count; + } + + public int countComponents(int n, int[][] edges) { + if (n == 0) { + return 0; + } else if (edges == null || edges.length == 0) { + return n; + } + + count = n; + parent = new int[n]; + for (int i = 0; i < n; i++) { + parent[i] = i; + } + + for (int[] edge : edges) { + int x = edge[0]; + int y = edge[1]; + if (find(x) != find(y)) { + union(x, y); + } + } + + return query(); + } +} + + +/* +DFS with adjacent list +1. build adjacent list graph: map > +2. dfs(graph, visited, int index). visited:boolean[] +*/ + +class Solution { + public int countComponents(int n, int[][] edges) { + if (n == 0) { + return 0; + } else if (edges == null || edges.length == 0) { + return n; + } + + int result = 0; + Map> graph = buildGraph(n, edges); + boolean[] visited = new boolean[n]; + + // dfs(graph, visited, i), and count result + for (int i = 0; i < n; i++) { + if (!visited[i]) { + dfs(graph, visited, i); + result++; + } + } + + return result; + } + + // build graph in form of adjacent list + private Map> buildGraph(int n, int[][] edges) { + Map> graph = new HashMap<>(); + for (int i = 0; i < n; i++) { + if (!graph.containsKey(i)) { + graph.put(i, new ArrayList<>()); + } + } + for (int[] edge: edges) { + graph.get(edge[0]).add(edge[1]); + graph.get(edge[1]).add(edge[0]); + } + return graph; + } + + // dfs: mark visited nodes, and keep dfs into children nodes + private void dfs(Map> graph, boolean[] visited, int i) { + if (visited[i]) { + return; + } + visited[i] = true; + for (int j : graph.get(i)) { + dfs(graph, visited, j); + } + } +} + +``` \ No newline at end of file diff --git a/Java/Number of Digit One.java b/Java/Number of Digit One.java new file mode 100644 index 0000000..218f8a8 --- /dev/null +++ b/Java/Number of Digit One.java @@ -0,0 +1,36 @@ +H +1529474471 +tags: Math + +Pure math problem, not quite representative + +Explanation +https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython + +``` +/* + +Given an integer n, count the total number of digit 1 appearing in all +non-negative integers less than or equal to n. + +Example: + +Input: 13 +Output: 6 +Explanation: Digit 1 occurred in the following numbers: 1, 10, 11, 12, 13. + + */ + + class Solution { + public int countDigitOne(int n) { + int count = 0; + for (long i = 1; i <= n; i *= 10) { + count += (n / i + 8) / 10 * i; + if (n / i % 10 == 1) { + count += n % i + 1; + } + } + return count; + } +} +``` \ No newline at end of file diff --git a/Java/Number of Islands II.java b/Java/Number of Islands II.java old mode 100644 new mode 100755 index 04bf01a..8f16b82 --- a/Java/Number of Islands II.java +++ b/Java/Number of Islands II.java @@ -1,7 +1,264 @@ +H +1520396316 +tags: Union Find + +给一个island grid[][], and list of operations to fill a particualr (x,y) position. + +count # of remaining island after each operation. + +#### Union Find, model with int[] +- 把board转换成1D array, 就可以用union-find来判断了. +- 用int[] father 的unionFind, 需要转换2D position into 1D index. 这样比较clean +- 判断时,是在四个方向各走一步,判断是否是同一个Land. +- 每走一次operator,都会count++. 若发现是同一个island, count-- +- count的加减, 都放在了UnionFind自己的function里面, 方便tracking, 给几个helper function就对了. +- Time: O(k * log(mn)) + +#### Union Find, model with Hashmap +- 用HashMap的Union-find. + + +#### Note: +- Proof of UnionFind log(n) time: https://en.wikipedia.org/wiki/Proof_of_O(log*n)_time_complexity_of_union%E2%80%93find + +``` +/* +A 2d grid map of m rows and n columns is initially filled with water. +We may perform an addLand operation which turns the water at position (row, col) into a land. + +Given a list of positions to operate, count the number of islands after each addLand operation. + +An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. + +You may assume all four edges of the grid are all surrounded by water. + +Example: + +Given m = 3, n = 3, positions = [[0,0], [0,1], [1,2], [2,1]]. +Initially, the 2d grid grid is filled with water. (Assume 0 represents water and 1 represents land). + +0 0 0 +0 0 0 +0 0 0 +Operation #1: addLand(0, 0) turns the water at grid[0][0] into a land. + +1 0 0 +0 0 0 Number of islands = 1 +0 0 0 +Operation #2: addLand(0, 1) turns the water at grid[0][1] into a land. + +1 1 0 +0 0 0 Number of islands = 1 +0 0 0 +Operation #3: addLand(1, 2) turns the water at grid[1][2] into a land. + +1 1 0 +0 0 1 Number of islands = 2 +0 0 0 +Operation #4: addLand(2, 1) turns the water at grid[2][1] into a land. + +1 1 0 +0 0 1 Number of islands = 3 +0 1 0 +We return the result as an array: [1, 1, 2, 3] + +*/ + /* -Given a n,m which means the row and column of the 2D matrix and an array of pair A( size k). Originally, the 2D matrix is all 0 which means there is only sea in the matrix. The list pair has k operator and each operator has two integer A[i].x, A[i].y means that you can change the grid matrix[A[i].x][A[i].y] from sea to island. Return how many island are there in the matrix after each operator. +Thoughts: + +1. UnionFind with count of island: initially 0 island, with addLand, it creates land. +2. Need to union with the 4 directions every time when adding new land. +3. Turn 2D array into 1D, then use unionFind. +4. have query function in UnionFind to get final result. + +Time: O(k logm*n): k = positions.length; union(x,y) time is log(m*n) +https://en.wikipedia.org/wiki/Proof_of_O(log*n)_time_complexity_of_union%E2%80%93find +*/ +class Solution { + public List numIslands2(int m, int n, int[][] positions) { + List rst = new ArrayList<>(); + if (validateInput(m, n, positions)) { + return rst; + } + + int[] dx = {0, 0, 1, -1}; + int[] dy = {1, -1, 0, 0}; + int[][] grid = new int[m][n]; + UnionFind unionFind = new UnionFind(m * n); + + for (int i = 0; i < positions.length; i++) { + int x = positions[i][0]; + int y = positions[i][1]; + if (grid[x][y] == 1) { // no need to fill + continue; + } + grid[x][y] = 1; + unionFind.increaseCount(); + for (int j = 0; j < dx.length; j++) { + int movedX = x + dx[j]; + int movedY = y + dy[j]; + if (validateBorder(grid, movedX, movedY, m, n)) { + unionFind.union(x * n + y, movedX * n + movedY); + } + } + rst.add(unionFind.query()); + } + + return rst; + } + + private boolean validateBorder(int[][] grid, int x, int y, int m, int n) { + return x >= 0 && x < m && y >= 0 && y < n && grid[x][y] == 1; + } + + private boolean validateInput(int m, int n, int[][] positions) { + return m <= 0 || n <= 0 || positions == null || positions.length == 0 || positions[0] == null || positions[0].length == 0; + } +} + +class UnionFind { + int[] father; + int count; + + public UnionFind(int x) { + father = new int[x]; + count = 0; + for (int i = 0; i < x; i++) { + father[i] = i; + } + } + + public void union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootX != rootY) { + father[rootX] = rootY; + count--; + return; + } + } + + public int query() { + return count; + } + + public void increaseCount() { + count++; + } + + private int find(int x) { + if (father[x] == x) { + return x; + } + return father[x] = find(father[x]); + } +} + +/* +Thoughts: +The problem is to find how many disjoint sets there are on the island. We should use union-find approach. +1. Build Union-Find class +2. Greedy approach: every new spot-fill will count++ first. +3. Go 4 directions from the spot, and check if the adjacent spots are islands. +4. If a adjacent spot is island, use union-find to figure out both current && adjacent spot's root parent. +5. If the root parents are different: the are not joined together and count has been over-added, so count-- + +Note1: hugely relying on the default behavior of Union-Find: find parent && be able to union two set. Even though the union-find code is just 3 lines. +Note2: The Union-Find solution we provided is based on 1D array, using a HashMap. So, if we want to use this Union-Find model, +we need to transform the 2D array into 1D array. 1D array postion = x * col + y. +Note3: There can be other ways to model UnionFind, for instance, just use a array: int[] roots = new int[m * n]; +*/ + +class Solution { + class UnionFind { + private HashMap map = new HashMap<>(); + + /* + Model the disjoint set with 1D array + During initialization, assume each spot has itself as the parent + */ + public UnionFind(int size) { + for (int i = 0; i < size; i++) { + map.put(i, i); + } + } + + /* + Use one key and find out the root parent of this set where they key belongs to. + */ + public int findRootParent(int item) { + int parent = map.get(item); + while (parent != map.get(parent)) { + parent = map.get(parent); + } + return parent; + } + + /* + Find the root parent of each item. If the root parent is different, + join them together by adding them into the map as pair. + */ + public void union(int itemX, int itemY) { + int parentX = findRootParent(itemX); + int parentY = findRootParent(itemY); + if (parentX != parentY) { + map.put(parentX, parentY); + } + } + } + + private int dx[] = {1, -1, 0, 0}; + private int dy[] = {0, 0, 1, -1}; + + public List numIslands2(int m, int n, int[][] positions) { + final List result = new ArrayList<>(); + if (positions == null || positions.length == 0 || positions[0].length == 0) { + return result; + } + + int count = 0; + final int[] islands = new int[m * n]; + final UnionFind unionFind = new UnionFind(m * n); + for (int i = 0; i < positions.length; i++) { + // Find current spot and mark as 1. Greedily count++ + int x = positions[i][0]; + int y = positions[i][1]; + int pos = x * n + y; + islands[pos] = 1; + count++; + // Check if pos's adjacent spots has been marked as island; if so, count-- + for (int j = 0; j < dx.length; j++) { + int adjX = x + dx[j]; + int adjY = y + dy[j]; + int adjPos = adjX * n + adjY; + if (adjX >= 0 && adjX < m && adjY >= 0 && adjY < n && islands[adjPos] == 1) { + int currSpotRoot = unionFind.findRootParent(pos); + int adjSpotRoot = unionFind.findRootParent(adjPos); + if (currSpotRoot != adjSpotRoot) { + count--; + unionFind.union(currSpotRoot, adjSpotRoot); + } + } + } + result.add(count); + } + return result; + } + +} + + +/* +LintCode: +Given a n,m which means the row and column of the 2D matrix and an array of pair A( size k). +Originally, the 2D matrix is all 0 which means there is only sea in the matrix. +The list pair has k operator and each operator has two integer A[i].x, A[i].y means +that you can change the grid matrix[A[i].x][A[i].y] from sea to island. + +Return how many island are there in the matrix after each operator. + -Have you met this question in a real interview? Yes Example Given n = 3, m = 3, array of pair A = [(0,0),(0,1),(2,2),(2,1)]. @@ -14,15 +271,16 @@ Union Find */ + /* Thoughts: Each pos(x,y) turns that sea spot into a island spot. -Image each isleand spot is a node in the graph, and each island(many island spots) has a root parent. +Imagine each island spot is a node in the graph, and each island(many island spots) has a root parent. In for loop, try to add operators into the matrix one after another. Every time when adding a new island spot, check its sourandings and see if there are islands existed. - If souranding island was land: + If surrounding spot was island: To check if the surrouding spot are on common island (use find and union). - Since the operator spot was sea, the it's root parent is itself. Then, souranding spot has different island root, + Since the operator spot was sea, then it's root parent is itself. Then, souranding spot has different island root, they will surely have differet root parent, but they will do after they connect, so we do count--. On the otherhand, if surrounding was just sea, then count++ is natural @@ -108,3 +366,68 @@ public List numIslands2(int n, int m, Point[] operators) { return rst; } } + + +/* +Thoughts: +A naive solution would be utilizing numIslands. +It's accurate and brutle. However, it timesout at 155 / 158 test cases. +It's O(mn)*O(k) which could lead to O(n^3) if given n = m = k. +*/ +class Solution { + private int[] dx = {1, -1, 0, 0}; + private int[] dy = {0, 0, 1, -1}; + + public List numIslands2(int m, int n, int[][] positions) { + final List result = new ArrayList<>(); + if (positions == null || positions.length == 0 || positions[0].length == 0) { + return result; + } + char[][] grid = new char[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + grid[i][j] = '0'; + } + } + for (int i = 0; i < positions.length; i++) { + grid[positions[i][0]][positions[i][1]] = '1'; + result.add(numIslands(grid)); + } + return result; + } + + private int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + char[][] localGrid = new char[grid.length][grid[0].length]; + for (int i = 0; i < localGrid.length; i++) { + for (int j = 0; j < localGrid[i].length; j++) { + localGrid[i][j] = grid[i][j]; + } + } + + int count = 0; + for (int i = 0; i < localGrid.length; i++) { + for (int j = 0; j < localGrid[i].length; j++) { + if (localGrid[i][j] == '1') { + count++; + dfs(localGrid, i, j); + } + } + } + return count; + } + + private void dfs(char[][] grid, int x, int y) { + if (x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == '0') { + return; + } + grid[x][y] = '0'; + for (int i = 0; i < dx.length; i++) { + dfs(grid, x + dx[i], y + dy[i]); + } + } +} + +``` \ No newline at end of file diff --git a/Java/Number of Islands.java b/Java/Number of Islands.java old mode 100644 new mode 100755 index 4aeb36d..2c8f4b6 --- a/Java/Number of Islands.java +++ b/Java/Number of Islands.java @@ -1,9 +1,24 @@ -可以用union-find, 就像Number of island II 一样。 -只不过这个不Return list, 而只是# of islands +M +1520355237 +tags: DFS, BFS, Union Find, Matrix DFS + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. -可以自己再做一次。 ``` -/* +/*in Given a boolean 2D matrix, find the number of islands. Example @@ -24,6 +39,236 @@ */ + +/* +Thoughts: +- DFS and flip the bit-1 on the grid to 0 as we go: to 4 different directions +- Loop through all positions +- Visited spots won't be visited again because they are updated to '0' +*/ +class Solution { + private int[] dx = {1, -1, 0, 0}; + private int[] dy = {0, 0, 1, -1}; + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int count = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[i].length; j++) { + if (grid[i][j] == '1') { + count++; + dfs(grid, i, j); + } + } + } + return count; + } + + private void dfs(char[][] grid, int x, int y) { + if (!validateInput(grid, x, y)) { + return; + } + grid[x][y] = '0'; + for (int i = 0; i < dx.length; i++) { + dfs(grid, x + dx[i], y + dy[i]); + } + } + + private boolean validateInput(char[][] grid, int x, int y) { + return x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == '0'; + } +} + +/* +Thoughts: +Similart to ConnectingGraph, and we count # of unions left. +Build UnionFind and let query return # of unions left (isolated island in this problem). +Need to know which island to connect/union, need to go 4 directions. +Convert 2D matrix to 1D index = rowNum * numOfColumn + colNum +*/ +class Solution { + + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) { + return 0; + } + int m = grid.length; + int n = grid[0].length; + + UnionFind unionFind = new UnionFind(m * n); + int totalLandCount = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + totalLandCount += grid[i][j] == '1' ? 1 : 0; + } + } + // # of island blocks, goal is to connect them all. + unionFind.setCount(totalLandCount); + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == '1') { + for (int k = 0; k < dx.length; k++) { // 4 directions + int x = i + dx[k]; + int y = j + dy[k]; + if (!validateInput(grid, x, y)) { + // Attemp to union all of the 4 directions + unionFind.union(convertToIndex(i, j, n), convertToIndex(x, y, n)); + } + } + } + } + } + + return unionFind.query(); + } + + // 1D index = rowNum * numOfColumn + colNum + private int convertToIndex(int x, int y, int rowLength) { + return x * rowLength + y; + } + private boolean validateInput(char[][] grid, int x, int y) { + return x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == '0'; + } +} + +class UnionFind { + int father[] = null; + int count; + + public UnionFind(int n) { + father = new int[n]; + for (int i = 0; i < n; i++) { + father[i] = i; + } + } + + public void union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootX != rootY) { + father[rootX] = rootY; + count--; + } + } + + public int query() { + return count; + } + + public void setCount(int value) { + count = value; + } + + private int find(int x) { + if (father[x] == x) { + return x; + } + return father[x] = find(father[x]); + } +} + + +/* +Thoughts: +UnionFind. +Traverse all points of grid and count the total number of island. See Number of Islands II for details. +Note: need to initialize the 1D array first with all 1's. +Therefore, when we start perform union-find, we already have knowledge of entire island status. + +However, it's not as straight-forward as DFS though. +*/ +class Solution { + class UnionFind { + private HashMap map = new HashMap<>(); + + /* + Model the disjoint set with 1D array + During initialization, assume each spot has itself as the parent + */ + public UnionFind(int size) { + for (int i = 0; i < size; i++) { + map.put(i, i); + } + } + + /* + Use one key and find out the root parent of this set where they key belongs to. + */ + public int findRootParent(int item) { + int parent = map.get(item); + while (parent != map.get(parent)) { + parent = map.get(parent); + } + return parent; + } + + /* + Find the root parent of each item. If the root parent is different, + join them together by adding them into the map as pair. + */ + public void union(int itemX, int itemY) { + int parentX = findRootParent(itemX); + int parentY = findRootParent(itemY); + if (parentX != parentY) { + map.put(parentX, parentY); + } + } + } + + private int[] dx = {1, -1, 0, 0}; + private int[] dy = {0, 0, 1, -1}; + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int count = 0; + int m = grid.length; + int n = grid[0].length; + final int[] islands = new int[m * n]; + final UnionFind unionFind = new UnionFind(m * n); + // Initialize island + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == '1') { + int pos = i * n + j; + islands[pos] = 1; + count++; + } + } + } + // Merge island and decrease count if on merged island + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + int pos = i * n + j; + if (islands[pos] != 1) { + continue; + } + for (int k = 0; k < dx.length; k++) { + int adjX = i + dx[k]; + int adjY = j + dy[k]; + int adjPos = adjX * n + adjY; + if (adjX >= 0 && adjX < m && adjY >= 0 && adjY < n && islands[adjPos] == 1) { + int currSpotRoot = unionFind.findRootParent(pos); + int adjSpotRoot = unionFind.findRootParent(adjPos); + if (currSpotRoot != adjSpotRoot) { + count--; + unionFind.union(currSpotRoot, adjSpotRoot); + } + } + + } + } + } + return count; + } +} + /* 12.12.2015 recap We are checking if a sets of adjacent nodes are int the same set @@ -79,4 +324,36 @@ public boolean check(int x, int y, int mark, boolean[][] grid) { return false; } } + + +//from leetcode: +public class Solution { + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int count = 0; + for(int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + count = mark(i, j, grid)? (count + 1) : count; + } + } + return count; + } + + public boolean mark(int i, int j, char[][] grid) { + if (i >= 0 && i < grid.length && j >= 0 && j < grid[0].length) { + if (grid[i][j] == '1') { + grid[i][j] = 'M'; + mark(i - 1, j, grid); + mark(i + 1, j, grid); + mark(i, j - 1, grid); + mark(i, j + 1, grid); + return true; + } + } + return false; + } +} ``` \ No newline at end of file diff --git a/Java/Number of Longest Increasing Subsequence.java b/Java/Number of Longest Increasing Subsequence.java new file mode 100644 index 0000000..bd43280 --- /dev/null +++ b/Java/Number of Longest Increasing Subsequence.java @@ -0,0 +1,113 @@ +M +1531969906 +tags: DP, Coordinate DP +time: O(n^2) +time: O(n) + +给一串 unsorted sequence, 找到长 increasing subsequence 的个数! + +#### Coordinate DP +- 需要能够判断综合题, 分清楚情况和套路: combination of `longest subsequence` and `ways to do`, as well as global variable. +- len[i] (我们平时的dp[i]): 在前i个元素中, 最长的 increasing subsequence length; +- count[i]: 在前i个元素中, 并且以 len[i]这个长度为准的 subsequence的 count. 或者: 在前i个元素中, ways to reach longest increasing subsequence. +- `len[i] == len[j] + 1`: same length, but different sequence, so add all `count[i] += count[j]` +- `len[i] < len[j] + 1`: 这就是更长的情况找到了, 那么有多少次 count[j] 有多少, count[i] 就有多少. 仔细想sequence: 长度增长了, 但是ways to reach i 没有增长. +- 同样的判断需要用在 maxLen 和 maxFreq上: +- 如果没有增长 maxLen 不变, maxFreq上面需要 +=count[i] (同一种长度, 多了更多的做法) +- 如果maxLen 变长, maxFreq 也就是采用了 count[i] = count[j] +- TODO: Is rolling array possible? + +#### 相关 +- 都是 Coordiate DP, DP的鼻祖家族: +- Longest Increasing Subsequence (跟这道题的一部分一模一样) +- Longest Continuous Increasing Subsequence (连续, 只check dp[i - 1]) +- Longest Increasing Continuous Subsequence I, II (Lintcode, II 是matrix) + +``` +/** +Given an unsorted array of integers, find the number of longest increasing subsequence. + +Example 1: +Input: [1,3,5,4,7] +Output: 2 +Explanation: The two longest increasing subsequence are [1, 3, 4, 7] and [1, 3, 5, 7]. +Example 2: +Input: [2,2,2,2,2] +Output: 5 +Explanation: The length of longest continuous increasing subsequence is 1, +and there are 5 subsequences' length is 1, so output 5. + + + */ + +// Coordiate DP +class Solution { + public int findNumberOfLIS(int[] nums) { + if (nums == null || nums.length == 0) return 0; + + int n = nums.length, maxLen = 0, maxFreq = 0; + int[] len = new int[n]; + int[] count = new int[n]; + + // process len + for (int i = 0; i < n; i++) { + len[i] = 1; + count[i] = 1; + for (int j = 0; j < i; j++) { + if (nums[j] < nums[i]) { + if (len[i] == len[j] + 1) { + count[i] += count[j]; + } else if (len[i] < len[j] + 1) { + len[i] = len[j] + 1; + count[i] = count[j]; + } + } + } + if (maxLen == len[i]) { + maxFreq += count[i]; + } else if (maxLen < len[i]) { + maxLen = len[i]; + maxFreq = count[i]; + } + } + + return maxFreq; + } +} + +// 做成了sequence dp, 其实没必要 +class Solution { + public int findNumberOfLIS(int[] nums) { + if (nums == null || nums.length == 0) return 0; + + int n = nums.length, maxLen = 0, maxFreq = 0; + int[] len = new int[n + 1]; // len[0] = 0 + int[] count = new int[n + 1]; + + // process len + for (int i = 1; i <= n; i++) { + len[i] = 1; + count[i] = 1; + for (int j = 1; j < i; j++) { + if (nums[j - 1] < nums[i - 1]) { + if (len[i] == len[j] + 1) { + count[i] += count[j]; + } else if (len[i] < len[j] + 1) { + len[i] = len[j] + 1; + count[i] = count[j]; + } + } + } + if (maxLen == len[i]) { + maxFreq += count[i]; + } else if (maxLen < len[i]) { + maxLen = len[i]; + maxFreq = count[i]; + } + } + + return maxFreq; + } +} + +``` \ No newline at end of file diff --git a/Java/O(1) Check Power of 2.java b/Java/O(1) Check Power of 2.java old mode 100644 new mode 100755 index 8da73e3..38b92cd --- a/Java/O(1) Check Power of 2.java +++ b/Java/O(1) Check Power of 2.java @@ -1,3 +1,8 @@ +E +tags: Bit Manipulation + +``` + /* Using O(1) time to check whether an integer n is a power of 2. Example @@ -32,3 +37,5 @@ public boolean checkPowerOf2(int n) { }; + +``` \ No newline at end of file diff --git a/Java/One Edit Distance.java b/Java/One Edit Distance.java new file mode 100755 index 0000000..fef2e5f --- /dev/null +++ b/Java/One Edit Distance.java @@ -0,0 +1,106 @@ +M +1533623972 +tags: String + +如果S, T只用一个operation就能变成相等, return true. + +#### Edit: 删除,增加,和替换 +- 换完之后,理论上换成的String 就应该全等 +- for loop, 一旦找到不一样的char, 就判断那三种可能性: insert/delete/replace +- insert/delete 对于2个string来说, 效果是类似的 +- O(n) + +``` +/* +Given two strings S and T, determine if they are both one edit distance apart. + +*/ +class Solution { + public boolean isOneEditDistance(String s, String t) { + if (s == null || t == null || s.equals(t) || Math.abs(s.length() - t.length()) > 1) { + return false; + } + int size = Math.min(s.length(), t.length()); + for (int i = 0; i < size; i++) { + if (s.charAt(i) != t.charAt(i)) { // check insert/delete or replace + return compareStr(s, t, i + 1, i) || compareStr(s, t, i, i + 1) || compareStr(s, t, i + 1, i + 1); + } + } + + return true; // size == 0, and length diff == 1 + } + + private boolean compareStr(String s, String t, int x, int y) { + return s.substring(x).equals(t.substring(y)); + } +} + +/* +Thoughts: +Insert/Delete/Replace. +Insert/Delete have similar effect regarding the 2 strings. +Loop over s and t. When there is diff, apply one of the operations, compare && return +*/ +class Solution { + public boolean isOneEditDistance(String s, String t) { + if (s == null || t == null || s.equals(t) || Math.abs(s.length() - t.length()) > 1) { + return false; + } + int size = s.length() > t.length() ? t.length() : s.length(); + for (int i = 0; i < size; i++) { + if (s.charAt(i) != t.charAt(i)) { + // insert/delete + if (s.substring(i + 1).equals(t.substring(i)) || s.substring(i).equals(t.substring(i + 1))) { + return true; + } + // check replace + return s.substring(i + 1).equals(t.substring(i + 1)); + } + } + // size == 0, and length diff == 1 + return true; + } +} + +/* + Thoughts: + One edit distance: delete, insert, and substitude. + For loop. Whenever differenct, chech 3 cases and return + + Note: null cannot become "" or "a", so whenever s or t is null, return false; +*/ + +public class Solution { + public boolean isOneEditDistance(String s, String t) { + if (s == null || t == null) { + return false; + } else if (Math.abs(s.length() - t.length()) > 1 || s.equals(t)) { + return false; + } + int leng = s.length() > t.length() ? t.length() : s.length(); + for (int i = 0; i < leng; i++) { + String ss = s; + String tt = t; + if (s.charAt(i) != t.charAt(i)) { + //Check delete + tt = t.substring(i + 1); + ss = s.substring(i); + if (tt.equals(ss)) { + return true; + } + //Check insert + tt = t.substring(i); + ss = s.substring(i + 1); + if (tt.equals(ss)) { + return true; + } + //check substitude + ss = s.substring(0, i) + s.substring(i + 1); + tt = t.substring(0, i) + t.substring(i + 1); + return ss.equals(tt); + } + }//end for + return true; + } +} +``` \ No newline at end of file diff --git a/Java/Ones and Zeroes.java b/Java/Ones and Zeroes.java new file mode 100644 index 0000000..b294ac6 --- /dev/null +++ b/Java/Ones and Zeroes.java @@ -0,0 +1,190 @@ +H +1519371920 +tags: DP + +还是Double Sequence, 但是考虑第三种状态: 给的string array的用量. +所以开了3维数组. + +如果用滚动数组优化空间, 需要把要滚动的那个for loop放在最外面, 而不是最里面. +当然, 这个第三位define在 dp[][][]的哪个位置, 问题都不大. + +另外, 注意在外面calcualte zeros and ones, 节约时间复杂度. + +``` +/* +In the computer world, use restricted resource you have +to generate maximum benefit is what we always want to pursue. + +For now, suppose you are a dominator of m 0s and n 1s respectively. +On the other hand, there is an array with strings consisting of only 0s and 1s. + +Now your task is to find the maximum number of strings that you can form +with given m 0s and n 1s. Each 0 and 1 can be used at most once. + +Note: +The given numbers of 0s and 1s will both not exceed 100 +The size of given string array won't exceed 600. +Example 1: +Input: Array = {"10", "0001", "111001", "1", "0"}, m = 5, n = 3 +Output: 4 +Explanation: This are totally 4 strings can be formed by the using of 5 0s and 3 1s, +which are “10,”0001”,”1”,”0” + +Example 2: +Input: Array = {"10", "0", "1"}, m = 1, n = 1 +Output: 2 +Explanation: You could form "10", but then you'd have nothing left. Better form "0" and "1". +*/ + + +/* +Thoughts: +We need to track: +how many 0's being used: i +how many 1's being used: j +how many items being formed/taken from the strs array: k + +dp[i][j][k]: given i 0's, j 1's and k items from the array, what's max# of strings can we form? + +for instance if strs[k - 1] = '0011' => dp[i][j][k] = 1 + dp[i - 2][j - 2][k - 1] | i >= 2, j >= 2 + +dp[0][0][0] = 0; // no 0 or 1 and no item => 0 +dp[0][0][k] = 0; // no 0 or 1 => 0 +dp[i][j][0] = 0; // no items =>0 + +Two possible conditions: +1. can't match, dp[i][j][k] = dp[i][j][k - 1]. 0's and 1's are not being used, but moving on to next item, k-- +2. find a match, but have to make sure remaining balance of 0's and 1's is not negative +dp[i][j][k] = Math.max(dp[i][j][k - 1], dp[i - countZero][j - countOne][k - 1] + 1| i >=countZero && j>=countOne); + +Space: O(MNK) +Time: O(MNK) +*/ + +class Solution { + public int findMaxForm(String[] strs, int m, int n) { + if (strs == null || strs.length == 0 || (m == 0 && n == 0)) { + return 0; + } + int len = strs.length; + int[][][] dp = new int[m + 1][n + 1][len + 1]; + + // Count zeros and ones. space O(K), time O(KH), H is longest string length; + int[] ones = new int[len]; + int[] zeros = new int[len]; + for (int k = 0; k < len; k++) { + char[] ss = strs[k].toCharArray(); + for (char c : ss) { + ones[k] += c == '1' ? 1 : 0; + zeros[k] += c == '0' ? 1 : 0; + } + } + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + for (int k = 0; k <= len; k++) { + if (k == 0 || (i == 0 && j == 0)) { + dp[i][j][k] = 0; + continue; + } + dp[i][j][k] = dp[i][j][k - 1]; // no matching + int countZero = zeros[k - 1]; + int countOne = ones[k - 1]; + if (i >= countZero && j >= countOne) { + dp[i][j][k] = Math.max(dp[i][j][k], dp[i - countZero][j - countOne][k - 1] + 1); + } + } + } + } + return dp[m][n][len]; + } +} + +/** +Optimization. +Note, for rolling array to work properly, it's best to have k-loop at top level. +Now Space O(MN), time is still O(MNK) + */ +class Solution { + public int findMaxForm(String[] strs, int m, int n) { + if (strs == null || strs.length == 0 || (m == 0 && n == 0)) { + return 0; + } + int len = strs.length; + int[][][] dp = new int[m + 1][n + 1][2]; + // rolling array + int curr = 1; + int prev = 0; + + // Count zeros and ones. space O(K), time O(KH), H is longest string length; + int[] ones = new int[len]; + int[] zeros = new int[len]; + for (int k = 0; k < len; k++) { + char[] ss = strs[k].toCharArray(); + for (char c : ss) { + ones[k] += c == '1' ? 1 : 0; + zeros[k] += c == '0' ? 1 : 0; + } + } + for (int k = 0; k <= len; k++) { + prev = curr; + curr = 1 - prev; + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (k == 0 || (i == 0 && j == 0)) { + dp[i][j][curr] = 0; + continue; + } + dp[i][j][curr] = dp[i][j][prev]; // no matching + int countZero = zeros[k - 1]; + int countOne = ones[k - 1]; + if (i >= countZero && j >= countOne) { + dp[i][j][curr] = Math.max(dp[i][j][curr], dp[i - countZero][j - countOne][prev] + 1); + } + } + } + } + return dp[m][n][curr]; + } +} + + +/* +Original +Space: O(MNK) +Time: O(MNKH), where H is the longest string length. + */ +class Solution { + public int findMaxForm(String[] strs, int m, int n) { + if (strs == null || strs.length == 0 || (m == 0 && n == 0)) { + return 0; + } + int len = strs.length; + int[][][] dp = new int[m + 1][n + 1][len + 1]; + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + for (int k = 0; k <= len; k++) { + if (k == 0 || (i == 0 && j == 0)) { + dp[i][j][k] = 0; + continue; + } + // Count + char[] ss = strs[k - 1].toCharArray(); + int countOne = 0; + int countZero = 0; + for (char c : ss) { + countOne += c == '1' ? 1 : 0; + countZero += c == '0' ? 1 : 0; + } + dp[i][j][k] = dp[i][j][k - 1]; // no matching + if (i >= countZero && j >= countOne) { + dp[i][j][k] = Math.max(dp[i][j][k], dp[i - countZero][j - countOne][k - 1] + 1); + } + } + } + } + return dp[m][n][len]; + } +} +``` \ No newline at end of file diff --git a/Java/Paint Fence.java b/Java/Paint Fence.java new file mode 100755 index 0000000..c26e6f5 --- /dev/null +++ b/Java/Paint Fence.java @@ -0,0 +1,118 @@ +E +1531701138 +tags: DP, Sequence DP +time: O(n) +space: O(n) + +#### DP +- 最多2个fence 颜色相同 +- 假设i是和 i-1不同,那么结果就是 (k-1)*dp[i - 1] +- 假设i是何 i-1相同,那么根据条件,i-1和i-2肯定不同。那么所有的结果就是(k-1)*dp[i-2] +- dp[i]: count # of ways to paint 前i个 fence +- 加法原理 +- time, space: O(n) +- rolling array: space O(1) + +#### Previous Notes +- 这题目很有意思. 一开始分析的太复杂, 最后按照这个哥们的想法(http://yuanhsh.iteye.com/blog/2219891) 的来做,反而简单了许多。 +- 设定T(n)的做法,最后题目化简以后就跟Fibonacci number一样一样的。详细分析如下。 +- 做完,还是觉得如有神。本来是个Easy题,想不到,就是搞不出。 + + +``` +/* +There is a fence with n posts, each post can be painted with one of the k colors. + +You have to paint all the posts such that no more than two adjacent fence posts have the same color. + +Return the total number of ways you can paint the fence. + +Note: +n and k are non-negative integers. + +Tags: Dynamic Programming +Similar Problems: (E) House Robber, (M) House Robber II, (M) Paint House, (H) Paint House II + +*/ + + +/* +Thoughts: +dp[i]: i ways to paint first i posts. +dp[i] depends on dp[i-1] or dp[i - 2]: no more than 2 adjacent same color means: dp[i] will differ from dp[i-1] or dp[i-2] +The remain color will be k +*/ +class Solution { + public int numWays(int n, int k) { + // edge case + if (n <= 1 || k <= 0) { + return n * k; + } + // init dp[n+1] + int[] dp = new int[n + 1]; + dp[0] = 0; + dp[1] = k; + dp[2] = k + k * (k - 1); // [1,2] same color + [1,2] diff color + + // process + for (int i = 3; i <= n; i++) { + dp[i] = (k - 1) * (dp[i - 1] + dp[i - 2]); + } + + return dp[n]; + } +} + +// Rolling array, space O(1) +class Solution { + public int numWays(int n, int k) { + // edge case + if (n <= 1 || k <= 0) { + return n * k; + } + // init dp[n+1] + int[] dp = new int[3]; + dp[0] = 0; + dp[1] = k; + dp[2] = k + k * (k - 1); + + // process + for (int i = 3; i <= n; i++) { + dp[i % 3] = (k - 1) * (dp[(i - 1) % 3] + dp[(i - 2) % 3]); + } + + return dp[n % 3]; + } +} + +/* +Thoughts: +Inspiration(http://yuanhsh.iteye.com/blog/2219891) +Consider posts from 1 ~ n. Now we look at last post, marked n: +S(n) means: last 2 fence posts have same color. + Note: S(n) will equal to whatever that's on n-1 position. + Also, just because n and n-1 are same, that means n-2 and n-1 have to be differnet. +SO: +S(n) = D(n - 1) +D(n) means: last 2 fence posts have different color. + Note: for n - 1, and n-2 positions, we have 2 different conditions: + For example: xxy, or wxy, same 2 x's or different w vs. x. +So: +D(n) = (k - 1) * (D(n - 1) + S(n - 1)) + +We can also create T(n) = S(n) + D(n); //T(n) is our totoal results. Will need to return T(n); +Use above equations to figure out T(n) +T(n) = S(n) + D(n) = D(n - 1) + (k - 1) * (D(n - 1) + S(n - 1)) + = D(n - 1) + (k - 1)(T(n - 1)) + = (k - 1) * (D(n - 2) + S(n - 2)) + (k - 1)(T(n - 1)) + = (k - 1)(T(n - 1) + T(n - 2)) + Since n-2 >=1, so n>=3. We need fiture out cases for n = 0,1,2,3 + +Note: +n == 1: just k ways +n == 0: just 0. +k == 0: just 0; +Besides these cases, we are okay. Btw, k does not really matter as long as it's >=1, it can be plugged in. +*/ + +``` \ No newline at end of file diff --git a/Java/Paint House II.java b/Java/Paint House II.java new file mode 100644 index 0000000..b692795 --- /dev/null +++ b/Java/Paint House II.java @@ -0,0 +1,226 @@ +H +1523336609 +tags: DP, Sequence DP, Status DP +time: O(NK^2): +space: (NK) + +一排n个房子, 每个房子可涂成k种颜色, 涂每个房子的价钱不一样, 用costs[][]表示. + +costs[0][1]表示涂了index是0的房子, 用了color 1. + +规则: 相邻的两个房子不能使同一种颜色 + +求: 最少的cost + +#### DP +- 跟Paint House I 几乎一模一样, 只不过paint color更多了: k colors. +- 先考虑单纯地用dp[i]表示涂前 i 个房子的最小cost +- 但是 dp[i] 和 dp[i-1] 两个index选什么颜色会互相影响, 难讨论, 于是加状态: 序列DP被加了状态变成2D. +- 考虑最后位, 而前一位i-1又被i位的颜色限制, 于是在考虑 min dp[i] 时候, 又多了一层iteration. +- 做dp[i][j]: # cost for 前 i 个房子, 所以要先pick (i-1) 房子的cost, 然后在找出 (i-2)房子的cost +- K种颜色 => O(NK^2) +- 如果不优化, 跟Paint House I 几乎是一模一样的代码 +- Time O(NK^2), space(NK) +- Rolling array: reduce space to O(K) + +#### 注意 +- 序列型dp[i]表示'前i-1个'的结果. 所以dp最好设定为 int[n + 1] size. +- 然而, 颜色在这里是状态, 所以保留在 j: [ 0~k) +- [[8]] 这样的edge case. 跑不进for loop, 所以特殊handle. + +#### Optimization Solution +- Time: O(NK) +- 如果已知每次都要从cost里面选两个不同的最小cost,那么先把最小两个挑出来, 就不必有第三个for loop 找 min +- 每次在数列里面找: 除去自己之外的最小值, 利用最小值/次小值的思想 +- 维持2个最值: 最小值/次小值. +- 计算的时候, 如果除掉的不是最小值的index, 就给出最小值; 如果除掉的是最小值的index, 就给出次小值. +- Every loop: 1. calculate the two min vlaues for each i; 2. calcualte dp[i][j] +- 如何想到优化: 把表达式写出来, 然后看哪里可以优化 +- 另外, 还是可以rolling array, reduce space complexity to O(K) + +``` + +/* +There are a row of n houses, each house can be painted with one of the k colors. +The cost of painting each house with a certain color is different. +You have to paint all the houses such that no two adjacent houses have the same color. + +The cost of painting each house with a certain color is represented by a n x k cost matrix. +For example, costs[0][0] is the cost of painting house 0 with color 0; +costs[1][2] is the cost of painting house 1 with color 2, and so on... + +Find the minimum cost to paint all houses. + +Note: +All costs are positive integers. + +Follow up: +Could you solve it in O(nk) runtime? +*/ + + +/* +Thoughts: +This generial approach O(nK^2) has almost identical code as Paint House I. +Min cost, DP. +If dp[i] represents the min cost of painting leading i-1 houses, then consider how to build dp[i]: +It'll be dp[i - 1] + int[i][?] cost. However, we don't know what was choen on index i -1, so it may require a round of traverse. Think about storing the status. +dp[i][j], represents the min of the leading (i - 1) houses' cost, also at index i - 1, color j was chosen + +if chosen j at index i, loop over all possibilities at i - 1 index. +dp[i][j] = Math.max(dp[i - 1][0 ~ k] + costs[i][j]) +*/ +class Solution { + public int minCostII(int[][] costs) { + if (costs == null || costs.length == 0) { + return 0; + } else if (costs.length == 1 && costs[0].length == 1) { + return costs[0][0]; + } + int minCost = Integer.MAX_VALUE; + int n = costs.length; + int k = costs[0].length; + int[][] dp = new int[n + 1][k]; + // Paint 0 houese should be initialized with 0 cost + for (int j = 0; j < k; j++) { + dp[0][j] = 0; + } + + for (int i = 1; i <= n; i++) { // iterate over house # + for (int j = 0; j < k; j++) { // choose color j for dp[i] + dp[i][j] = Integer.MAX_VALUE; + for (int m = 0; m < k; m++) { // choose adjacent color for dp[i-1] + if (m == j) { + continue; + } + dp[i][j] = Math.min(dp[i][j], dp[i - 1][m] + costs[i - 1][j]); + } + if (i == n) { + minCost = Math.min(minCost, dp[i][j]); + } + } + + } + return minCost; + } +} + +// space optimization, rolling array, O(k) space +class Solution { + public int minCostII(int[][] costs) { + if (costs == null || costs.length == 0) { + return 0; + } else if (costs.length == 1 && costs[0].length == 1) { + return costs[0][0]; + } + int minCost = Integer.MAX_VALUE; + int n = costs.length, k = costs[0].length; + int[][] dp = new int[2][k]; // dp[0][j] = 0; Paint 0 house, for j=[0 ~ k) + + for (int i = 1; i <= n; i++) { // iterate over house # + for (int j = 0; j < k; j++) { // choose color j for dp[i] + dp[i % 2][j] = Integer.MAX_VALUE; + for (int m = 0; m < k; m++) { // choose adjacent color for dp[i-1] + if (m == j) continue; + dp[i % 2][j] = Math.min(dp[i % 2][j], dp[(i - 1) % 2][m] + costs[i - 1][j]); + } + if (i == n) minCost = Math.min(minCost, dp[i % 2][j]); + } + } + return minCost; + } +} + +/* +Thoughts: +Optimization. +The rule is adjacent 2 houses cannot be in same color, +which means, they can be chosen from the lowest two costs. +If we know the lowest 2 ahead of the 3-level-for loop, we can reduce the most-inner loop. + +*/ +class Solution { + public int minCostII(int[][] costs) { + if (costs == null || costs.length == 0) { + return 0; + } else if (costs.length == 1 && costs[0].length == 1) { + return costs[0][0]; + } + int minCost = Integer.MAX_VALUE; + int n = costs.length; + int k = costs[0].length; + int[][] dp = new int[n + 1][k]; + // Paint 0 houese should be initialized with 0 cost + for (int j = 0; j < k; j++) { + dp[0][j] = 0; + } + + for (int i = 1; i <= n; i++) { // iterate over house # + // Find minSecond and min indexes + // min value: dp[i - 1][minIndex] + // 2nd min value: dp[i - 1][minSecIndex] + int minIndex = -1; + int minSecIndex = -1; + for (int j = 0; j < k; j++) { + if (minIndex == -1 || dp[i - 1][j] < dp[i - 1][minIndex]) { + minSecIndex = minIndex; + minIndex = j; + } else if (minSecIndex == -1 || dp[i - 1][j] < dp[i - 1][minSecIndex]) { + minSecIndex = j; + } + } + + // DP Processing + for (int j = 0; j < k; j++) { // choose color for house i - 1 + if (j == minIndex) { // if color at minIndex is chosen for dp[i], then the remaining min is at minSecIndex + dp[i][j] = dp[i - 1][minSecIndex] + costs[i - 1][j]; + } else { // if color is not chosen at minIndex, minIndex will represent the overall min + dp[i][j] = dp[i - 1][minIndex] + costs[i - 1][j]; + } + + if (i == n) { + minCost = Math.min(minCost, dp[i][j]); + } + } + + } + return minCost; + } +} + +// Rolling array, O(k) space, a little bit slower because of the %2 +class Solution { + public int minCostII(int[][] costs) { + if (costs == null || costs.length == 0) { + return 0; + } else if (costs.length == 1 && costs[0].length == 1) { + return costs[0][0]; + } + int minCost = Integer.MAX_VALUE; + int n = costs.length, k = costs[0].length; + int[][] dp = new int[2][k]; // dp[0][j] = 0; for j=[0 ~ k) + + for (int i = 1; i <= n; i++) { + int minIndex = -1, minSecIndex = -1; + for (int j = 0; j < k; j++) { + if (minIndex == -1 || dp[(i - 1) % 2][j] < dp[(i - 1) % 2][minIndex]) { + minSecIndex = minIndex; + minIndex = j; + } else if (minSecIndex == -1 || dp[(i - 1) % 2][j] < dp[(i - 1) % 2][minSecIndex]) { + minSecIndex = j; + } + } + + // DP Processing + for (int j = 0; j < k; j++) { + if (j == minIndex) dp[i % 2][j] = dp[(i - 1) % 2][minSecIndex] + costs[i - 1][j]; + else dp[i % 2][j] = dp[(i - 1) % 2][minIndex] + costs[i - 1][j]; + + if (i == n) minCost = Math.min(minCost, dp[i % 2][j]); + } + } + return minCost; + } +} + +``` \ No newline at end of file diff --git a/Java/Paint House.java b/Java/Paint House.java new file mode 100644 index 0000000..ffee047 --- /dev/null +++ b/Java/Paint House.java @@ -0,0 +1,164 @@ +E +1522770984 +tags: DP, Sequence DP, Status DP +time: O(nm), m = # of colors +space: O(nm) + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, 但是不知道最后一个房子选什么颜色, 那么就遍历最后一个房子(i - 1)的颜色 +- 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- 考虑DP最后一个位置的情况(颜色选择):需要附带颜色status在DP[i]上: 定义二维数组, 其中一位是status +- dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. +- dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- 然后选ith 房子的 color, 再选(i-1)th 房子的color. Double for loop, skip same color + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 + +``` +/* +There are a row of n houses, each house can be painted with one of the three colors: +red, blue or green. The cost of painting each house with a certain color is different. +You have to paint all the houses such that no two adjacent houses have the same color. + +The cost of painting each house with a certain color is represented by a n x 3 cost matrix. +For example, costs[0][0] is the cost of painting house 0 with color red; +costs[1][2] is the cost of painting house 1 with color green, and so on... + +Find the minimum cost to paint all houses. + +Note: +All costs are positive integers. + + */ +/* +Thoughts: +Consider last step: what color was chosen, +and what options can be taken from costs to find minimum +dp[i][0~2]: cost of paiting for the i houses, when last house patined with costs[i][0, 1, or 2] +*/ +class Solution { + public int minCost(int[][] costs) { + if (costs == null || costs.length == 0 || costs[0] == null || costs[0].length == 0) { + return 0; + } + int n = costs.length; // n houses + int m = costs[0].length; + int[][] dp = new int[n + 1][m]; + dp[0][0] = dp[0][1] = dp[0][2] = 0; + + for (int i = 1; i <= n; i++) { + for (int j = 0; j < m; j++) {// Color of house i-1, frin dp[i][j] + dp[i][j] = Integer.MAX_VALUE; + for (int k = 0; k < m; k++) {// Color of house i - 2, from dp[i][j - 1] + if (j == k) { // avoid same color + continue; + } + dp[i][j] = Math.min(dp[i][j], dp[i - 1][k] + costs[i - 1][j]); + } + } + } + + int min = Integer.MAX_VALUE; + for (int j = 0; j < m; j++) { + min = Math.min(min, dp[n][j]); + } + return min; + } +} + +// Rolling Array, optimize space +class Solution { + public int minCost(int[][] costs) { + if (costs == null || costs.length == 0 || costs[0] == null || costs[0].length == 0) { + return 0; + } + int n = costs.length; // n houses + int m = costs[0].length; + int[][] dp = new int[2][m]; + dp[0][0] = dp[0][1] = dp[0][2] = 0; + + for (int i = 1; i <= n; i++) { + for (int j = 0; j < m; j++) {// Color of house i-1, frin dp[i][j] + dp[i % 2][j] = Integer.MAX_VALUE; + for (int k = 0; k < m; k++) {// Color of house i - 2, from dp[i][j - 1] + if (j == k) { // avoid same color + continue; + } + dp[i % 2][j] = Math.min(dp[i % 2][j], dp[(i - 1) % 2][k] + costs[i - 1][j]) ; + } + } + } + + int min = Integer.MAX_VALUE; + for (int j = 0; j < m; j++) { + min = Math.min(min, dp[n % 2][j]); + } + return min; + } +} + +/* +Thgouths: +Find minimum, think of DP. +Think of last house: +if red, the front options are blue/green +if green, the front options are red/blue +if blue, the front options are red/green + +If define dp[i] : min cost to paint to house i. +min(dp[i - 1] + redCost, dp[i - 1] + blueCost, dp[i - 1] + greenCost) + +we don't know the status of last selection: blue/green/red? +redefine dp[i][j]: choose color j and the minimum is? + +init: +dp[0][0] = costs[0][0]; +dp[0][1] = costs[0][1]; +dp[0][2] = costs[0][2]; + +*/ +class Solution { + public int minCost(int[][] costs) { + if (costs == null || costs.length == 0 || costs[0].length == 0) { + return 0; + } + int n = costs.length; + int[][] dp = new int[n][3]; + dp[0][0] = costs[0][0]; + dp[0][1] = costs[0][1]; + dp[0][2] = costs[0][2]; + + for (int i = 1; i < n; i++) { + dp[i][0] = costs[i][0] + Math.min(dp[i - 1][1], dp[i - 1][2]); + dp[i][1] = costs[i][1] + Math.min(dp[i - 1][0], dp[i - 1][2]); + dp[i][2] = costs[i][2] + Math.min(dp[i - 1][0], dp[i - 1][1]); + } + return Math.min(Math.min(dp[n - 1][0], dp[n - 1][1]), dp[n - 1][2]); + } +} + +// little optimize, not necessary +class Solution { + public int minCost(int[][] costs) { + if (costs == null || costs.length == 0 || costs[0].length == 0) { + return 0; + } + int n = costs.length; + int[][] dp = new int[n][3]; + dp[0][0] = costs[0][0]; + dp[0][1] = costs[0][1]; + dp[0][2] = costs[0][2]; + + for (int i = 1; i < n; i++) { + for (int j = 0 ; j < 3; j++) { + dp[i][j] = costs[i][j] + Math.min(dp[i - 1][(j + 1)%3], dp[i - 1][(j + 2)%3]); + } + } + return Math.min(Math.min(dp[n - 1][0], dp[n - 1][1]), dp[n - 1][2]); + } +} +``` \ No newline at end of file diff --git a/Java/Palindrome Linked List.java b/Java/Palindrome Linked List.java old mode 100644 new mode 100755 index 418294b..da2c17f --- a/Java/Palindrome Linked List.java +++ b/Java/Palindrome Linked List.java @@ -1,5 +1,17 @@ -Palindrome都是要两边回溯相等。 -linkedlist不能reverse iterating, 那么就reverse the list, 从中间开花作比较。 +E +1525645137 +tags: Linked List, Two Pointers + +#### Reverse Linked List +- Palindrome概念很简单, 但是要在Linkde List random access坐标, 是很难得: 所以需要把一半 ListNode 翻转 +- reverse linked list: 遍历接开头 +- 用快慢指正找到mid point +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + +#### Previous Note +- Palindrome都是要两边回溯相等 +- linkedlist不能reverse iterating, 那么就reverse the list, 从中间开花作比较。 + ``` /* Implement a function to check if a linked list is a palindrome. @@ -48,7 +60,6 @@ Could you do it in O(n) time and O(1) space? * } */ public class Solution { - public boolean isPalindrome(ListNode head) { if (head == null || head.next == null) { return true; @@ -56,21 +67,21 @@ public boolean isPalindrome(ListNode head) { //Find middle ListNode mid = findMiddle(head); //Reverse and return right side - ListNode right = reverse(mid.next); + ListNode rightNode = reverse(mid.next); mid.next = null; - ListNode left = head; - while (left != null && right != null) { - if (left.val != right.val) { + ListNode leftNode = head; + while (leftNode != null && rightNode != null) { + if (leftNode.val != rightNode.val) { return false; } - left = left.next; - right = right.next; + leftNode = leftNode.next; + rightNode = rightNode.next; } - //It's possible that left&&right both finishes; or just right finishes. Both cases are returnning true. - return right == null; + + return true; } - public ListNode findMiddle(ListNode head) { + private ListNode findMiddle(ListNode head) { ListNode slow = head; ListNode fast = head.next; while (fast != null && fast.next != null) { @@ -80,7 +91,7 @@ public ListNode findMiddle(ListNode head) { return slow; } - public ListNode reverse(ListNode head) { + private ListNode reverse(ListNode head) { ListNode dummy = new ListNode(0); ListNode reversedList = dummy; while (head != null) { @@ -99,17 +110,4 @@ public ListNode reverse(ListNode head) { - - - - - - - - - - - - - ``` \ No newline at end of file diff --git a/Java/Palindrome Pairs.java b/Java/Palindrome Pairs.java new file mode 100644 index 0000000..097caff --- /dev/null +++ b/Java/Palindrome Pairs.java @@ -0,0 +1,227 @@ +H +1521010215 +tags: Hash Table, String, Trie + +Obvious的做法是全部试一遍, 判断, 变成 O(n^2) * O(m) = O(mn^2). O(m): isPalindrome() time. + +当然不行了, 那就看是O(nlogN), 还是O(n)? + +#### 方法1: Hash Table + Palindrome的性质. 复合型. +O(mn) + +##### 思路 +- 每一个word, 都可以拆分成 front + mid + end. 如果这个word + 其他word可以组成palindrome,那就是说 +- 砍掉 (mid+end), front.reverse() 应该存在 words[] 里面. +- 砍掉 (front+mid), end.reverse() 应该存在 words[] 里面. +- 我们用HashMap存所有的, 然后reverse, 找配对就好. + +##### Corner case +- 如果有 empty string "", 那么它跟任何一个palindrome word, 都可以配对, 并且根据位置前后变换, 凑成2份 distinct indexes. +- 这样就有了那个 `if (reverseEnd.equals("")) {...}` 的logic. +- 注意: 虽然在处理砍头/砍尾的两个 for loop里面都在根据 empty string 重复记录, + 但因为 "" 自己本身不能作为起点, 所以overall只会在被其他palindrome配对时记录一次. + + +#### 方法2: Trie +还要做一下那. + +``` +/* +Given a list of unique words, find all pairs of distinct indices (i, j) in the given list, +so that the concatenation of the two words, i.e. words[i] + words[j] is a palindrome. + +Example 1: +Given words = ["bat", "tab", "cat"] +Return [[0, 1], [1, 0]] +The palindromes are ["battab", "tabbat"] +Example 2: +Given words = ["abcd", "dcba", "lls", "s", "sssll"] +Return [[0, 1], [1, 0], [3, 2], [2, 4]] +The palindromes are ["dcbaabcd", "abcddcba", "slls", "llssssll"] +*/ + +/* +Thoughts: +If pairing all elements in words, it'll take O(n^2), which is not sufficient. +What about O(nLogN)? O(n)? +Given hints, we could leverage the characristic of palindrome to make it O(mn) + +Given the 'pair' requirement, one palindrome can be broken down to 2 parts: front, mid, end, where mid can be '' +Taking any word from words: 'bat': +if we take front = 'b', mid = 'at', we want to find end = 'b' from rest of words +if we take front = 'ba', mid = 't', we want to find end = 'ab' from rest of words +if we take front = 'bat', mid = '', we want to find end = 'tab' from rest of words: found it. + +corner case: +empty string? should be fine, matching with all palindrome words +what if ["aab", "baa", "aa", "bbaa"] creates same palindrome? They count as distinct pairs + +- set of words +- cut string, look up, set pair +*/ +/* +Thoughts: +If pairing all elements in words, it'll take O(n^2), which is not sufficient. +What about O(nLogN)? O(n)? +Given hints, we could leverage the characristic of palindrome to make it O(mn) + +Given the 'pair' requirement, one palindrome can be broken down to 2 parts: front, mid, end, where mid can be '' +Taking any word from words: 'bat': +if we take front = 'b', mid = 'at', we want to find end = 'b' from rest of words +if we take front = 'ba', mid = 't', we want to find end = 'ab' from rest of words +if we take front = 'bat', mid = '', we want to find end = 'tab' from rest of words: found it. + +corner case: +empty string? should be fine, matching with all palindrome words +what if ["aab", "baa", "aa", "bbaa"] creates same palindrome? They count as distinct pairs + +- set of words +- cut string, look up, set pair +*/ +class Solution { + public List> palindromePairs(String[] words) { + List> rst = new ArrayList<>(); + if (words == null || words.length == 0) { + return rst; + } + + int n = words.length; + HashMap map = new HashMap<>(); + for (int i = 0; i < n; i++) { + map.put(words[i], i); + } + + + for (String word : words) { // O(n) + // Cut middle and append the reverse to end + for (int i = 0; i < word.length(); i++) { // O(m) + String middle = word.substring(i + 1); + if (!isPalindrome(middle)) { + continue; + } + String reverseEnd = new StringBuffer(word.substring(0, i + 1)).reverse().toString(); + if (map.containsKey(reverseEnd) && map.get(reverseEnd) != map.get(word)) { + int index1 = map.get(word); + int index2 = map.get(reverseEnd); + List list = Arrays.asList(index1, index2); + rst.add(list); + if (reverseEnd.equals("")) { // empty create 2nd pair + list = Arrays.asList(index2, index1); + rst.add(list); + } + } + } + + // Cut front and append the reverse to front + for (int i = 0; i < word.length(); i++) { // O(m) + String middle = word.substring(0, i + 1); + if (!isPalindrome(middle)) { + continue; + } + String reverseEnd = new StringBuffer(word.substring(i + 1)).reverse().toString(); + if (map.containsKey(reverseEnd) && map.get(reverseEnd) != map.get(word)) { + int index1 = map.get(word); + int index2 = map.get(reverseEnd); + List list = Arrays.asList(index2, index1); + rst.add(list); + if (reverseEnd.equals("")) { // empty create 2nd pair + list = Arrays.asList(index1, index2); + rst.add(list); + } + } + } + } + + return rst; + } + + private boolean isPalindrome(String word) { + if (word.length() == 1) { + return true; + } + int start = 0; + int end = word.length() - 1; + while (start < end) { + if (word.charAt(start) != word.charAt(end)) { + return false; + } + start++; + end--; + } + + return true; + } +} + +// Still solution1, with a bit simplified code. +class Solution { + HashMap map = new HashMap<>(); + public List> palindromePairs(String[] words) { + List> rst = new ArrayList<>(); + if (words == null || words.length == 0) { + return rst; + } + + int n = words.length; + for (int i = 0; i < n; i++) { + map.put(words[i], i); + } + + + for (String word : words) { // O(n) + for (int i = 0; i < word.length(); i++) { // O(m) + String middle = word.substring(i + 1); + String reverseEnd = new StringBuffer(word.substring(0, i + 1)).reverse().toString(); + // Cut middle and append the reverse to end + findPair(rst, word, middle, reverseEnd, true); + + // Cut front and append the reverse to front + middle = word.substring(0, i + 1); + reverseEnd = new StringBuffer(word.substring(i + 1)).reverse().toString(); + findPair(rst, word, middle, reverseEnd, false); + } + } + + return rst; + } + + private void findPair(List> rst, String word, String middle, String reverseEnd, boolean front) { + if (!isPalindrome(middle)) { + return; + } + if (map.containsKey(reverseEnd) && map.get(reverseEnd) != map.get(word)) { + int index1 = map.get(word); + int index2 = map.get(reverseEnd); + List list = Arrays.asList(index1, index2); + if (!front) { + Collections.reverse(list); + } + rst.add(list); + if (reverseEnd.equals("")) { // empty create 2nd pair + list = Arrays.asList(index2, index1); + if (!front) { + Collections.reverse(list); + } + rst.add(list); + } + } + } + + private boolean isPalindrome(String word) { + if (word.length() == 1) { + return true; + } + int start = 0; + int end = word.length() - 1; + while (start < end) { + if (word.charAt(start) != word.charAt(end)) { + return false; + } + start++; + end--; + } + + return true; + } +} +``` \ No newline at end of file diff --git a/Java/Palindrome Partitioning II.java b/Java/Palindrome Partitioning II.java old mode 100644 new mode 100755 index 447313e..245eabd --- a/Java/Palindrome Partitioning II.java +++ b/Java/Palindrome Partitioning II.java @@ -1,12 +1,36 @@ -Double for loop 检查每种substring string (i~j). 若i,j相邻或者同点,那么肯定isPal;否则,i,j之间的(i+1, j-1)一定得isPal。 -看上去,在检查i,j的时候,中间按的(i+1, j-1)怎么可能先知道? 其实不然..在j慢慢长大的时候,所有的0~j的substring都检查过。所以isPal[i+1][j-1]一定是已经知道结果的。 +H +1524717518 +tags: DP, Partition DP + +给一个String s, 找出最少用多少cut, 使致 切割出的每一个substring, 都是palindrome + +#### Partition DP +- Find minimum cut: 分割型DP +- dp[i]: 最少cut多少刀, 使得前 i 长度的string, 割出来都是palindrome +- 最终要得到 dp[n], 所以 int[n + 1] +- 移动切刀, 看在哪里切, index j in [0 ~ i] +- 考虑[j, i - 1] 是否是回文串, 如果是, 那么: dp[i]= min(dp[i], d[j] + 1). +- note: 估计遍历 j 的时候, 反过来遍历也可以. + +#### 计算Palindrome的优化 +- 利用palindrome的性质, 可以算出 boolean palindrome[i, j]的情况. +- 找一个任意mid point: +- 1. 假设palindrome是奇数长度, 那么 mid 是单独的字符, 而两边的字符 [mid-1], [mid+1] 应该完全相等. +- 2. 假设palindrome是偶数长度, 那么 [mid] 和 [mid + 1] 这样位置的字符应该相等. +- 这样做出来 palindrome[i, j]: 从字符 i 到 字符 j 的 substring 是否是 palindrome +- 这样就给我们的问题合理降维, 目前是time: O(n^2). +- 不然求一次palindrome, 就是n, 会变成O(n^3) + +#### Previous Notes +- Double for loop 检查每种substring string (i~j). 若i,j相邻或者同点,那么肯定isPal;否则,i,j之间的(i+1, j-1)一定得isPal。 +- 看上去,在检查i,j的时候,中间按的(i+1, j-1)怎么可能先知道? 其实不然..在j慢慢长大的时候,所有的0~j的substring都检查过。所以isPal[i+1][j-1]一定是已经知道结果的。 +- okay.那么假如以上任意一种情况成立,也就是说isPal[i][j] == true。那就要判断,切到第一层循环参数j的末尾点时,有多少种切法? +- 想法很顺:我们naturally会想到,把i之前的cut加上i~j之间发生的不就好了。 +- 反正现在j不变,现在就看吧i定在哪里,cut[i - 1]是否更小/最小; 再在cut[i-1]基础上+1就完了。 +- 当然,如果i==0, 而 i~j又是isPal,那没啥好谈的,不必切,0刀。 +- 最终,刷到cut[s.length() - 1] 也就是最后一点。 return的理所应当。 -okay.那么假如以上任意一种情况成立,也就是说isPal[i][j] == true。那就要判断,切到第一层循环参数j的末尾点时,有多少种切法? -想法很顺:我们naturally会想到,把i之前的cut加上i~j之间发生的不就好了。 -反正现在j不变,现在就看吧i定在哪里,cut[i - 1]是否更小/最小; 再在cut[i-1]基础上+1就完了。 - 当然,如果i==0, 而 i~j又是isPal,那没啥好谈的,不必切,0刀。 -最终,刷到cut[s.length() - 1] 也就是最后一点。 return的理所应当。 ``` /* Given a string s, cut s into some substrings such that every substring is a palindrome. @@ -16,6 +40,143 @@ Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut. Tags Expand Dynamic Programming +*/ + +/* +Thoughts: +Find minimum? +Say dp[i] represents min # of cuts for string with length i. +Think about last index i: dp[i]'s min depends on if previous 1 or more indexes forms a palindrome: +dp[i] = Min(dp[i - 1], dp[i - 2], dp[i - 3], .... dp[i - x]) + 1, assuming [i-1, i) is palindrome. +Set up a j and let j = i - 1, j-- to try all options see if any [j, i-1] is palindrome. + +Note: +dp[i] gives minimum # of palindromes. +We need cuts, so dp[i] - 1 + +O(n^2) +We optimized using a palindrome[][] to save all possible palindromes across [i,j] +*/ +class Solution { + public int minCut(String s) { + if (s == null || s.length() <= 1) { + return 0; + } + int len = s.length(); + int[] dp = new int[len + 1]; + boolean[][] palindrome = calcPalindrome(s.toCharArray()); + dp[0] = 0; + + for (int i = 1; i <= len; i++) { + dp[i] = Integer.MAX_VALUE; + for (int j = 0; j < i; j++) { // this works too: (int j = i - 1; j >= 0; j--) + if (palindrome[j][i - 1]) { // check if [j , i - 1] + dp[i] = Math.min(dp[i], dp[j] + 1); // move cursor to j + } + } + } + return dp[len] - 1; // need cuts + } + + /* + Find if any [i,j] is palindrome. + Start attempting from mid point, and expand to both side. Set true of is palindrome. + For odd length: there will be a mid point, and expansion goes to both sides naturally. + For even length: mid and mid+1 will be middle 2 indexes. + */ + private boolean[][] calcPalindrome(char[] s) { + int n = s.length; + boolean[][] palindrome = new boolean[n][n]; + + for (int mid = 0; mid < n; mid++) { + // odd + int i = mid, j = mid; + while (i >= 0 && j < n && s[i] == s[j]) { + palindrome[i][j] = true; + i--; + j++; + } + + i = mid; + j = mid + 1; + while (i >= 0 && j < n && s[i] == s[j]) { + palindrome[i][j] = true; + i--; + j++; + } + } + return palindrome; + } +} + +/** +Print the min-cut solution +1. Record the index where the min-cut was taken. +2. Once the cuts: pi[i] is recorded, start from i = n to print the path +**/ +class Solution { + public int minCut(String s) { + if (s == null || s.length() <= 1) { + return 0; + } + int len = s.length(); + int[] dp = new int[len + 1]; + boolean[][] palindrome = calcPalindrome(s.toCharArray()); + dp[0] = 0; + + int[] pi = new int[len + 1]; + for (int i = 1; i <= len; i++) { + dp[i] = Integer.MAX_VALUE; + for (int j = 0; j < i; j++) { + if (palindrome[j][i - 1]) { // check if [j , i - 1] + dp[i] = Math.min(dp[i], dp[j] + 1); // move cursor to j + if (dp[i] == dp[j] + 1) { + pi[i] = j; + } + } + } + } + // Print result + int i = len; + char[] arr = s.toCharArray(); + while (i != 0) { + for (int j = pi[i]; j < i; j++) { + System.out.print(arr[j]); + } + + System.out.println(); + i = pi[i]; // move i to the cut position, backtrack + } + return dp[len] - 1; // need cuts + } + + private boolean[][] calcPalindrome(char[] s) { + int n = s.length; + boolean[][] palindrome = new boolean[n][n]; + + for (int mid = 0; mid < n; mid++) { + // odd + int i = mid, j = mid; + while (i >= 0 && j < n && s[i] == s[j]) { + palindrome[i][j] = true; + i--; + j++; + } + + i = mid; + j = mid + 1; + while (i >= 0 && j < n && s[i] == s[j]) { + palindrome[i][j] = true; + i--; + j++; + } + } + return palindrome; + } +} + +/* +Previous notes Thinking process: DP problem. Use a isPal to record if any [i ~ j] is Palindrome, true/false @@ -24,8 +185,7 @@ by default, cut[j] = j because the worst condition is cut j times at each charactor: none 2+ character palindrome, and split into individual chars. update cut[j] by comparing existing cut[j] and (cut[i - 1] + 1). At the end, return cut[s.length() - 1]. -*/ - + */ public class Solution { /** * @param s a string diff --git a/Java/Palindrome Partitioning.java b/Java/Palindrome Partitioning.java old mode 100644 new mode 100755 index 95dff80..9b555bb --- a/Java/Palindrome Partitioning.java +++ b/Java/Palindrome Partitioning.java @@ -1,30 +1,112 @@ -很清楚的DFS Backtracking. -在遍历str的时候,考虑从每个curr spot 到 str 结尾,是能有多少种palindorme? 那就从curr spot当个字符开始算,开始back tracing. -如果所选不是palindrome, 那move on. -若所选的确是palindrome, 加到path里面,DFS去下个level,等遍历到了结尾,这就产生了一种分割成palindrome的串。 -每次DFS结尾,要把这一层加的所选palindrome删掉,backtracking嘛。 +M +1529478652 +tags: DFS, Backtracking + +给个string s, partition(分段)后, 要确保每个partition都是palindrome. + +求所有partition palindrome组合. `list>` + +#### DFS +- 可以top->bottom: 遍历str, validate substring(start, i); if valid, add as candidate, and dfs; backtrack by remove candidate. +- 也可以bottom->up: 遍历str, validate substring(start, i); if valid, dfs(remaining str), return list of suffix; cross match with curr candidate. + +#### DFS Top->Bottom +- 在遍历str的时候,考虑从每个curr spot 到 str 结尾,是能有多少种palindorme? +- 那就从curr spot当个字符开始算,开始backtracking. +- 如果所选不是palindrome, 那move on. +- 若所选的确是palindrome, 加到path里面,DFS去下个level,等遍历到了结尾,这就产生了一种分割成palindrome的串。 +- 每次DFS结尾,要把这一层加的所选palindrome删掉,backtracking嘛 + +#### Optimization +- 可以再每一个dfs level 算 isPalindrome(S), 但是可以先把 boolean[][] isPalin 算出来, 每次O(1) 来用 +- 注意: isPalin[i][j] 是 inclusive的, 所以用的时候要认准坐标 +- Calculate isPalin[i][j]: pick mid point [0 ~ n] +- expand and validate palindrome at these indexes: `[mid, mid+1]` or `[mid-1][mid+1]` + +#### Complexity +- Overall Space O(n^2): 存 isPlain[][] +- Time O(2^n), 每一层都在做 pick/not pick index i 的选择, 所以worst case 2^n. +- 因为我们的isPalin[][]优化了palindrome的判断O(1), 所以overall Time: O(2^n) + ``` /* Given a string s, partition s such that every substring of the partition is a palindrome. + Return all possible palindrome partitioning of s. -Example -given s = "aab", + +For example, given s = "aab", Return - [ - ["aa","b"], - ["a","a","b"] - ] -Tags Expand -Backtracking -Thinking process: -1. Know how to write isPalindrome -2. Back tracking: - check partial string - add it - recursive call - remove it. + +[ + ["aa","b"], + ["a","a","b"] +] */ +/* +Thoughts: +DFS to return all possible palindromes: +partition at index x in [0, s.length() ) +Optimize by calculating boolean[][] isPalin ahead of time + +// 99.82% +*/ +class Solution { + boolean[][] isPalin; + String str; + public List> partition(String s) { + List> rst = new ArrayList<>(); + if (s == null || s.length() == 0) { + return rst; + } + isPalin = calcPalin(s); + str = s; + dfs(rst, new ArrayList<>(), 0); + return rst; + } + + private void dfs(List> rst, List list, int index) { + if (x == str.length()) { + rst.add(new ArrayList<>(list)); + return; + } + for (int i = index + 1; i <= str.length(); i++) { + if (isPalin[index][i - 1]) { // 也需要查看自身是不是 palindrome: s.charAt(x). isPalin[i][j] 是 inclusive的 + list.add(str.substring(index, i)); + dfs(rst, list, i); + list.remove(list.size() - 1); + } + } + } + // Kinda DP, isPalin[i][j] shows palindrome status for s[i,j] inclusivly + private boolean[][] calcPalin(String s) { + int n = s.length(); + char[] arr = s.toCharArray(); + boolean[][] isPalin = new boolean[n][n]; + int i, j; + + for (int mid = 0; mid < n; mid++) { + // odd: single char in center + i = j = mid; + while (i >= 0 && j < n && arr[i] == arr[j]) { + isPalin[i][j] = true; + i--; + j++; + } + + // even: always even number of palindrome characters + i = mid; + j = mid + 1; + while (i >= 0 && j < n && arr[i] == arr[j]) { + isPalin[i][j] = true; + i--; + j++; + } + } + return isPalin; + } +} + public class Solution { /** * @param s: A string diff --git a/Java/Palindrome Permutation II.java b/Java/Palindrome Permutation II.java new file mode 100755 index 0000000..bb2e351 --- /dev/null +++ b/Java/Palindrome Permutation II.java @@ -0,0 +1,108 @@ +M +tags: Backtracking, Permutation + +TODO: need to review permutation + +permutation的综合题: +1. validate Input 是不是可以做palindromic permutation. 这个就是(Palindrome Permutation I) +2. 顺便存一下permutation string的前半部分和中间的single character(if any) +3. DFS 做unique permutation: given input有duplicate characters。 + +``` +/* +Given a string s, return all the palindromic permutations (without duplicates) of it. +Return an empty list if no palindromic permutation could be form. + +For example: + +Given s = "aabb", return ["abba", "baab"]. + +Given s = "abc", return []. + +Hint: + +If a palindromic permutation exists, we just need to generate the first half of the string. +To generate all distinct permutations of a (half of) string, use a similar approach from: +Permutations II or Next Permutation. +Hide Tags Backtracking +Hide Similar Problems (M) Next Permutation (M) Permutations II (E) Palindrome Permutation + +*/ + + +//Validate if can build palindromic, add half of the char, and record the odd char. +//Do permutation on first half +public class Solution { + String halfStr = ""; + String oddStr = ""; + public List generatePalindromes(String s) { + List rst = new ArrayList(); + if (s == null || s.length() == 0) { + return rst; + } + if (!validate(s)) { + return rst; + } + boolean[] visited = new boolean[halfStr.length()]; + permutateUnique(rst, "", visited, halfStr); + + for (int i = 0; i < rst.size(); i++) { + String str = rst.get(i); + StringBuffer sb = new StringBuffer(str); + String reverse = sb.reverse().toString(); + rst.set(i, str + oddStr + reverse); + } + + return rst; + } + + //Validate and return palidrome candidate + public boolean validate(String s) { + int[] map = new int[256]; + for (int i = 0; i < s.length(); i++) { + map[s.charAt(i)]++; + } + char[] arr = new char[s.length() / 2]; + int countOdd = 0; + int ind = 0; + for (int i = 0; i < map.length; i++) { + if (map[i] % 2 != 0) { + countOdd++; + oddStr += (char)i; + } + + map[i] = map[i] / 2; + while (map[i] > 0) { + arr[ind] = (char)i; + map[i]--; + ind++; + } + + if (countOdd > 1) { + return false; + } + } + + Arrays.sort(arr); + halfStr = new String(arr); + return true; + } + + //Permutation with duplicates control: + public void permutateUnique(List rst, String str, boolean[] visited, String s) { + if (str.length() == s.length()) { + rst.add(str); + return; + } + for (int i = 0; i < s.length(); i++) { + if (visited[i] || (i > 0 && !visited[i - 1] && s.charAt(i - 1) == s.charAt(i))) { + continue; + } + visited[i] = true; + permutateUnique(rst, str + s.charAt(i), visited, s); + visited[i] = false; + } + } + +} +``` \ No newline at end of file diff --git a/Java/Palindrome Permutation.java b/Java/Palindrome Permutation.java new file mode 100755 index 0000000..aa4caa5 --- /dev/null +++ b/Java/Palindrome Permutation.java @@ -0,0 +1,126 @@ +E +1525666192 +tags: Hash Table + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count occurrance +- 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assum lower case letter. 应该至少是所有ASCII code + +``` +/* +Given a string, determine if a permutation of the string could form a palindrome. + +For example, +"code" -> False, "aab" -> True, "carerac" -> True. + +Hint: + +Consider the palindromes of odd vs even length. What difference do you notice? +Count the frequency of each character. +If each character occurs even number of times, then it must be a palindrome. How about character which occurs odd number of times? + +Tags: Hash Table +Similar Problems: (M) Longest Palindromic Substring, (E) Valid Anagram, (M) Palindrome Permutation II + +*/ + + +//LeetCode. Made assumption on ASCII code, so use int[256] +/* +Toughts: +count characters. Can have up to 1 odd number +*/ +class Solution { + public boolean canPermutePalindrome(String s) { + if (s == null || s.length() == 0) { + return false; + } + int[] chars = new int[256]; + for (char c : s.toCharArray()) { + chars[c]++; + } + int countOdd = 0; + for (int i = 0; i < chars.length; i++) { + countOdd += chars[i] % 2; + if (countOdd > 1) { + return false; + } + } + return true; + } +} + +/* +Add each char into map. +Count if odd > 1, false + +Note: Iterate HashMap +HashMap.Entry entry : map.entrySet() +*/ + +public class Solution { + public boolean canPermutePalindrome(String s) { + if (s == null || s.length() == 0) { + return true; + } + HashMap map = new HashMap(); + for (int i = 0; i < s.length(); i++) { + String str = s.charAt(i) + ""; + if (!map.containsKey(str)) { + map.put(str, 1); + } else { + map.put(str, map.get(str) + 1); + } + }//ENd for + int countOdd = 0; + for (HashMap.Entry entry : map.entrySet()) { + if (entry.getValue() % 2 == 1) { + countOdd++; + } + if (countOdd > 1) { + return false; + } + }//END for + return true; + } +} + + +/* +Invalid solution. Assumption made on lowercase letter. +12.12.2015 recap: +use a array of length == 26 to track it? No, because ther ecould be captalized letters, other ASCII code +If with assmption of 26 chars + +AND NO, cannot make that assuption. +*/ +public class Solution { + public boolean canPermutePalindrome(String s) { + if (s == null || s.length() <= 1) { + return true; + } + int[] counts = new int[26]; + for (char c : s.toCharArray()) { + counts[c - 'a'] += 1; + } + int countOne = 0; + for (int count : counts) { + if (count == 1 && countOne >= 1) { + return false; + } else if (count == 1) { + countOne++; + } else if (count % 2 != 0) { + return false; + } + } + + return true; + } +} + + +``` \ No newline at end of file diff --git a/Java/Palindromic Substrings.java b/Java/Palindromic Substrings.java new file mode 100644 index 0000000..8888a74 --- /dev/null +++ b/Java/Palindromic Substrings.java @@ -0,0 +1,60 @@ +M +1531469053 +tags: String, DP + +根据题意, count # of palindromic substring. (不同index截取出来的substring算不同的情况) + +#### isPalin[][] +- build boolean[][] to check isPalin[i][j] with DP concept +- check all candidates isPalin[][] +- O(n^2) + +#### odd/even split check +https://leetcode.com/problems/palindromic-substrings/discuss/105689/Java-solution-8-lines-extendPalindrome + +``` +/* +Given a string, your task is to count how many palindromic substrings in this string. + +The substrings with different start indexes or end indexes are counted +as different substrings even they consist of same characters. + +Example 1: +Input: "abc" +Output: 3 +Explanation: Three palindromic strings: "a", "b", "c". +Example 2: +Input: "aaa" +Output: 6 +Explanation: Six palindromic strings: "a", "a", "a", "aa", "aa", "aaa". +Note: +The input string length won't exceed 1000. +*/ + +class Solution { + public int countSubstrings(String s) { + if (s == null || s.length() == 0) return 0; + int n = s.length(), count = 0; + boolean[][] isPalin = buildPalin(s); + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) count += isPalin[i][j] ? 1 : 0; + } + + return count; + } + + private boolean[][] buildPalin(String s) { + int n = s.length(); + boolean[][] isPalin = new boolean[n][n]; + // init: + for (int i = 0; i < n; i++) isPalin[i][i] = true; + // Calc: + for (int j = 0; j < n; j++) { + for (int i = 0; i <= j; i++) { // index [i, j] + isPalin[i][j] = s.charAt(i) == s.charAt(j) && (j - i <= 1 || isPalin[i + 1][j - 1]); + } + } + return isPalin; + } +} +``` \ No newline at end of file diff --git a/Java/Partition Array by Odd and Even.java b/Java/Partition Array by Odd and Even.java old mode 100644 new mode 100755 index 27c784f..62f53a1 --- a/Java/Partition Array by Odd and Even.java +++ b/Java/Partition Array by Odd and Even.java @@ -1,3 +1,10 @@ +E +tags: Two Pointers, Array + +- 更正常的start/end partition pointer类似: when condition meet, swap +- Clean up TODO + +``` /* Partition an integers array into odd number first and even number second. @@ -49,3 +56,5 @@ public void partitionArray(int[] nums) { } } } + +``` \ No newline at end of file diff --git a/Java/Partition Array.java b/Java/Partition Array.java old mode 100644 new mode 100755 index 5ded8b4..904faf5 --- a/Java/Partition Array.java +++ b/Java/Partition Array.java @@ -1,12 +1,24 @@ -Partition Array根据pivot把array分成两半。 -从array两边开始缩进。while loop到遍历完。非常直白的implement。 -注意low/high,或者叫start/end不要越边界 -O(n) +M +1527814146 +tags: Array, Two Pointers, Quick Sort, Sort + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + -Quick sort的基础。 ``` /* -Given an array nums of integers and an int k, partition the array (i.e move the elements in "nums") such that: +Given an array nums of integers and an int k, +partition the array (i.e move the elements in "nums") such that: All elements < k are moved to the left All elements >= k are moved to the right @@ -16,7 +28,8 @@ Given an array nums of integers and an int k, partition the array (i.e move the If nums=[3,2,2,1] and k=2, a valid answer is 1. Note -You should do really partition in array nums instead of just counting the numbers of integers smaller than k. +You should do really partition in array nums +instead of just counting the numbers of integers smaller than k. If all elements in nums are smaller than k, then return nums.length diff --git a/Java/Partition List.java b/Java/Partition List.java old mode 100644 new mode 100755 index 5e4a1e1..d39aa21 --- a/Java/Partition List.java +++ b/Java/Partition List.java @@ -1,11 +1,15 @@ -不能像partitioin array一样从两边遍历。 +M +1528086917 +tags: Linked List, Two Pointers -那就最普通的,建造两个list +#### Linked List +- linked list 不能像partitioin array一样从两边遍历 +- 把小于value的加在前半段, 把 >= value的加在后半段 +- 做法很普通: 建造两个list, midTail pointer, post pointer +- 把满足条件(=x)的数字分别放到两个list里面 +- 记得用dummyNode track head. +- 最终midTail.next = post链接起来。 -把满足条件(=x)的数字分别放到两个list里面 - -记得用dummyNode track head. -最终pre.next = post链接起来。 ``` /* 33% Accepted @@ -31,51 +35,41 @@ 3. Link them togeter 4. return dummyPre.next */ - /** - * Definition for ListNode. + * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; - * ListNode(int val) { - * this.val = val; - * this.next = null; - * } + * ListNode(int x) { val = x; } * } - */ + */ public class Solution { - /** - * @param head: The first node of linked list. - * @param x: an integer - * @return: a ListNode - */ public ListNode partition(ListNode head, int x) { if (head == null) { return head; } - ListNode dummyPre = new ListNode(0); + ListNode dummy = new ListNode(0); ListNode dummyPost = new ListNode(0); - ListNode pre = dummyPre; + ListNode midTail = dummy; ListNode post = dummyPost; while (head != null) { if (head.val < x) { - pre.next = head; - pre = pre.next; + midTail.next = head; + midTail = midTail.next; } else { post.next = head; post = post.next; } - head = head.next; + head = head.next; } post.next = null; - pre.next = dummyPost.next; + midTail.next = dummyPost.next; - return dummyPre.next; + return dummy.next; } } - ``` \ No newline at end of file diff --git a/Java/Pascal's Triangle II.java b/Java/Pascal's Triangle II.java new file mode 100755 index 0000000..4ec942f --- /dev/null +++ b/Java/Pascal's Triangle II.java @@ -0,0 +1,59 @@ +E + +简单处理array list. + +``` + + +/* +Given an index k, return the kth row of the Pascal's triangle. + +For example, given k = 3, +Return [1,3,3,1]. + +Note: +Could you optimize your algorithm to use only O(k) extra space? + +Hide Tags Array +Hide Similar Problems (E) Pascal's Triangle + +*/ + + +/* + 1 + 1 1 +1 2 1 +1 3 3 1 +*/ + +//list store 0 1 0. Iteratve over k, each time createa a new list. +//Add 1 on two size before calculating each row +public class Solution { + public List getRow(int rowIndex) { + List rst = new ArrayList(); + if (rowIndex < 0) { + return rst; + } else if (rowIndex == 0) { + rst.add(1); + return rst; + } + + rst.add(1); + ArrayList list = new ArrayList(); + + while (rowIndex > 0) {//2, 1, 0x + list.add(1); + for (int i = 0; i < rst.size() - 1; i++) { + list.add(rst.get(i) + rst.get(i + 1)); + } + list.add(1); + rst = list;//[1,1], [1,2,1] + list = new ArrayList(); + rowIndex--; + } + + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/Path Sum II.java b/Java/Path Sum II.java new file mode 100755 index 0000000..ae90df2 --- /dev/null +++ b/Java/Path Sum II.java @@ -0,0 +1,261 @@ +E +1526525774 +tags: Tree, DFS, Backtracking + +给一个inputSum, 然后dfs, 找到所有path, 满足: path sum 跟 inputSum 一样. + +#### DFS, Backtracking +- 用remaining sum 来检测是否满足 input path sum 条件 +- 满足的时候add to result list +- 两种backtracking: +- 1. backtrack 当下node, 加进list, 然后dfs. dfs结束后删掉之前加进去的元素. 非常clean. +- 2. backtrack 下一个dfs level增加的value. dfs return 之后, 删掉list里面的末尾元素: 但是删掉的dfs余下的value. +- 第一种backtrack更加好掌握. + +#### Previous Notes: +- Binary Tree的一个基本题: 找到所有满足条件的path +- 遍历到底,比较sum vs. target +- 注意divide的情况。要把遍历的例子写写 + +``` +/* +LeetCode +Given a binary tree and a sum, find all root-to-leaf paths where each path's sum equals the given sum. + +For example: +Given the below binary tree and sum = 22, + 5 + / \ + 4 8 + / / \ + 11 13 4 + / \ / \ + 7 2 5 1 +return +[ + [5,4,11,2], + [5,8,4,5] +] +*/ + +// Back tracking current node whenever after use +class Solution { + public List> pathSum(TreeNode root, int sum) { + List> result = new ArrayList<>(); + if (root == null) { + return result; + } + + dfs(result, new ArrayList<>(), root, sum); + return result; + } + + private void dfs(List> result, List list, TreeNode node, int sum) { + if (node == null) return; + + list.add(node.val); + // check leaf + if (node.left == null && node.right == null && node.val == sum) { + result.add(new ArrayList<>(list)); + list.remove(list.size() - 1); + return; + } + dfs(result, list, node.left, sum - node.val); + dfs(result, list, node.right, sum - node.val); + // backtracking + list.remove(list.size() - 1); + } +} + + +// Backtracking the next level, whenever after dfs +class Solution { + public List> pathSum(TreeNode root, int sum) { + List> result = new ArrayList<>(); + if (root == null) { + return result; + } + + dfs(result, new ArrayList<>(), root, sum); + return result; + } + + private void dfs(List> result, List list, TreeNode node, int sum) { + if (node == null) { + return; + } + list.add(node.val); + + // leaf + if (node.left == null && node.right == null) { + if (node.val == sum) { + result.add(new ArrayList<>(list)); + } + return; + } + + if (node.left != null) { + dfs(result, list, node.left, sum - node.val); + list.remove(list.size() - 1); + } + if (node.right != null) { // 2 + dfs(result, list, node.right, sum - node.val); + list.remove(list.size() - 1); + } + } +} + + + + + + +/* +// LintCode: Binary Tree Path Sum +Given a binary tree, find all paths that sum of the nodes in the path equals to a given number target. + +A valid path is from root node to any of the leaf nodes. + +Example +Given a binary tree, and target = 5: + + 1 + / \ + 2 4 + / \ + 2 3 +return + +[ + [1, 2, 2], + [1, 4] +] +Tags Expand +Binary Tree Binary Tree Traversal +*/ + +/* +Recursively: list rst, path, currSum, sum +*/ +class Solution { + public List> pathSum(TreeNode root, int sum) { + List> rst = new ArrayList<>(); + if (root == null) { + return rst; + } + dfs(rst, new ArrayList<>(), root, 0, sum); + return rst; + } + + private void dfs (List> rst, List path, TreeNode root, int currSum, int sum) { + path.add(root.val); + if (root.left == null && root.right == null) { + if (currSum + root.val == sum) { + rst.add(new ArrayList<>(path)); + } + return; + } + if (root.left != null) { + dfs(rst, path, root.left, currSum + root.val, sum); + path.remove(path.size() - 1); + } + + + if (root.right != null) { + dfs(rst, path, root.right, currSum + root.val, sum); + path.remove(path.size() - 1); + } + } +} + +/* + Thoughts: + path: has to be from root to leaf. + binary tree: no order logic in the tree. + DPS on all nodes. If final sum == target, add list of nodes into rst +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + +/* +3.1.2016 Recap +Same approach +*/ +public class Solution { + public List> pathSum(TreeNode root, int sum) { + List> rst = new ArrayList>(); + if (root == null) { + return rst; + } + dfs(rst, new ArrayList(), root, 0, sum); + + return rst; + } + + public void dfs(List> rst, ArrayList list, TreeNode node, int add, int sum) { + list.add(node.val); + if (node.left == null && node.right == null) { + if (add + node.val == sum) { + rst.add(new ArrayList(list)); + } + return; + } + if (node.left != null) { + dfs(rst, list, node.left, add + node.val, sum); + list.remove(list.size() - 1); + } + if (node.right != null) { + dfs(rst, list, node.right, add + node.val, sum); + list.remove(list.size() - 1); + } + } +} + +public class Solution { + public List> binaryTreePathSum(TreeNode root, int target) { + List> rst = new ArrayList>(); + if (root == null) { + return rst; + } + ArrayList list = new ArrayList(); + list.add(root.val); + traversal(rst, list, root, root.val, target); + return rst; + } + + + public void traversal(List> rst, ArrayList list, TreeNode node, int sum, int target) { + if (node.left == null && node.right == null) { + if (sum == target) { + rst.add(new ArrayList(list)); + } + return; + } + if (node.left != null) { + list.add(node.left.val); + traversal(rst, list, node.left, sum + node.left.val, target); + list.remove(list.size() - 1); + } + if (node.right != null) { + list.add(node.right.val); + traversal(rst, list, node.right, sum + node.right.val, target); + list.remove(list.size() - 1); + } + } +} + + + + + +``` \ No newline at end of file diff --git a/Java/Path Sum III.java b/Java/Path Sum III.java new file mode 100644 index 0000000..5952bf5 --- /dev/null +++ b/Java/Path Sum III.java @@ -0,0 +1,79 @@ +E +1526529797 +tags: Tree, DFS, Double Recursive + +count所有存在的 path sum == target sum. 可以从任意点开始. 但是只能parent -> child . + +#### DFS +- 对所给的input sum 做减法, 知道 sum 达到一个目标值截止 +- 因为可以从任意点开始, 所以当sum达标时候, 需要继续recursive, 从而找到所有情况 (有正负数, sum可能继续增加/减少) +- 经典的 helper dfs recursive + self recursive +- 1. helper dfs recursive 处理包括root的情况 +- 2. self recursive 来引领 skip root的情况. + +#### 特点 +- 与 `Binary Tree Longest Consecutive Sequence II` 在recursive的做法上很相似: +- 利用dfs做包括root的recursive computation +- 利用这个function自己, 做`不包括root的recursive computation` + +``` +/* +You are given a binary tree in which each node contains an integer value. + +Find the number of paths that sum to a given value. + +The path does not need to start or end at the root or a leaf, +but it must go downwards (traveling only from parent nodes to child nodes). + +The tree has no more than 1,000 nodes and the values are in the range -1,000,000 to 1,000,000. + +Example: + +root = [10,5,-3,3,2,null,11,3,-2,null,1], sum = 8 + + 10 + / \ + 5 -3 + / \ \ + 3 2 11 + / \ \ +3 -2 1 + +Return 3. The paths that sum to 8 are: + +1. 5 -> 3 +2. 5 -> 2 -> 1 +3. -3 -> 11 +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public int pathSum(TreeNode root, int sum) { + if (root == null) { + return 0; + } + + return dfs(root, sum) + pathSum(root.left, sum) + pathSum(root.right, sum); + } + + private int dfs(TreeNode node, int sum) { + int count = 0; + if (node == null) return count; + + count += node.val == sum ? 1 : 0; + count += dfs(node.left, sum - node.val); + count += dfs(node.right, sum - node.val); + return count; + } +} + + +``` \ No newline at end of file diff --git a/Java/Path Sum IV.java b/Java/Path Sum IV.java new file mode 100644 index 0000000..9d45e0d --- /dev/null +++ b/Java/Path Sum IV.java @@ -0,0 +1,108 @@ +M +1531456768 +tags: Tree, Hash Table, DFS + +给一串3-digit 的数组. 每个数字的表达一个TreeNode, 3 digit分别代表: depth.position.value + +这串数字已经从小到大排列. 求: 所有可能的 root->leaf path 的所有可能的 path sum 总和. + +#### DFS, Hash Table +- 因为`前两个digit可以uniquely identify`一个node, 所以可以把前两个digit作为key, 定位node. +- 特点: 比如考虑root, 有 n 个leaf, 就会加 n 遍root, 因为有 n 个 unique path嘛. +- 实现: 每个node, 上来先把curr value加进sum; 只要有child, 到这个node位置的以上path sum 就要被重加一次. +- format: depth.position.value. (on same level, position may not be continuous) +- approach: map each number into: , and dfs. +- Start from dfs(map, rootKey, sum): +- 1. add node value to sum +- 2. compute potential child. +- 3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +- 4. also, if child exist, dfs into next level +- Space, time O(n) + +``` +/* +If the depth of a tree is smaller than 5, +then this tree can be represented by a list of three-digits integers. + +For each integer in this list: +The hundreds digit represents the depth D of this node, 1 <= D <= 4. +The tens digit represents the position P of this node in the level it belongs to, 1 <= P <= 8. +The position is the same as that in a full binary tree. + +The units digit represents the value V of this node, 0 <= V <= 9. +Given a list of ascending three-digits integers representing a binary with the depth smaller than 5. +You need to return the sum of all paths from the root towards the leaves. + +Example 1: +Input: [113, 215, 221] +Output: 12 +Explanation: +The tree that the list represents is: + 3 + / \ + 5 1 + +The path sum is (3 + 5) + (3 + 1) = 12. +Example 2: +Input: [113, 221] +Output: 4 +Explanation: +The tree that the list represents is: + 3 + \ + 1 + +The path sum is (3 + 1) = 4. + +*/ + +/* +Thoughts: +Goal: find all paths sum +format: depth.position.value. (on same level, position may not be continuous) +approach: +map each number into: +start from dfs(map, rootKey, sum): +1. add node value to sum +2. compute potential child. +3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +4. also, if child exist, dfs into next level + +int dfs(num, ) +*/ + + +class Solution { + public int pathSum(int[] nums) { + if (nums == null || nums.length == 0) return 0; + + // build map<[depth][position], value> + Map map = new HashMap<>(); + for (int num : nums) map.put(num / 10, num % 10);// key = [depth][position] + + return dfs(map, nums[0] / 10, 0); // key = nums[0] / 10 + } + + // key = [depth][position] + private int dfs(Map map, int key, int sum) { + sum += map.get(key); // add curr value to sum; will be used in children dfs. + + // compute left/right child + int level = key / 10; + int position = key % 10; + int leftChildKey = (level + 1) * 10 + position * 2 - 1; + int rightChildKey = leftChildKey + 1; + + // if no children, return leaf sum + if (!map.containsKey(leftChildKey) && !map.containsKey(rightChildKey)) return sum; + + // dfs on child, if applicable + int result = 0; + if (map.containsKey(leftChildKey)) result += dfs(map, leftChildKey, sum); + if (map.containsKey(rightChildKey)) result += dfs(map, rightChildKey, sum); + + return result; + } +} + +``` \ No newline at end of file diff --git a/Java/Path Sum.java b/Java/Path Sum.java new file mode 100644 index 0000000..019f5af --- /dev/null +++ b/Java/Path Sum.java @@ -0,0 +1,81 @@ +E +1526525767 +tags: Tree, DFS + +给一个inputSum, 然后dfs, 找到是否有一条path, 得出的path sum 跟 inputSum 一样. + +#### DFS +- 确定好结尾的条件: is leaf && val == sum +- 每一层减掉node.val, 然后dfs. +- 写一写: root == null => false 对parent nodes的影响. 这里发现没影响, 所以可以简化成用1个functionDFS. + + +``` +/* +Given a binary tree and a sum, determine if the tree has a root-to-leaf path +such that adding up all the values along the path equals the given sum. + +For example: +Given the below binary tree and sum = 22, + + 5 + / \ + 4 8 + / / \ + 11 13 4 + / \ \ + 7 2 1 +return true, as there exist a root-to-leaf path 5->4->11->2 which sum is 22. +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Thoughts: +DFS on the function itself, keep subtracting the root.val. +when root == null && sum == 0, return true; +*/ + +class Solution { + public boolean hasPathSum(TreeNode root, int sum) { + if (root == null) { + return false; + } + if (root.left == null && root.right == null && sum == root.val) { + return true; + } + return hasPathSum(root.left, sum - root.val) || hasPathSum(root.right, sum - root.val); + } +} + + +class Solution { + public boolean hasPathSum(TreeNode root, int sum) { + if (root == null) { + return false; + } + return dfs(root, sum); + } + + public boolean dfs(TreeNode root, int sum) { + if (root.left == null && root.right == null && sum == root.val) { + return true; + } + boolean rst = false; + if (root.left != null) { + rst |= dfs(root.left, sum - root.val); + } + if (root.right != null) { + rst |= dfs(root.right, sum - root.val); + } + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/Peeking Iterator.java b/Java/Peeking Iterator.java new file mode 100755 index 0000000..7b3bb90 --- /dev/null +++ b/Java/Peeking Iterator.java @@ -0,0 +1,188 @@ +M +1528087487 +tags: Design + +#### Use concept pre cache +- 找一个cache来存next()的值, 也就是: next value的值提前存在cache里面 +- 因此peek()的时候, 就可以直接return cache, 而不用做 itt.next() +- 然后每次真的next()的时候, 里取下一个itt.next()维护这个cache + +#### Previous notes +- 再一次理解错题意. peek() 就是头顶,但是不一定是最大值啊。 +- 总是把PEEK想成了最大值,然后用2 STACK做了最大值的cache,练的一手好双stack,可惜错了。 + + +``` +/* +Given an Iterator class interface with methods: next() and hasNext(), +design and implement a PeekingIterator that support the peek() operation -- +it essentially peek() at the element that will be returned by the next call to next(). + +Here is an example. Assume that the iterator is initialized to the beginning of the list: [1, 2, 3]. + +Call next() gets you 1, the first element in the list. + +Now you call peek() and it returns 2, the next element. Calling next() after that still return 2. + +You call next() the final time and it returns 3, the last element. Calling hasNext() after that should return false. + +Hint: + +Think of "looking ahead". You want to cache the next element. +Is one variable sufficient? Why or why not? +Test your design with call order of peek() before next() vs next() before peek(). +For a clean implementation, check out Google's guava library source code. (https://github.com/google/guava/blob/703ef758b8621cfbab16814f01ddcc5324bdea33/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterators.java#L1125) +Follow up: How would you extend your design to be generic and work with all types, not just integer? + +It looks like the guava library uses 'E' for generic element + +Tags: Design +Similar Problems: (M) Binary Search Tree Iterator, (M) Flatten 2D Vector, (M) Zigzag Iterator + +*/ + +/* +Second attempt. +Thoughts: Of coruse can't store in a queue, that will be brutle and meaning less. +Instead, use a iterator variable, cache, to hold next(). +When called next(), move forward; otherwise, return the cache. +Make sure also return the cached peek, and update cache with next() value. +*/ +// Java Iterator interface reference: +// https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html +class PeekingIterator implements Iterator { + private int cache; + private Iterator itt; + private boolean notEnd; + public PeekingIterator(Iterator iterator) { + // initialize any member here. + itt = iterator; + cache = itt.next(); + notEnd = iterator.hasNext(); + } + + // Returns the next element in the iteration without advancing the iterator. + public Integer peek() { + return cache; + } + + // hasNext() and next() should behave the same as in the Iterator interface. + // Override them if needed. + @Override + public Integer next() { + int curr = cache; + notEnd = itt.hasNext(); + if (itt.hasNext()) { + cache = itt.next(); + } + return curr; + } + + @Override + public boolean hasNext() { + return notEnd; + } +} + +// Generic and work with all types +// Java Iterator interface reference: +// https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html +class PeekingIterator implements Iterator { + private T cache; + private Iterator itt; + private boolean notEnd; + public PeekingIterator(Iterator iterator) { + // initialize any member here. + itt = iterator; + cache = itt.next(); + notEnd = iterator.hasNext(); + } + + // Returns the next element in the iteration without advancing the iterator. + public T peek() { + return cache; + } + + // hasNext() and next() should behave the same as in the Iterator interface. + // Override them if needed. + @Override + public T next() { + T curr = cache; + notEnd = itt.hasNext(); + if (itt.hasNext()) { + cache = itt.next(); + } + return curr; + } + + @Override + public boolean hasNext() { + return notEnd; + } +} + + +/* +Attempt1, failed. Reason: I thought we are looking for the real max-peek element! However, this problem only asks for peek() element, which is not necessarily the maximun element. This mistake is bloody. +Thoughts: +To find peek, have to run through the iterator at least once. O(n). +Store everything in 2 stacks: +We want to process the end of the iterator first, put everything into stack. +Therefore the top of the stack is the next() element of iterator. +Also, use second stack to hold max value for each element stage. + +Each stack1 element has a max coresponding element in stack2. For example, [5,9,1,3,6] +s1: 6,3,1,9,5 [5 gets out first] +s2: 6,6,6,9,9 [end 9 gets out first] +*/ + +// Java Iterator interface reference: +// https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html +class PeekingIterator implements Iterator { + private Stack s1; + private Stack s2; + private int size; + public PeekingIterator(Iterator iterator) { + // initialize any member here. + s1 = new Stack(); + s2 = new Stack(); + Stack temp = new Stack(); + size = 0; + int max = Integer.MIN_VALUE; + while(iterator.hasNext()) { + temp.push(iterator.next()); + size++; + } + while(!temp.empty()) { + s1.push(temp.peek()); + max = Math.max(max, temp.peek()); + s2.push(max); + temp.pop(); + } + } + + // Returns the next element in the iteration without advancing the iterator. + public Integer peek() { + if (s1.size() > size) { + s1.pop(); + return s2.pop(); + } else { + return s2.peek(); + } + } + + // hasNext() and next() should behave the same as in the Iterator interface. + // Override them if needed. + + @Override + public Integer next() { + size--; + return s1.pop(); + } + + @Override + public boolean hasNext() { + return !s1.empty(); + } +} +``` \ No newline at end of file diff --git a/Java/Perfect Rectangle.java b/Java/Perfect Rectangle.java new file mode 100644 index 0000000..4b2f165 --- /dev/null +++ b/Java/Perfect Rectangle.java @@ -0,0 +1,119 @@ +H +1528569462 +tags: Geometry, Hash Table, Design + +看的list of coordinates 是否能组成perfect rectangle, 并且不允许overlap area. + +#### 画图发现特点 +- 特点1: 所有给出的点(再找出没有specify的对角线点), 如果最后组成perfect rectangle, 都应该互相消除, 最后剩下4个corner +- 特点2: 找到所有点里面的min/max (x,y), 最后组成的 maxArea, 应该跟过程中accumulate的area相等 +- 特点1确保中间没有空心的部分, 保证所有的重合点都会互相消除, 最后剩下4个顶点 +- 特点2确保没有重合: 重合的area会最终超出maxArea + +``` +/* +Given N axis-aligned rectangles where N > 0, +determine if they all together form an exact cover of a rectangular region. + +Each rectangle is represented as a bottom-left point and a top-right point. +For example, a unit square is represented as [1,1,2,2]. +(coordinate of bottom-left point is (1, 1) and top-right point is (2, 2)). + + +Example 1: + +rectangles = [ + [1,1,3,3], + [3,1,4,2], + [3,2,4,4], + [1,3,2,4], + [2,3,3,4] +] + +Return true. All 5 rectangles together form an exact cover of a rectangular region. + +Example 2: + +rectangles = [ + [1,1,2,3], + [1,3,2,4], + [3,1,4,2], + [3,2,4,4] +] + +Return false. Because there is a gap between the two rectangular regions. + +Example 3: + +rectangles = [ + [1,1,3,3], + [3,1,4,2], + [1,3,2,4], + [3,2,4,4] +] + +Return false. Because there is a gap in the top center. + +Example 4: + +rectangles = [ + [1,1,3,3], + [3,1,4,2], + [1,3,2,4], + [2,2,4,4] +] + +Return false. Because two of the rectangles overlap with each other. +*/ + + +/* +- build coordinates, and diagonal points, cancel all points from hashmap. +- should remain 4 points, which are diagonal. otherwise, fail +*/ +class Solution { + public boolean isRectangleCover(int[][] rectangles) { + // check input + if (rectangles == null || rectangles.length == 0 || rectangles[0] == null || rectangles.length == 0) { + return false; + } + int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE, minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE, area = 0; + // build hashmap of all points, and diagonal points + Set set = new HashSet<>(); + + for (int[] row: rectangles) { + area += (row[2] - row[0]) * (row[3] - row[1]); + validate(set, row[0], row[1]); + validate(set, row[2], row[3]); + validate(set, row[0], row[3]); + validate(set, row[2], row[1]); + minX = Math.min(minX, row[0]); + minY = Math.min(minY, row[1]); + maxX = Math.max(maxX, row[2]); + maxY = Math.max(maxY, row[3]); + } + + if (set.size() != 4 || !set.contains(buildKey(minX, minY)) || !set.contains(buildKey(maxX, maxY)) || + !set.contains(buildKey(minX, maxY)) || !set.contains(buildKey(maxX, minY))) { + return false; + } + + return area == (maxX - minX) * (maxY - minY); + } + + private void validate(Set set, int x, int y) { + String key = buildKey(x, y); + if (set.contains(key)) { + set.remove(key); + } else { + set.add(key); + } + } + private String buildKey(int x, int y) { + return x + "@" + y; + } +} + + + +``` \ No newline at end of file diff --git a/Java/Perfect Squares.java b/Java/Perfect Squares.java new file mode 100755 index 0000000..58bdce2 --- /dev/null +++ b/Java/Perfect Squares.java @@ -0,0 +1,205 @@ +M +1518159874 +tags: Math, DP, BFS, Partition DP + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + +``` +/* +Given a positive integer n, find the least number of perfect square numbers +(for example, 1, 4, 9, 16, ...) which sum to n. + +For example, given n = 12, return 3 because 12 = 4 + 4 + 4; +given n = 13, return 2 because 13 = 4 + 9. +*/ + + +/* +Thoughts: DP +dp[i] : min # of perfect square numbers with integer i +*/ +class Solution { + public int numSquares(int n) { + if (n <= 1) { + return 1; + } + int[] dp = new int[n + 1]; // need to calculate dp[n] + dp[0] = 0; + for (int i = 1; i <= n; i++) { + dp[i] = Integer.MAX_VALUE; + for (int j = 1; j <= i; j++) { + if (i >= j * j) { + dp[i] = Math.min(dp[i], dp[i - j * j] + 1); + } else { + break; // if (i - j * j) < 0, there is no point to j++ + } + } + } + return dp[n]; + } +} +// Same +class Solution { + public int numSquares(int n) { + if (n <= 1) { + return 1; + } + int[] dp = new int[n + 1]; // need to calculate dp[n] + dp[0] = 0; + for (int i = 1; i <= n; i++) { + dp[i] = Integer.MAX_VALUE; + for (int j = 1; j <= i && j * j <= i; j++) { + dp[i] = Math.min(dp[i], dp[i - j * j] + 1); + } + } + return dp[n]; + } +} + +/* +BFS: +- count level, add input number n into queue +- consume num from queue, try out all integers [1, num) +- add remains (num - x^2) back to queue +- if any remain == 0, that's end of search, return level + 1 +*/ +class Solution { + public int numSquares(int n) { + // check input + if (n <= 0) { + return 0; + } + // build queue, add n + Queue queue = new LinkedList<>(); + Set set = new HashSet<>(); // save remain, skip the ones already added + queue.offer(n); + // consume queue, count level + int level = 1; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int x = 0; x < size; x++) { + int num = queue.poll(); + for (int i = 1; i <= num; i++) { + int remain = num - i * i; + if (remain == 0) { + return level; + } else if (remain > 0 && !set.contains(remain)) { + queue.offer(remain); + set.add(remain); + } else if (remain < 0) { + break; + } + } + } + level++; + } + return level; + } +} +/* +Previous notes +Thoughts: +Take the 12 example: to start with can be break down to (1, 11), (4, 8), (9, 3). +Then we need to find minNumSquares for 11, or for 8, or for 3, +where we can verify that 11 and 3 does not have a perfect square root, +so we need to go with (4, 8) -> (4, 4, 4) or (4, 1, 7) => (4, 4, 4) +let's say dp[i]: # of perfect square at i = Min{dp[i - j^2] + 1}, where 1<=j<=i +We are not sure what last number we could pick, so loop over 1<=j<=i to try all +O(n*sqrt(n)) +*/ + +/* +Thoughts: + Math: + num =13. sqrt(13) = 3.xxx. Floor() = 3. count++;//1 + num = 13 - 9 = 4. sqrt(4) = 2. No remaining. count++;//2 + DP: + state + dp[i]: min # of perfect square till i. + dp[0] = 0; + dp[1] = 1; + dp[2] = 1 + 1 = 2; + dp[3] = 1,1,1;//3 + dp[4] = 2^2;//1 + dp[5] = dp[5 - floor(sqrt(5))^2] + 1; + fn: //Pick the largest perfect square possible, then added on what's remaining's dp. Do a BFS on all possiblilities + maxFlorNum = Math.floor(Math.sqrt(i)) + 12 + -3^2 = 3 -2^2 = 8 -1^2 = 11 + 1 + dp[3] 1 + dp[8] 1 + dp[11] + for (j = 0 ~ i) + dp[i] = min(min, dp[i - j ^ 2] + 1) + init: + dp[0] = 0; + dp[1] = 1; + return dp[n]; +*/ + + + +public class Solution { + public int numSquares(int n) { + if (n <= 0) { + return 0; + } + int[] dp = new int[n + 1]; + dp[0] = 0; + + for (int i = 1; i <= n; i++) { + int maxSqrNum = (int)Math.floor(Math.sqrt(i)); + int min = Integer.MAX_VALUE; + for (int j = 1; j <= maxSqrNum; j++) { + min = Math.min(min, dp[i - j * j] + 1); + } + dp[i] = min; + } + return dp[n]; + } +} + + +/* +//Test Cases + dp[2] =2; + dp[4] = 1 + dp[5] = 2; + dp[6] = 2 + 1 =3; + dp[7] = 3 + 1 = 4; + dp[8] = dp[4] + 1 = 1 = 1 = 2; + dp[9] = 1 + dp[10] = 1 + 1 = 2; + dp[11] = 2 + 1 = 3 + dp[12] = dp[12 - 9] + 3 +*/ +``` \ No newline at end of file diff --git a/Java/Permutation Index.java b/Java/Permutation Index.java old mode 100644 new mode 100755 index 0b67a44..58d222c --- a/Java/Permutation Index.java +++ b/Java/Permutation Index.java @@ -1,39 +1,43 @@ -这题目标为简单。但是做了很久。 -最后分析出来: -每个数位的数字,都是基于这个数字之前越过的战壕...好吧,意思是,跳过了多少种可能。 -对,就用4,2,1举例。网上大家都用这个例子。不行,我要换一个。 +E -换【6,5,2】吧。我们找6,5,2是permudation里面的第几个。 -正常排序,也就是permutation的第一个,应该是【2,5,6】 -如果要从首位,2,变成6,要跨过多少条尸体呢? -很简单,高中就学过,重点来了:小于6的数字有多少个呢?(2,5).然后每个数字变成head,都有各自的变化可能,而每个数字打头,都有(n-1)!种可能。明显了吧,把每个(n-1)!加起来。 注意(n-1)意思就是出去开头的数字(2、5),后面有多少个,有序排列一下有多少情况,不就是(n-1)!嘛。 - 这一步,公式推出来就是很简单的 (有几个小于6的数字呀) ×(出去head剩下有多少个数字)! +和Permutation Sequence相反的题目。思想类似。 -以上 种种,都是为了把6推上皇位,而牺牲的条数。 +题目为Easy,琢磨很久,分析: +每个数位的数字,都是跳过了小于这数字开头的多种可能。 + +举例【6,5,2】吧。我们找6,5,2是permudation里面的第几个。 +正常排序,也就是permutation的第一个,应该是【2,5,6】 +如果要从首位,2,变成6,要跨过多少可能性呢? +很简单,就是问:小于6的数字有多少个呢?(2,5).每个数字变成head,都有各自的一套变化,都有(n-1)!种可能。 + +本题做法:每个(n-1)!加起来。 Note:(n-1) means, 开头的数字(2,5)各带出多少种排列,也就是不就是(n-1)!嘛。 + 这一步,计算数量很简单: (有几个小于6的数字) ×(除去head剩下有多少个数字)! + +以上 ,都是为了把6推上皇位,而牺牲的条数。 那么把6推上去以后,还有接下去的呢。 -接下去要看5,2. +接下去要看5,2. 6确定,后面permudation可变的情况有可能是【6,5,2】,那还可能是【6,2,5】呢。 -方法一样了。 -看given 数组的第二位5,算它接下去: -1. 有几个数字小于5呢? -2. 除去5,还有几个数字可以 factorial呢? -3. 一样的。第一步就结果乘以第二步。 +Same process, 看given 数组的第二位5,算它接下去: +1. 有几个数字小于5呢? +2. 除去5,还有几个数字可以 factorial呢? +3. 一样的。第一步就结果乘以第二步。 -接下去要看最后一个元素2了。 -一样的想法。 +最后接下去要看最后一个元素2了。 -6,5,2全看过了以后咋办。 -加起来呗。 -加起来,就是【6,5,2】上位,所踏过的所有小命啊! +6,5,2全看过了以后,加起来。 +就是【6,5,2】上位,所踏过的所有小命啊! 我这解释太生动了。因为耗费了好长时间思考... + ``` /* -Given a permutation which contains no repeated number, find its index in all the permutations of these numbers, which are ordered in lexicographical order. The index begins at 1. +Given a permutation which contains no repeated number, find its index in all the permutations of these numbers, +which are ordered in lexicographical order. The index begins at 1. + Example Given [1,2,4], return 1. diff --git a/Java/Permutation Sequence.java b/Java/Permutation Sequence.java new file mode 100755 index 0000000..32beb63 --- /dev/null +++ b/Java/Permutation Sequence.java @@ -0,0 +1,199 @@ +M +tags: Math, Backtracking + +TODO: what about regular DFS/backtracking to compute the kth? dfs(rst, list, candiate list, k) + +k是permutation的一个数位。而permutation是有规律的。 + +也就是说,可以根据k的大小来判断每一个数位的字符(从最大数位开始,因为默认factorio从最大数位开始变化)。 + +于是先求出n!, 然后 k/n!就可以推算出当下这一个数位的字符。然后分别把factorio 和 k减小。 + +另外, 用一个boolean[] visited来确保每个数字只出现一次。 + +这个方法比计算出每个permutation要efficient许多。 + + +``` +/* +The set [1,2,3,…,n] contains a total of n! unique permutations. + +By listing and labeling all of the permutations in order, +We get the following sequence (ie, for n = 3): + +"123" +"132" +"213" +"231" +"312" +"321" +Given n and k, return the kth permutation sequence. + +Note: Given n will be between 1 and 9 inclusive. + +Hide Tags Backtracking Math +Hide Similar Problems (M) Next Permutation (M) Permutations + +*/ +/* + Correct solution is to reduce k, and manipulate the sequence + http://www.jiuzhang.com/solutions/permutation-sequence/ + + Thoughts: + k is the sum of possibilities. + Based on attempt1, attempt2, we understand that each digit leads a differnet sets of possibilities. The total is n! + For example, + factorio = # of a paticular set of possibilities, and remain = (k / factorio) means how any sets are there. + If remain == 0, that means factorio has more possibiities than k (factorio > k) so there is nothing changed on 1st + char position. For example, if given [1,2,3], then the string will end up as '1xx'. + + With the above fact, we can find out each char by calculate k vs. factorio. + + Note, each round, the factorio itself need to reduced. +*/ + +public class Solution { + public String getPermutation(int n, int k) { + if (n <= 0 || k <= 0) { + return ""; + } + StringBuffer sb = new StringBuffer(); + boolean[] visited = new boolean[n]; + //Largest possible number of posibilities, n! + int factorio = 1; + for (int i = 1; i < n; i++) { + factorio *= i; + } + //Put k into 0-base + k = k - 1; + + for (int i = 0; i < n; i++) { + int index = k / factorio; + k = k % factorio; + + for (int j = 0; j < n; j++) { + if (!visited[j]) { + if (index == 0) { + visited[j] = true; + sb.append((char)('0' + j + 1)); + break; + } else { + index--; + } + } + } + + if (i < n - 1) { + factorio = factorio / (n - i - 1); + } + } + + return sb.toString(); + } +} + + +/* + Based on attempt1, we can actually find every index in front by calling getPermutation(n,k) recursivly + Still timeout +*/ +public class Solution { + public String getPermutation(int n, int k) { + if (n <= 0 || k <= 0) { + return ""; + } + ArrayList nums = new ArrayList(); + for (int i = 1; i <= n; i++) { + nums.add(i); + } + + int frontInd = 0; + for (; frontInd < nums.size(); frontInd++) { + //find max # of permutations that's less than k, and start permutation with that perticular n at index 0. + int factorio = 1; + for (int i = 1; i < n; i++) { + factorio = factorio * i; + } + int startInd = frontInd; + int numPermute = factorio; + while (numPermute < k) { + numPermute += factorio; + startInd++; + } + + if (startInd != frontInd) { + int temp = nums.get(startInd); + nums.remove(startInd); + nums.add(frontInd, temp); + k = k - numPermute; + n--; + } + } + StringBuffer sb = new StringBuffer(); + for (int num : nums) { + sb.append(num); + } + return sb.toString(); + } +} + +/* + Attempt1: timeout + based on k[1 ~ k], find the max x that has x! < k, where x = [1 ~ n] + then swap (x + 1) and 1, start permutation, and find the (k - x!)th +*/ +public class Solution { + public String rst = ""; + public int index = 0; + public String getPermutation(int n, int k) { + if (n <= 0 || k <= 0) { + return ""; + } + int[] nums = new int[n]; + for (int i = 0; i < nums.length; i++) { + nums[i] = i + 1; + } + + //find max # of permutations that's less than k, and start permutation with that perticular n at index 0. + int factorio = 1; + for (int i = 1; i < n; i++) { + factorio = factorio * i; + } + int startInd = 0; + int numPermute = factorio; + while (numPermute + factorio < k) { + numPermute += factorio; + startInd++; + } + + if (startInd != 0) { + int temp = nums[startInd];//findMax return value in [1 ~ n], and we need index + nums[startInd] = nums[0]; + nums[0] = temp; + + k = k - numPermute; + } + + permute("", k, nums);// startInd + 1 is in [1 ~ n] + return rst; + } + + public void permute(String s, int k, int[] nums) { + if (rst.length() != 0) { + return; + } + if (s.length() == nums.length) { + index++; + if (index == k) { + rst = s; + } + return; + } + for (int i = 0; i < nums.length; i++) { + if (!s.contains(nums[i] + "")) { + permute(s + nums[i], k, nums); + } + } + } +} +``` \ No newline at end of file diff --git a/Java/Permutation in String.java b/Java/Permutation in String.java new file mode 100644 index 0000000..cc24f97 --- /dev/null +++ b/Java/Permutation in String.java @@ -0,0 +1,66 @@ +M +1523946802 +tags: Two Pointers + +#### Two Pointer +- 如果做s1的permudation, 时间复杂度是O(n!) 肯定不可以 +- 这里用HashTable的做法 (因为26字母, 所以用int[26]简化) 来记录window内的 character count +- 如果window内的character count 相等, 那么就是permudation +- 更进一步优化: 找两个map相互对应, 不如用一个 int[26]: s1对遇到的character做加法, s2对遇到的character做减法 +- two pointer 运用在 n1, n2 的把控; 以及 s2.charAt(i - n1) 这一步 + +``` +/* +Given two strings s1 and s2, write a function to return true +if s2 contains the permutation of s1. In other words, +one of the first string's permutations is the substring of the second string. +Example 1: +Input:s1 = "ab" s2 = "eidbaooo" +Output:True +Explanation: s2 contains one permutation of s1 ("ba"). +Example 2: +Input:s1= "ab" s2 = "eidboaoo" +Output: False +Note: +The input strings only contain lower case letters. +The length of both given strings is in range [1, 10,000]. + +*/ + +class Solution { + public boolean checkInclusion(String s1, String s2) { + if (s1 == null || s1.length() == 0 || s2 == null || s2.length() == 0 || s1.length() > s2.length()) { + return false; + } + int n1 = s1.length(); + int n2 = s2.length(); + int[] charCount = new int[26]; + for (int i = 0; i < n1; i++) { + charCount[s1.charAt(i) - 'a']++; + charCount[s2.charAt(i) - 'a']--; + } + + if (zeroCount(charCount)) { + return true; + } + + for (int i = n1; i < n2; i++) { + charCount[s2.charAt(i) - 'a']--; + charCount[s2.charAt(i - n1) - 'a']++; + if (zeroCount(charCount)) { + return true; + } + } + return false; + } + + private boolean zeroCount(int[] count) { + for (int i = 0; i < 26; i++) { + if (count[i] != 0) { + return false; + } + } + return true; + } +} +``` \ No newline at end of file diff --git a/Java/Permutations II.java b/Java/Permutations II.java old mode 100644 new mode 100755 index 38a1438..3ac12cb --- a/Java/Permutations II.java +++ b/Java/Permutations II.java @@ -1,8 +1,44 @@ -要unique,就是要确定visit过的那个index不被重新visit. -一个办法就是给一个visited queue。 和queue在所有的地方一同populate. 然后visited里面存得时visited indexes +M +1524025921 +tags: Backtracking + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +#### Backtracking +- 排序, +- Mark visited. 通过permutation规律查看是否排出了重复结果 +- 并且要检查上一层recursive时有没有略过重复element +- time O(n!) + +##### 背景1 +- 在recursive call里面有for loop, 每次从i=0开始, 试着在当下list上加上nums里面的每一个。 +- 从i=0开始,所以会依次recursive每一个nums: +- 因此,例如i=2,肯定比i=3先被访问。也就是:取i=2的那个list permutation肯定先排出来。 + +##### 背景2 +- 重复的例子:给出Input[x, y1, y2], 假设y的值是一样的。那么,{x,y1,y2}和{x,y2,y1}是相同结果。 + +##### Note +- 综上,y1肯定比y2先被访问,{x,y1,y2}先出。 紧随其后,在另一个recursive循环里,{x,y2...}y2被先访问,跳过了y1。 +- 重点:规律在此,如果跳过y1,也就是visited[y1] == false, 而num[y2] == num[y1],那么这就是一个重复的结果,没必要做,越过。 +- 结果:那么,我们需要input像{x,y1,y2}这样数值放一起,那么必须排序。 + +#### Non-recursive, manuall swap +- Idea from: https://www.sigmainfy.com/blog/leetcode-permutations-i-and-ii.html +- 用到 sublist sort +- 用 swap function, 在原数组上调节出来新的permutation +- 注意: 每次拿到新的candidate, 都要把没有permutate的数位sort, 然后再开始swap. +- 这是为了确保, [j]和[j-1]在重复时候, 不用重新记录. + +#### Queue +- 给一个visited queue +- 和queue在所有的地方一同populate. +- 然后visited里面存得时visited indexes。 (Not efficient code. check again) + ``` /* -Given a list of numbers with duplicate number in it. Find all unique permutations. +Given a collection of numbers that might contain duplicates, +return all possible unique permutations. Example For numbers [1,2,2] the unique permutations are: @@ -25,6 +61,88 @@ */ +/* +Use visited[i] to mark visited copies +*/ +class Solution { + boolean[] visited; + public List> permuteUnique(int[] nums) { + List> rst = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return rst; + } + Arrays.sort(nums); + int n = nums.length; + visited = new boolean[n]; + dfs(rst, new ArrayList<>(), nums); + return rst; + } + + private void dfs(List> rst, List list, int[] nums) { + if (list.size() == nums.length) { + rst.add(new ArrayList<>(list)); + return; + } + for (int i = 0; i < nums.length; i++) { + if (visited[i] || (i - 1 >= 0 && visited[i - 1] && nums[i] == nums[i - 1])) { + continue; + } + visited[i] = true; + list.add(nums[i]); + dfs(rst, list, nums); + visited[i] = false; + list.remove(list.size() - 1); + } + } +} + +// Sort +/* +Thoughts: +1. Use nums to swap nodes +2. Each swap should produce a unique row +3. Iterate over position to swap x, and then iterative over (x + 1, n) to swap each one +*/ +class Solution { + public List> permuteUnique(int[] nums) { + List> rst = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return rst; + } + int n = nums.length; + Arrays.sort(nums); + List list = new ArrayList<>(); + for (int num : nums) { + list.add(num); + } + + rst.add(new ArrayList<>(list)); + + for (int pos = 0; pos < n; pos++) { + for (int i = rst.size() - 1; i >= 0; i--) { + list = rst.get(i); + Collections.sort(list.subList(pos, list.size())); + + for (int j = pos + 1; j < n; j++) { + if (list.get(j) == list.get(j - 1)) { + continue; + } + swap(list, pos, j); + rst.add(new ArrayList<>(list)); + swap(list, pos, j); + } + } + } + + return rst; + } + + private void swap(List list, int x, int y) { + int temp = list.get(x); + list.set(x, list.get(y)); + list.set(y, temp); + } +} /* Thoughts: @@ -89,10 +207,6 @@ public ArrayList> permuteUnique(ArrayList nums) { Do a backtrack on the dfs, to make sure a element has same chance of 'selectd' or 'non-solectd' */ class Solution { - /** - * @param nums: A list of integers. - * @return: A list of unique permutations. - */ public ArrayList> permuteUnique(ArrayList nums) { ArrayList> rst = new ArrayList>(); if (nums == null || nums.size() == 0) { @@ -127,5 +241,4 @@ public void dfs (ArrayList nums, ArrayList list, - ``` \ No newline at end of file diff --git a/Java/Permutations.java b/Java/Permutations.java old mode 100644 new mode 100755 index 89bfab2..f93b714 --- a/Java/Permutations.java +++ b/Java/Permutations.java @@ -1,26 +1,41 @@ -还是递归: 取,或者不取。 -Iterative: 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍。 -``` -/* -Given a list of numbers, return all possible permutations. - -Example -For nums [1,2,3], the permutaions are: - -[ +M +1533623494 +tags: Backtracking, DFS, Permutation - [1,2,3], +#### Recursive: Backtracking +- Given a remaining list: 取, 或者不取 +- always iterate over full `nums[]`, use list.contains() to check if item has been added. +- Improvement: maintain list (add/remove elements) instead of 'list.contains' +- time O(n!): visit all possible outcome +- T(n) = n * T(n-1) + O(1) - [1,3,2], +#### Iterative: Insertion +- 插入法: +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- 但是比dfs要快, 因该是因为 # of checks 少: 不需要check list.size(), 不需要maintain remaining list. - [2,1,3], +#### Previous Notes +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- A bit slower, possibly because of the polling and saving the entire list every time - [2,3,1], - [3,1,2], - - [3,2,1] +``` +/* +Given a collection of distinct numbers, return all possible permutations. +For example, +[1,2,3] have the following permutations: +[ + [1,2,3], + [1,3,2], + [2,1,3], + [2,3,1], + [3,1,2], + [3,2,1] ] Challenge @@ -30,64 +45,155 @@ Recursion Search */ +class Solution { + public List> permute(int[] nums) { + List> result = new ArrayList>(); + if (nums == null || nums.length == 0) { + return result; + } + + dfs(result, new ArrayList<>(), nums); + return result; + } + + private void dfs(List> result, List levelList, int[] nums) { + if (levelList.size() == nums.length) { + result.add(new ArrayList<>(levelList)); + return; + } + for (int i = 0; i < nums.length; i++) { + if (levelList.contains(nums[i])) continue; + levelList.add(nums[i]); + dfs(result, levelList, nums); + levelList.remove(levelList.size() - 1); + } + } +} /* - Thoughts: 12.07.2015 recap - recursive: - pass list, rst, nums. - when list.size() == nums.size(), add to rst and return. - Need to re-add all of those non-added spots. So do for loop everytime to try all possible ways. - note: check if !list.contains(candiate). +Thoughts: +1. Backtracking: could start from any digit, until all elements are used +2. Need a easily-resizable object to pass through DFS function such that we can maintain the items left at current level +3. Each level loops through that levelList, minums one item and move to next level. +4. Check condition: when index reach to end, add to result + +Slightly optimized becaue we are not using list.contains(...) which essentially is a search O(nLog(n)) */ +class Solution { + public List> permute(int[] nums) { + List> result = new ArrayList>(); + if (nums == null || nums.length == 0) { + return result; + } + List numList = new ArrayList<>(); + for (int num : nums) { + numList.add(num); + } + dfs(result, new ArrayList<>(), numList); + return result; + } + + private void dfs(List> result, List levelList, List remainingList) { + if (remainingList.size() == 0) { + result.add(new ArrayList<>(levelList)); + return; + } + for (int i = 0; i < remainingList.size(); i++) { + int num = remainingList.get(i); + levelList.add(num); + remainingList.remove(i); + + dfs(result, levelList, remainingList); + + levelList.remove(levelList.size() - 1); + remainingList.add(i, num); + } + } +} /* - 12.07.2015 - Now, do a non-recursive. +Build permutation by insertion: +- Start with 1st element. +- Add 2nd element in front/end of 1st element; and add the new layout to rst +- Add 3rd element in front, between, end ... +- Loop over elements in nums => rst list => each row +*/ +class Solution { + public List> permute(int[] nums) { + List> rst = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return rst; + } + rst.add(new ArrayList<>()); + + for (int i = 0; i < nums.length; i++) { // Pick element + int currRstSize = rst.size(); + for (int j = 0; j < currRstSize; j++) { // pick base list + List baselist = rst.get(j); + // Pick slot to insert element + for (int index = 0; index < baselist.size(); index++) { + baselist.add(index, nums[i]); + rst.add(new ArrayList<>(baselist)); + baselist.remove(index); + } + baselist.add(nums[i]); + } + } + + return rst; + } +} + + + +// Previous notes + +/* + non-recursive. Set up a queue, add all elements into it. give a size/level variable to keep track of cycle. - + Slow, because every queue iteration requires O(n) to maintain entire list */ class Solution { - /** - * @param nums: A list of integers. - * @return: A list of permutations. - */ - public ArrayList> permute(ArrayList nums) { - ArrayList> rst = new ArrayList>(); - if (nums == null || nums.size() == 0) { + public List> permute(int[] nums) { + List> rst = new ArrayList<>(); + if (nums == null || nums.length == 0) { return rst; } - Queue> queue = new LinkedList>(); - ArrayList list; + List numList = new ArrayList<>(); for (int num : nums) { - list = new ArrayList(); + numList.add(num); + } + int n = nums.length; + + Queue> queue = new LinkedList<>(); + List list; + for (int num : nums) { + list = new ArrayList<>(); list.add(num); - queue.offer(new ArrayList(list)); + queue.offer(new ArrayList<>(list)); } while (!queue.isEmpty()) { list = queue.poll(); - if (list.size() == nums.size()) { - rst.add(new ArrayList(list)); + if (list.size() == n) { + rst.add(new ArrayList<>(list)); continue; } - for (int i = 0; i < nums.size(); i++) { - if (!list.contains(nums.get(i))) { - list.add(nums.get(i)); - queue.offer(new ArrayList(list)); - list.remove(list.size() - 1); - } + List candidates = new ArrayList<>(numList); + candidates.removeAll(list); + for (int num : candidates) { + list.add(num); + queue.offer(new ArrayList<>(list)); + list.remove(list.size() - 1); } } return rst; } } - - - /* - +BAD: contains() is O(logn) Thinking Process: 1. Very similar idea: choose or not choose (1 / 0) A key point is: when jumpped into next level of recursion, the 'list' will surely be filled up until it reach the max length. @@ -97,10 +203,6 @@ public ArrayList> permute(ArrayList nums) { */ class Solution { - /** - * @param nums: A list of integers. - * @return: A list of permutations. - */ public ArrayList> permute(ArrayList nums) { ArrayList> rst = new ArrayList>(); if (nums == null || nums.size() == 0) { @@ -125,6 +227,31 @@ public void helper(ArrayList> rst, ArrayList list, A } } } +//Same solution as above, for Leetcode: +public class Solution { + public List> permute(int[] nums) { + List> rst = new ArrayList>(); + if (nums == null || nums.length == 0) { + return rst; + } + helper(rst, new ArrayList(), nums); + return rst; + } + + public void helper(List> rst, ArrayList list, int[] nums) { + if (list.size() == nums.length) { + rst.add(new ArrayList(list)); + } + + for (int i = 0; i < nums.length; i++) { + if (!list.contains(nums[i])) { + list.add(nums[i]); + helper(rst, list, nums); + list.remove(list.size() - 1); + } + } + } +} ``` \ No newline at end of file diff --git a/Java/Plus One.java b/Java/Plus One.java old mode 100644 new mode 100755 index 4659f19..46b8980 --- a/Java/Plus One.java +++ b/Java/Plus One.java @@ -1,6 +1,13 @@ +E +1516342836 +tags: Array, Math + +简单的实现, 加1, 进位. 唯一取巧的地方, 最后如果要多一位, 一定是10000...这个模式, 可以走捷径, 直接来个+1size的array, 然后第一位=1. +注意,转换成long也不合理,用太多memory. +``` /* Given a non-negative number represented as an array of digits, plus one to the number. - +You may assume the integer do not contain any leading zero, except the number 0 itself. The digits are stored such that the most significant digit is at the head of the list. @@ -13,6 +20,33 @@ Array */ +/** +Thoughts: +Loop over all digit and see if it advanced into next position. If not, return digits. +Check if last position if over 10, if so, create a new array and set last pos = 1. + */ +public class Solution { + public int[] plusOne(int[] digits) { + if(digits.length==0) return digits; + digits[digits.length-1] += 1; + //Check index digit.length-1 to 1 + for(int i = digits.length-1; i>0; i--){ + if(digits[i] == 10){ + digits[i]=0; + digits[i-1]+=1; + } + else return digits; + } + + //Check index 0. If ==0, set it to 0 and carry over 1 + if(digits[0]==10){ + int[] output = new int[digits.length+1]; + output[0] = 1; + return output; + } + else return digits; + } +} public class Solution { public int[] plusOne(int[] digits) { @@ -93,3 +127,5 @@ public int[] plusOne(int[] digits) { } + +``` \ No newline at end of file diff --git a/Java/Populating Next Right Pointers in Each Node II.java b/Java/Populating Next Right Pointers in Each Node II.java new file mode 100755 index 0000000..5de3007 --- /dev/null +++ b/Java/Populating Next Right Pointers in Each Node II.java @@ -0,0 +1,179 @@ +M +1532062919 +tags: Tree, DFS +time: O(n) +space: O(1) + +给一个binary tree, 用constant space link 所有所有node.next to same level next node. + +#### DFS +- 用constant space 也就是不可以BFS, 但是mention了用dfs stack space没问题 (提示啊!) +- 1. link leftChild -> rightChild +- 2. resolve root.rightMost child -> first possible root.next.left/right child +- 3. dfs connect(rightChild), connect(leftChild) +- Each level should be fully linked from left side, so every reach to parent will have valid path or end. + +#### Trick +- 1. 处理 nextNode -> next -> next ...的case: 找到第一个有child的next node才可以罢休. 这个case很容易miss +- 2. 我们的假设是, 上一个level的所有node都应该是linked, 那么在dfs时候, 就应该先connect(root.right). 右孩子的全处理完毕, 那么trick1才可以施行. + +``` +/** +Given a binary tree + +struct TreeLinkNode { + TreeLinkNode *left; + TreeLinkNode *right; + TreeLinkNode *next; +} +Populate each next pointer to point to its next right node. +If there is no next right node, the next pointer should be set to NULL. + +Initially, all next pointers are set to NULL. + +Note: + +You may only use constant extra space. +Recursive approach is fine, implicit stack space does not count as extra space for this problem. +Example: + +Given the following binary tree, + + 1 + / \ + 2 3 + / \ \ +4 5 7 +After calling your function, the tree should look like: + + 1 -> NULL + / \ + 2 -> 3 -> NULL + / \ \ +4-> 5 -> 7 -> NULL + +*/ + +/* +DFS: +1. link leftChild -> rightChild +2. resolve root.rightMost child -> first possible root.next's left/right child +3. dfs connect(rightChild), connect(leftChild) +Each level should be fully linked from left side, so every reach to parent will have valid path or end. +*/ +public class Solution { + public void connect(TreeLinkNode root) { + if (root == null) return; + if (root.left == null && root.right == null) return; + + // link children, if applicable + if (root.left != null) { + root.left.next = root.right; + } + + // resolve root's right-most children, with neighbor + TreeLinkNode rightMostNode = root.right != null ? root.right : root.left; + TreeLinkNode nextNode = root.next; + while (nextNode != null) { + if (nextNode.left != null || nextNode.right != null) { + rightMostNode.next = nextNode.left != null ? nextNode.left : nextNode.right; + break; + } + nextNode = nextNode.next; + } + + connect(root.right); + connect(root.left); + } +} + + +/* +Follow up for problem "Populating Next Right Pointers in Each Node". + +What if the given tree could be any binary tree? Would your previous solution still work? + +Note: + +You may only use constant extra space. +For example, +Given the following binary tree, + 1 + / \ + 2 3 + / \ \ + 4 5 7 +After calling your function, the tree should look like: + 1 -> NULL + / \ + 2 -> 3 -> NULL + / \ \ + 4-> 5 -> 7 -> NULL +Hide Company Tags Microsoft Bloomberg Facebook +Hide Tags Tree Depth-first Search +Hide Similar Problems (M) Populating Next Right Pointers in Each Node + +*/ + + // Previous solution, think too much : ) + /* + DFS to traverse the tree. + Also BFS using next pointer. clear node's children level per visit + Populating Next Right Pointers in Each Node I 里面依赖parent.next.left来作链接,但现在这个parent.next.left很可能也是Null. +1. 于是需要移动parent去找children level的next node。 +2. 并且每次在一个level, 要用BFS的思想把所有parent 过一遍,也就是把parent 正下方的children全部用.next链接起来 + 原因: 到下一层children变成parent, 他们需要彼此之间的connection, grand children才可以相互连接。 +Note: runtime O(n * 2^log(n) ) = O(n^2), not good. + */ + public class Solution { + public void connect(TreeLinkNode root) { + if (root == null || (root.left == null && root.right == null)) { + return; + } + dfs(root); + } + //Clear connection problems on each level: because the lower children level will relay on parent's level connection. + public void dfs(TreeLinkNode node) { + if (node == null) { + return; + } + TreeLinkNode parent = node; + while (parent != null) { + //Connect left-> right in normal case + if (parent.left != null) { + parent.left.next = parent.right; + } + //Left || right: one of needs to use addRight(node) method to build the .next bridge. + if (parent.left != null && parent.left.next == null) {//Fact: parent.right = null, from last step + parent.left.next = addRight(parent); + } else if (parent.right != null && parent.right.next == null) { + parent.right.next = addRight(parent); + } + parent = parent.next; + } + + dfs(node.left); + dfs(node.right); + } + + //Always take parentNode, and try to return the very first available node at child level + public TreeLinkNode addRight(TreeLinkNode node) { + while (node != null) { + if (node.next == null) { + return null; + } + if (node.next.left != null) { + return node.next.left; + } + if (node.next.right != null) { + return node.next.right; + } + node = node.next; + } + return null; + } + +} + + +``` \ No newline at end of file diff --git a/Java/Populating Next Right Pointers in Each Node.java b/Java/Populating Next Right Pointers in Each Node.java new file mode 100755 index 0000000..6ff8265 --- /dev/null +++ b/Java/Populating Next Right Pointers in Each Node.java @@ -0,0 +1,147 @@ +M +1519715027 +tags: Tree, DFS, Divide and Conquer + +给一个特殊的binary tree, treeNode里面有一个 next pointer. + +写一个function, 把所有node都更同level的node 连在一起. 最右边的node.next = NULL + +#### DFS + Divide and Conquer +- 题目要求DFS. 想清楚了如何在DFS level把几种情况都考虑了, 写起来很简单. NOT BFS, because requires O(1) space +- 对于一个root来说, 只有几个点可以顾忌到: root.left, root.right, root.next. +- 想办法把这三个方向的点, 能连起来的都连起来: +- 1. `node.left.next = node.right` +- 2. If `node.next != null`, link `node.right.next = node.next.left`; +- 然后在dfs(root.left), dfs(root.right) +- Time: visit && connect all nodes, O(n) + +#### BFS +- 不和题意,用了queue space,与Input成正比。太大。 +- BFS over Tree。 用Queue 和 queue.size(),老规矩。 +- process每层queue时, 注意把next pointer加上去就好. + +``` +/* +Given a binary tree + + struct TreeLinkNode { + TreeLinkNode *left; + TreeLinkNode *right; + TreeLinkNode *next; + } +Populate each next pointer to point to its next right node. +If there is no next right node, the next pointer should be set to NULL. + +Initially, all next pointers are set to NULL. + +Note: + +You may only use constant extra space. +You may assume that it is a perfect binary tree +(ie, all leaves are at the same level, and every parent has two children). + +For example, +Given the following perfect binary tree, + 1 + / \ + 2 3 + / \ / \ + 4 5 6 7 +After calling your function, the tree should look like: + 1 -> NULL + / \ + 2 -> 3 -> NULL + / \ / \ + 4->5->6->7 -> NULL +Hide Tags Tree Depth-first Search +Hide Similar Problems (H) Populating Next Right Pointers in Each Node II (M) Binary Tree Right Side View + +*/ + +/** + * Definition for binary tree with next pointer. + * public class TreeLinkNode { + * int val; + * TreeLinkNode left, right, next; + * TreeLinkNode(int x) { val = x; } + * } + */ +/* +Thoughts: +BFS with queue will be trival, but 2^(logN) = N, so O(n) space can't do. +DFS: at each level, carefully link: +1. node.left.next = node.right +2. If node.next != null, link node.right.next = node.next.left; +*/ +public class Solution { + public void connect(TreeLinkNode root) { + if (root == null || root.left == null || root.right == null) { + return; + } + root.left.next = root.right; + if (root.next != null) { + root.right.next = root.next.left; + } + connect(root.left); + connect(root.right); + } +} + +// Previous solution. Actuall no need of a helper dfs. +//DFS. Basic implementation according to problem. + public class Solution { + public void connect(TreeLinkNode root) { + if (root == null || (root.left == null && root.right == null)) { + return; + } + root.left.next = root.right; + dfs(root.left); + dfs(root.right); + } + + public void dfs(TreeLinkNode node) { + if (node == null || node.left == null || node.right == null) { + return; + } + node.left.next = node.right; + if (node.next != null) + node.right.next = node.next.left; + dfs(node.left); + dfs(node.right); + } +} + + + +// # of leaf nodes: 2 ^ (height) ~= 2 ^ (logN) = N => used O(N) space +//BFS, However, 不和题意。 point each node to the next in queue. if none, add null +public class Solution { + public void connect(TreeLinkNode root) { + if (root == null || (root.left == null && root.right == null)) { + return; + } + Queue queue = new LinkedList(); + queue.offer(root); + int size = 0; + + while (!queue.isEmpty()) { + size = queue.size(); + for (int i = 0; i < size; i++) { + TreeLinkNode node = queue.poll(); + if (i < size - 1) { + node.next = queue.peek(); + } else { + node.next = null; + } + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + size = queue.size(); + } + } +} +``` diff --git a/Java/Pow(x, n).java b/Java/Pow(x, n).java new file mode 100755 index 0000000..bf16c05 --- /dev/null +++ b/Java/Pow(x, n).java @@ -0,0 +1,130 @@ +M +1519508947 +tags: Math, Binary Search + +傻做就O(n), 要更好就考虑O(logN). +减少重复计算, 一切两半. + +注意: +- n/2的奇数偶数 +- n的正负 +- n == 0的情况, x == 0, x == 1 的情况. +``` +/** +LeetCode: +Implement pow(x, n). + + +Example 1: + +Input: 2.00000, 10 +Output: 1024.00000 +Example 2: + +Input: 2.10000, 3 +Output: 9.26100 + */ +/* +Thoughts: +Given n, O(n) can get result. +Want to improve to O(logN) +Divide the n to half, and do val= myPow(x, n/2); +1. If n == even: return val * val +2. If n != even: return val * val * x; + +Also, consider the sign of x. +Even -> pos +Odd -> neg if x is negative + +What if n < 0 + +*/ +class Solution { + public double myPow(double x, int n) { + if (x == 0 || x == 1 || n == 1) { + return x; + } + if (n == 0) { + return 1; + } + double rst = helper(Math.abs(x), Math.abs(n)); + int sign = x < 0 && n % 2 == 1 ? -1 : 1; + if (n < 0) { + return sign / rst; + } else { + return sign * rst; + } + } + + public double helper(double x, int n) { + if (n == 0) { + return 1; + } + if (n == 1) { + return x; + } + double val = helper(x, n / 2); + if (n % 2 == 0) { + return val * val; + } else { + return val * val * x; + } + } +} + + +/* +Pow(x, n) + +Implement pow(x, n). + +Have you met this question in a real interview? Yes +Example +Pow(2.1, 3) = 9.261 +Pow(0, 1) = 0 +Pow(1, 0) = 1 +Note +You don't need to care about the precision of your answer, it's acceptable if the expected answer and your answer 's difference is smaller than 1e-3. + +Challenge +O(logn) time + +Tags Expand +Binary Search LinkedIn Divide and Conquer Mathematics Facebook +*/ + +/* + Everytime: if divide the power n by 2, then it equlas to pow(x,n/2) * pow(x, n/2). + Also consider n could be negative. Let myPow handle the negative n. basically return 1/myPow(x,n) + Cnsider the case of 0: x^0 = 1. + use a helper funtion: when n%2 == 0, regular; if n%2 ==1, do pow(x, (n-1)/2) * x. + Note: n/2 = (n-1)/2. So this can be optmized. +*/ + +public class Solution { + /** + * @param x the base number + * @param n the power number + * @return the result + */ + public double myPow(double x, int n) { + if(n >= 0) { + return pow(x, n); + } else { + return 1/pow(x, n); + } + } + + public double pow(double x, int n) { + if (n == 0) { + return 1; + } + double num = pow(x, n/2); + if (n % 2 == 0) { + return num * num; + } + return num * num * x; + } +} + +``` \ No newline at end of file diff --git a/Java/Power of Three.java b/Java/Power of Three.java new file mode 100755 index 0000000..2778595 --- /dev/null +++ b/Java/Power of Three.java @@ -0,0 +1,98 @@ +E +1516341495 +tags: Math + +方法1: +Power of 3: 3 ^ x == n ? +意思是 n / 3 一直除, 最后是可以等于1的, 那么就有了 n/=3, check n%3, 最后看结果是不是整除到1的做法. 用while loop. + +方法2: +如果n是power of 3, 那么 3 ^ x的这个 x一定是个比n小的数字. 那么可以在 0 ~ n 之间做binary serach, 但是就比较慢. + +方法3: +巧妙的想法.最大的3^x integer是 3^19. 那么找到这个数, 一定可以被n整除. 一步到位. + +``` +/* + +Given an integer, write a function to determine if it is a power of three. + +Follow up: +Could you do it without using any loop / recursion? + +Credits: +Special thanks to @dietpepsi for adding this problem and creating all test cases. + +Hide Company Tags Google +Hide Tags Math +Hide Similar Problems (E) Power of Two + + +*/ + +/* +Thoughts: +Use binary serach: pick an item, do 3^x and see if equals to n, < n or >n, then move the number. +*/ +class Solution { + public boolean isPowerOfThree(int n) { + if (n <= 0) { + return false; + } + int start = 0; + int end = n; + while (start + 1 < end) { + int mid = start + (end - start)/2; + long powerOfThree = (long) Math.pow(3, mid); + if (powerOfThree == n) { + return true; + } + if (powerOfThree < n) { + start = mid; + } else { + end = mid; + } + } + return Math.pow(3, start) == n; + } +} + +//Check if n = 3 ^ x; +public class Solution { + public boolean isPowerOfThree(int n) { + if (n <= 0) { + return false; + } + if (n == 1) { + return true; + } + if (n % 3 != 0) { + return false; + } + return isPowerOfThree(n / 3); + } +} + +// Shorter version +class Solution { + public boolean isPowerOfThree(int n) { + while (n/3>=1){ + if (n%3!=0) return false; + n/=3; + } + return n==1; + } +} + +/* +Thoughts: +The largest int is 2^31, and the largest 3^x = 3^19. +If n is power of 3, it should 3^19 % n should == 0 +*/ +class Solution { + public boolean isPowerOfThree(int n) { + return (n > 0 && Math.pow(3, 19) % n == 0); + } +} + +``` \ No newline at end of file diff --git a/Java/Power of Two.java b/Java/Power of Two.java new file mode 100644 index 0000000..56b283e --- /dev/null +++ b/Java/Power of Two.java @@ -0,0 +1,29 @@ +E +1516343302 +tags: Math, Bit Manipulation + +跟powerOfThree一样: 可以loop, check mod; 也可以用binary search找合适的数字. + +``` +/* +Given an integer, write a function to determine if it is a power of two. +*/ + +/* +Similar to powerOfThree. Two ways: 1 divide and check mode; 2. binary search +*/ +class Solution { + public boolean isPowerOfTwo(int n) { + if (n <= 0) { + return false; + } + while (n != 1) { + if (n % 2 != 0 || n / 2 == 0) { + return false; + } + n/=2; + } + return true; + } +} +``` \ No newline at end of file diff --git a/Java/Predict the Winner.java b/Java/Predict the Winner.java new file mode 100644 index 0000000..85dcf6d --- /dev/null +++ b/Java/Predict the Winner.java @@ -0,0 +1,59 @@ +M +1534306928 +tags: DP, MiniMax + +Detailed in `Coins in a Line III` + +``` +/* +Coins in a Line III + +Given an array of scores that are non-negative integers. Player 1 picks one of the numbers from either end of the array followed by the player 2 and then player 1 and so on. Each time a player picks a number, that number will not be available for the next player. This continues until all the scores have been chosen. The player with the maximum score wins. + +Given an array of scores, predict whether player 1 is the winner. You can assume each player plays to maximize his score. + +Example 1: +Input: [1, 5, 2] +Output: False +Explanation: Initially, player 1 can choose between 1 and 2. +If he chooses 2 (or 1), then player 2 can choose from 1 (or 2) and 5. If player 2 chooses 5, then player 1 will be left with 1 (or 2). +So, final score of player 1 is 1 + 2 = 3, and player 2 is 5. +Hence, player 1 will never be the winner and you need to return False. +Example 2: +Input: [1, 5, 233, 7] +Output: True +Explanation: Player 1 first chooses 1. Then player 2 have to choose between 5 and 7. No matter which number player 2 choose, player 1 can choose 233. +Finally, player 1 has more score (234) than player 2 (12), so you need to return True representing player1 can win. +Note: +1 <= length of the array <= 20. +Any scores in the given array are non-negative integers and will not exceed 10,000,000. +If the scores of both players are equal, then player 1 is still the winner. + + +*/ + +class Solution { + public boolean PredictTheWinner(int[] nums) { + if (nums == null || nums.length == 0) { + return false; + } + + int n = nums.length; + int[][] dp = new int[n][n]; + + // len = 1 + for (int i = 0; i < n; i++) { + dp[i][i] = nums[i]; + } + + // len = 2 + for (int len = 2; len <= n; len++) { + for (int i = 0; i <= n - len; i++) { + int j = len + i - 1; + dp[i][j] = Math.max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]); + } + } + return dp[0][n - 1] >= 0; + } +} +``` \ No newline at end of file diff --git a/Java/Prefix and Suffix Search.java b/Java/Prefix and Suffix Search.java new file mode 100644 index 0000000..f276f50 --- /dev/null +++ b/Java/Prefix and Suffix Search.java @@ -0,0 +1,51 @@ +H +tags: Trie + +``` +/* +Given many words, words[i] has weight i. + +Design a class WordFilter that supports one function, +WordFilter.f(String prefix, String suffix). +It will return the word with given prefix and suffix with maximum weight. If no word exists, return -1. + +Examples: +Input: +WordFilter(["apple"]) +WordFilter.f("a", "e") // returns 0 +WordFilter.f("b", "") // returns -1 +Note: +words has length in range [1, 15000]. +For each test case, up to words.length queries WordFilter.f may be made. +words[i] has length in range [1, 10]. +prefix, suffix have lengths in range [0, 10]. +words[i] and prefix, suffix queries consist of lowercase letters only. + + +*/ + +/* +Thought1: +TrieNode { + boolean isEnd; int weight; + + // suffix use as validation for suffix search, built based on [prefixIndex+1, end]. + // This will requires lots of in-memory space to save all possible suffix: [e, le, ple, pple] all map to apple + HashMap> suffix map; + Map children +} +Functions: +insert(); // build trie +generateSubStrings(int i): set +f: search using prefix, find the TrieNode where prefix lands; use suffix to find list of weights. Return max. +search time: O(n) to find max +*/ + +/* +Thought2: +Build 2 trie structure: regular order, and reverse order. +search: find set1, find set2; +intersect to return max. +search time: O(n) compare, O(n) find max +*/ +``` \ No newline at end of file diff --git a/Java/Product of Array Except Self.java b/Java/Product of Array Except Self.java new file mode 100644 index 0000000..29ee3d8 --- /dev/null +++ b/Java/Product of Array Except Self.java @@ -0,0 +1,83 @@ +M +1531595945 +tags: Array, PreProduct +time: O(n) +space: O(1) + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 看第二个解答, 进一步简化了代码 +- PreProduct, 跟preSum的感觉有点像, 就是差一位. + +``` +/* +Given an array nums of n integers where n > 1, +return an array output such that output[i] is equal to the product of all the elements of nums except nums[i]. + +Example: + +Input: [1,2,3,4] +Output: [24,12,8,6] +Note: Please solve it without division and in O(n). + +Follow up: +Could you solve it with constant space complexity? +(The output array does not count as extra space for the purpose of space complexity analysis.) +*/ + +class Solution { + public int[] productExceptSelf(int[] nums) { + if (nums == null || nums.length <= 1) return new int[]{}; + + // check input for len = 1 + int n = nums.length; + int[] rst = new int[n]; + + // Bulid from left + // for (int i = 0; i < n; i++) rst[i] = 1; // init removed for simplification + rst[0] = 1; + int carry = nums[0]; + for (int i = 1; i < n; i++) { + rst[i] = carry; + carry *= nums[i]; + } + + // Build from right + carry = nums[n - 1]; + for (int i = n - 2; i >= 0; i--) { + rst[i] *= carry; + carry *= nums[i]; + } + + return rst; + } +} + +// Modulize and Simplification +class Solution { + public int[] productExceptSelf(int[] nums) { + if (nums == null || nums.length <= 1) return new int[]{}; + + // check input for len = 1 + int n = nums.length; + int[] rst = new int[n]; + + for (int i = 0; i < n; i++) rst[i] = 1; // init + + int carry = nums[0]; // Bulid from left + for (int i = 1; i < n; i++) carry = multiply(rst, nums, i, carry); + + carry = nums[n - 1]; // Build from right + for (int i = n - 2; i >= 0; i--) carry = multiply(rst, nums, i, carry); + return rst; + } + + private int multiply(int[] rst, int nums[], int i, int carry) { + rst[i] *= carry; + return carry * nums[i]; + } +} +``` \ No newline at end of file diff --git a/Java/Product of Array Exclude Itself.java b/Java/Product of Array Exclude Itself.java old mode 100644 new mode 100755 index 2eb4432..65d9e95 --- a/Java/Product of Array Exclude Itself.java +++ b/Java/Product of Array Exclude Itself.java @@ -1,4 +1,31 @@ +M +tags: Array + + +``` +/* +LeetCode: +Given an array nums of n integers where n > 1, +return an array output such that output[i] is equal to the product of all the elements of nums except nums[i]. + +Example: + +Input: [1,2,3,4] +Output: [24,12,8,6] +Note: Please solve it without division and in O(n). + +Follow up: +Could you solve it with constant space complexity? +(The output array does not count as extra space for the purpose of space complexity analysis.) + +*/ + + + + + /* +LintCode Given an integers array A. Define B[i] = A[0] * ... * A[i-1] * A[i+1] * ... * A[n-1], calculate B WITHOUT divide operation. @@ -42,3 +69,5 @@ public ArrayList productExcludeItself(ArrayList A) { } + +``` \ No newline at end of file diff --git a/Java/Queue Reconstruction by Height.java b/Java/Queue Reconstruction by Height.java new file mode 100644 index 0000000..52bfd52 --- /dev/null +++ b/Java/Queue Reconstruction by Height.java @@ -0,0 +1,102 @@ +M +1516438554 +tags: Greedy + +别无他法, 只能写一遍例子, 找规律,然后greedy.  +需要写一遍发现的规律比如: 从h大的开始排列, 先放入k小的. 写comparator的时候要注意正确性. +如果要sort, 并且灵活insert:用arrayList. 自己做一个object. +最后做那个'matchCount'的地方要思路清晰, 找到最正确的spot, 然后greedy insert. + +O(n) space, O(nLog(n)) time, because of sorting. + +可能有简化的余地, 代码有点太长. +比如试一试不用额外空间? + +``` +/* +Suppose you have a random list of people standing in a queue. +Each person is described by a pair of integers (h, k), where h is the height of the person +and k is the number of people in front of this person who have a height greater than or equal to h. +Write an algorithm to reconstruct the queue. + +Note: +The number of people is less than 1,100. + +Example + +Input: +[[7,0], [4,4], [7,1], [5,0], [6,1], [5,2]] + +Output: +[[5,0], [7,0], [5,2], [6,1], [4,4], [7,1]] + +*/ + +/* +Thoughts: +Not having a great idea in mind, but we can pick each people and insert them into 'best at moment' spots. +Use arrayList to make insertion easier. +Greedy solution. +*/ +class Solution { + class People { + public int h; + public int k; + public People(int h, int k) { + this.h = h; + this.k = k; + } + } + + public int[][] reconstructQueue(int[][] people) { + if (people == null || people.length == 0 || people[0] == null || people[0].length == 0) { + return people; + } + final int[][] result = new int[people.length][people[0].length]; + + // Set up the list and sort + final List peopleList = new ArrayList<>(); + for (int i = 0; i < people.length; i++) { + peopleList.add(new People(people[i][0], people[i][1])); + } + Collections.sort(peopleList, new Comparator() { + public int compare(People p1, People p2) { + if (p1.h != p2.h) { + return p2.h - p1.h; + } else { + return p1.k - p2.k; + } + } + }); + + // Find correct index and insert + final List resultList = new ArrayList<>(); + for (int i = 0; i < peopleList.size(); i++) { + final People ppl = peopleList.get(i); + int insertIndex = findCorrectIndex(resultList, ppl.h, ppl.k); + resultList.add(insertIndex, ppl); + } + + // Output result + for (int i = 0; i < resultList.size(); i++) { + result[i][0] = resultList.get(i).h; + result[i][1] = resultList.get(i).k; + } + return result; + } + + public int findCorrectIndex(final List peopleList, int h, int k) { + int matchCount = 0; + int index = 0; + for (; index < peopleList.size(); index++) { + final People ppl = peopleList.get(index); + matchCount += ppl.h >= h ? 1 : 0; + if (matchCount > k) { + return index; + } + } + return index; + } +} + +``` \ No newline at end of file diff --git a/Java/QuickSort.java b/Java/QuickSort.java old mode 100644 new mode 100755 index 9f1a879..35c4e1e --- a/Java/QuickSort.java +++ b/Java/QuickSort.java @@ -1,49 +1,51 @@ -代码是不难的. +M +1526358989 +tags: Sort, Quick Sort -首先partition. 返还一个partition的那个中间点的位置。 -然后劈开两半。 -前后各自 quick sort, recursively +implement quick sort. -注意:在partition里面, 比较的时候nums[start] < pivot, nums[end]>pivot, 如果写成了 <= 会 stack overflow. +#### Quick Sort +- 首先partition. 返还一个partition的那个中间点的位置: 这个时候, 所有小于nums[partitionIndex] 都应该在 partitionIndex左边 +- 然后劈开两半 +- 前后各自 quick sort, recursively +- 注意:在partition里面, 比较的时候nums[start] < pivot, nums[end]>pivot, 如果写成了 <= 会 stack overflow. +- Time O(nlogn), Space: O(1) - -但是: 在partition array那个题目里面,第二个 nums[end] >= pivot, 是要去加上这个 ‘=’的 ``` /* Quick Sort a array. - -Self test. - */ -class Solution{ - public void quickSort(int[] nums){ +public class Solution { + public void quickSort(int[] nums) { if (nums == null || nums.length == 0) { return; } - sortHelper(nums, 0, nums.length - 1); + dfs(nums, 0, nums.length - 1); } - - public void sortHelper(int[] nums, int start, int end){ + + /** + Partition one side, and recursively partition(swaping). + Once one side finished, move on to partition && sorting the other side. + */ + private void dfs(int[] nums, int start, int end) { if (start >= end) { return; - } else { - - int partitionPoint = partition(nums, start, end); - sortHelper(nums, start, partitionPoint - 1); - sortHelper(nums, partitionPoint, end); } + int partitionIndex = partition(nums, start, end); + dfs(nums, start, partitionIndex - 1); + dfs(nums, partitionIndex, end); } - - public void swap(int[] nums, int x, int y) { - int temp = nums[x]; - nums[x] = nums[y]; - nums[y] = temp; - } - - public int partition(int[] nums, int start, int end){ - int mid = start + (end - start)/2; - + + /** + Use two pointers: find the two points on left/rigth of the pivot that are startNum < pivot < endNum. + Swap them, and keep moving star++, end--. + This operation partition the array into 2 parts: + - numbers less than pivot (unsorted) on left of pivot + - numbers breater than pivot (unsorted) on right of pivot + */ + private int partition(int[] nums, int start, int end) { + int mid = start + (end - start) / 2; int pivot = nums[mid]; while (start <= end) { while (start <= end && nums[start] < pivot) { @@ -60,5 +62,49 @@ public int partition(int[] nums, int start, int end){ } return start; } + + private void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } + + private void printArray(int[] nums, String str) { + System.out.print(str); + if (nums == null || nums.length == 0) { + System.out.println(); + return; + } + for (int num : nums) { + System.out.print(num + ", "); + } + System.out.println(); + } + + public static void main(String[] args) { + Solution sol = new Solution(); + System.out.println("Implement and test quick sort"); + + int[] array = {5,3,2,7,5,4,1,1,2,9,5,3, -1}; + sol.quickSort(array); + sol.printArray(array, "Return: "); + + array = null; + sol.quickSort(array); + sol.printArray(array, "Return Empty: "); + + array = new int[]{ - 1, 2}; + sol.quickSort(array); + sol.printArray(array, "Return: "); + + array = new int[]{ 1,1,1,1,1}; + sol.quickSort(array); + sol.printArray(array, "Return: "); + + array = new int[]{ 19,8,7,6,5,4,3,2,1}; + sol.quickSort(array); + sol.printArray(array, "Return: "); + } } + ``` \ No newline at end of file diff --git a/Java/Random Pick Index.java b/Java/Random Pick Index.java new file mode 100644 index 0000000..8f8893c --- /dev/null +++ b/Java/Random Pick Index.java @@ -0,0 +1,78 @@ +M +1531731969 +tags: Reservior Sampling +time: O(n) +space: O(n) for input int[], O(1) extra space used + +#### Reservior sampling +- Random choose: think about reservoir sampling. https://www.youtube.com/watch?v=A1iwzSew5QY +- Use random generator rd.nextInt(x) pick integer between [0, x) +- try all numbers, when target is met, we want to model reservoir sampling: +- item was chosen out of i samples, and all other samples are failed. +- where we can use 'count' to represent the denominator/base to choose. +- **HAVE TO finish all samples** to make sure equal opportunity +- we can pick that last matched item as result +- `rd.nextInt(count++) == 0` make sure we are always picking num == 0 to meet definition of reservoir sampling. + +#### Knowledge +- If multiply these probablities together to get the probability of one item being chosen with reservior sampling: +- probability = 1/i * (1 - 1/i+1) * (1 - 1/i+2) ....(1 - 1/n) = 1/n + + +``` +/* +Given an array of integers with possible duplicates, randomly output the index of a given target number. +You can assume that the given target number must exist in the array. + +Note: +The array size can be very large. Solution that uses too much extra space will not pass the judge. + +Example: + +int[] nums = new int[] {1,2,3,3,3}; +Solution solution = new Solution(nums); + +// pick(3) should return either index 2, 3, or 4 randomly. +Each index should have equal probability of returning. +solution.pick(3); + +// pick(1) should return 0. Since in the array only nums[0] is equal to 1. +solution.pick(1); +*/ + +/* +Random choose: think about reservoir sampling. +Use random generator rd.nextInt(x) pick integer between [0, x) +Here we can try all numbers, when target is met, we want to model reservoir sampling: +item was chosen out of i samples, and all later samples are failed; multiply these probablities together we'll get the probability in reservior sampling: +1/i * (1 - 1/i+1) * (1 - 1/i+2) ....(1 - 1/n), where we can use 'count' to represent the denominator +we'll pick that one particular number as result +*/ +class Solution { + Random rd; + int[] nums; + public Solution(int[] nums) { + this.rd = new Random(); + this.nums = nums; + } + + public int pick(int target) { + int count = 1, result = -1; + for (int i = 0; i < nums.length; i++) { + if (nums[i] != target) { + continue; + } + if (rd.nextInt(count++) == 0) { + result = i; + } + } + return result; + } +} + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(nums); + * int param_1 = obj.pick(target); + */ +``` \ No newline at end of file diff --git a/Java/Range Sum Query - Immutable.java b/Java/Range Sum Query - Immutable.java new file mode 100644 index 0000000..3001bbd --- /dev/null +++ b/Java/Range Sum Query - Immutable.java @@ -0,0 +1,54 @@ +E +1530161330 +tags: DP, PreSum + +给一串数字, 求sumRange. + +#### PreSum +- 就是pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + +``` +/** +Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. + +Example: +Given nums = [-2, 0, 3, -5, 2, -1] + +sumRange(0, 2) -> 1 +sumRange(2, 5) -> -1 +sumRange(0, 5) -> -3 +Note: +You may assume that the array does not change. +There are many calls to sumRange function. + + */ + + class NumArray { + int[] preSum = null; + public NumArray(int[] nums) { + if (nums == null || nums.length == 0) { + return; + } + int n = nums.length; + preSum = new int[n]; + preSum[0] = nums[0]; + for(int i = 1; i < n; i++) { + preSum[i] = preSum[i - 1] + nums[i]; + } + } + + public int sumRange(int i, int j) { + if (preSum == null || i < 0 || j > preSum.length - 1) return -1; + if (i == 0) return preSum[j]; + return preSum[j] - preSum[i - 1]; + } +} + +/** + * Your NumArray object will be instantiated and called as such: + * NumArray obj = new NumArray(nums); + * int param_1 = obj.sumRange(i,j); + */ +``` \ No newline at end of file diff --git a/Java/Read N Characters Given Read4 II - Call multiple times.java b/Java/Read N Characters Given Read4 II - Call multiple times.java new file mode 100644 index 0000000..d961b7a --- /dev/null +++ b/Java/Read N Characters Given Read4 II - Call multiple times.java @@ -0,0 +1,80 @@ +H +1533532566 +tags: String, Enumeration + +Read N Character using `Read4(char[] buf)` 的加强版: 可以不断读 read(buf, n) + +#### String +- 注意String的index handle, 慢慢写edge case +- 理解题目意思: `read4(char[] buf)` 这样的 `populate input object` 的function稍微少一点. +- 遇到时候, 仔细理解function用法, 不要慌乱. 其实思考方式很简单, 仔细handle string 还有 edge case就好了. + +``` +/* +The API: int read4(char *buf) reads 4 characters at a time from a file. + +The return value is the actual number of characters read. For example, +it returns 3 if there is only 3 characters left in the file. + +By using the read4 API, implement the function int read(char *buf, int n) +that reads n characters from the file. + +Note: +The read function may be called multiple times. + +Example 1: + +Given buf = "abc" +read("abc", 1) // returns "a" +read("abc", 2); // returns "bc" +read("abc", 1); // returns "" +Example 2: + +Given buf = "abc" +read("abc", 4) // returns "abc" +read("abc", 1); // returns "" + + */ + +/* The read4 API is defined in the parent class Reader4. + int read4(char[] buf); */ + +/* +Cache results from read4(char*); only read again if not enough; +Cache the read cacheIndex of read4 result; +If read4 returns 0, just return ""; maybe have global variable to mark end. +*/ +public class Solution extends Reader4 { + /** + * @param buf Destination buffer + * @param n Maximum number of characters to read + * @return The number of characters read + */ + private char[] cache = new char[4]; + private int cacheIndex = 0; + private int count = 0; + //private int index = 0; + private boolean isEnd = false; + public int read(char[] buf, int n) { + if (buf == null || n <= 0 || isEnd) return 0; // if read4 is ended, return 0; + + // decrease n for existing read4 content + int i = 0; // current iteration + while (cacheIndex > 0 && cacheIndex < count && i < n) { + buf[i++] = cache[cacheIndex++]; + } + // read while using while loop until n is exhausted + while (i < n) { + cache = new char[4]; + count = read4(cache); + int range = i + 3 < n ? count : Math.min(n - i, count); + System.arraycopy(cache, 0, buf, i, range); + i += range; + if (range < 4) cacheIndex = range; + if (count < 4 && range == count) isEnd = true; + if (count < 4) break; + } + return i; + } +} +``` \ No newline at end of file diff --git a/Java/Read N Characters Given Read4.java b/Java/Read N Characters Given Read4.java new file mode 100644 index 0000000..9b13365 --- /dev/null +++ b/Java/Read N Characters Given Read4.java @@ -0,0 +1,59 @@ +E +1533408053 +tags: String, Enumeration + +Read4 题目. 理解题目: 是有个input object buff, 会被populated with data. + +#### String in char[] format +- 理解题目: 其实就是track `可以read多少bytes by read4() response` +- 另外一个有用的function `System.arraycopy(src, srcIndex, dest, destIndex, length)` + +``` +/* +The API: int read4(char *buf) reads 4 characters at a time from a file. + +The return value is the actual number of characters read. For example, +it returns 3 if there is only 3 characters left in the file. + +By using the read4 API, implement the function int read(char *buf, int n) +that reads n characters from the file. + +Example 1: + +Input: buf = "abc", n = 4 +Output: "abc" +Explanation: The actual number of characters read is 3, which is "abc". +Example 2: + +Input: buf = "abcde", n = 5 +Output: "abcde" +Note: +The read function will only be called once for each test case. +*/ + +/* +- use temp to store all items read, and System.copy into buf. +- track overall progress: i +- if read4 returns < 4, mark end and return. +*/ +public class Solution extends Reader4 { + /** + * @param buf Destination buffer + * @param n Maximum number of characters to read + * @return The number of characters read + */ + public int read(char[] buf, int n) { + if (buf == null || n <= 0) return 0; + int i = 0; + while (i < n) { + char[] temp = new char[4]; + int count = read4(temp); + int range = i + 3 < n ? count : Math.min(n - i, count); + System.arraycopy(temp, 0, buf, i, range); + i += range; + if (count < 4) break; + } + return i; + } +} +``` \ No newline at end of file diff --git a/Java/Rearrange String k Distance Apart.java b/Java/Rearrange String k Distance Apart.java new file mode 100644 index 0000000..5bd5110 --- /dev/null +++ b/Java/Rearrange String k Distance Apart.java @@ -0,0 +1,98 @@ +H +1531238039 +tags: Hash Table, Heap, Greedy + +给一个string, 全是lowercase letter, 要求重新排列: 然后每个unique的character要有k distance apart. + +跟Task Scheduler有点像, 只不过Task那道题里面还可以用其他方法求count, 这道题要求出排列结果 + +#### PriorityQueue + HashTable +- PriorityQueue排序+分布排列的一个经典用法. +- Count frequency and store in pq. +- Consume element of pq for k rounds, each time pick one element from queue. +- Exception: if k still has content but queue is consumed: cannot complete valid string, return ""; +- space, O(n) extra space in sb, O(26) constant space with pq. +- time: O(n) to add all items + +``` +/* +Given a non-empty string s and an integer k, +rearrange the string such that the same characters are at least distance k from each other. + +All input strings are given in lowercase letters. +If it is not possible to rearrange the string, return an empty string "". + +Example 1: +s = "aabbcc", k = 3 + +Result: "abcabc" + +The same letters are at least distance 3 from each other. +Example 2: +s = "aaabc", k = 3 + +Answer: "" + +It is not possible to rearrange the string. +Example 3: +s = "aaadbbcc", k = 2 + +Answer: "abacabcd" + +Another possible answer is: "abcabcda" + +The same letters are at least distance 2 from each other. +*/ + +/* +Count frequency and store in pq. +Consume element of pq for k rounds, each time pick one element from queue. +Exception: if k still has content but queue is consumed: can't complete valid string, return ""; +*/ +class Solution { + class Node { + char c; + int count; + public Node(char c, int count) { + this.c = c; + this.count = count; + } + } + public String rearrangeString(String s, int k) { + if (s == null || s.length() == 0 || k <= 0) return s; + + PriorityQueue pq = buildQueue(s); + // Process + StringBuffer sb = new StringBuffer(); + while (!pq.isEmpty()) { + Set tempSet = new HashSet<>(); + for (int i = k - 1; i >= 0 && !pq.isEmpty(); i--) { // process k items/round + Node node = pq.poll(); + sb.append(node.c); + + if (node.count > 1) { + node.count--; + tempSet.add(node); + } + // exception case: k not consumed, but still has remains in tempSet + if (pq.isEmpty() && i > 0 && !tempSet.isEmpty()) return ""; + } + pq.addAll(tempSet); + } + + return sb.toString(); + } + + private PriorityQueue buildQueue(String s) { + int[] count = new int[26]; + for (char c : s.toCharArray()) count[c - 'a']++; + + PriorityQueue pq = new PriorityQueue<>((a, b) -> a.count != b.count ? b.count - a.count : a.c - b.c); + for (int i = 0; i < count.length; i++) { + if (count[i] != 0) pq.offer(new Node((char)(i + 'a'), count[i])); + } + + return pq; + } +} +``` \ No newline at end of file diff --git a/Java/Recover Binary Search Tree.java b/Java/Recover Binary Search Tree.java new file mode 100644 index 0000000..a969bce --- /dev/null +++ b/Java/Recover Binary Search Tree.java @@ -0,0 +1,97 @@ +H +1529560210 +tags: Tree, DFS, BST + +BST里面有2个node misplace, 要归为. 要求: O(1) extra space + +#### Observation +- BST inorder traversal should give small -> large sequence +- misplaced means: a **large**->small item would occur, and later a large>**small** would occur. +- The first large && second small item are the 2 candidates. Example +- [1, 5, 7, 10, 12, 15, 18] +- [1, 5, `15, 10`, `12, 7`, 18] + +#### dfs, O(1) extra space +- traverse, and take note of the candidate +- at the end, swap value of the 2 candidates + +#### O(n) space +- inorder traversal the nodes and save in array, find the 2 items misplanced and swap them +- But O(n) space should not be allowed + + +``` +/* +Two elements of a binary search tree (BST) are swapped by mistake. + +Recover the tree without changing its structure. + +Example 1: + +Input: [1,3,null,null,2] + + 1 + / + 3 + \ + 2 + +Output: [3,1,null,null,2] + + 3 + / + 1 + \ + 2 +Example 2: + +Input: [3,1,4,null,null,2] + + 3 + / \ +1 4 + / + 2 + +Output: [2,1,4,null,null,3] + + 2 + / \ +1 4 + / + 3 +Follow up: + +A solution using O(n) space is pretty straight forward. +Could you devise a constant space solution? + +*/ + +// DFS, track nodeA, nodeB. Beats 100%: https://leetcode.com/submissions/detail/159980654/ +class Solution { + TreeNode nodeA, nodeB, preNode = new TreeNode(Integer.MIN_VALUE); + public void recoverTree(TreeNode root) { + dfs(root); + + // swap + int temp = nodeA.val; + nodeA.val = nodeB.val; + nodeB.val = temp; + } + + /* + DFS function that in-order traverse the tree. + */ + private void dfs(TreeNode node) { + if (node == null) return; + dfs(node.left); + + // Assign nodeA, nodeB + if (nodeA == null && preNode.val >= node.val) nodeA = preNode; + if (nodeA != null && preNode.val >= node.val) nodeB = node; + preNode = node; // regardless, assign preNode as curr mid + + dfs(node.right); + } +} +``` \ No newline at end of file diff --git a/Java/Recover Rotated Sorted Array.java b/Java/Recover Rotated Sorted Array.java old mode 100644 new mode 100755 index 67030ac..ca23ded --- a/Java/Recover Rotated Sorted Array.java +++ b/Java/Recover Rotated Sorted Array.java @@ -1,3 +1,6 @@ +E +tags: Array + rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 Rotate三步: rotate前半 diff --git a/Java/Redundant Connection II.java b/Java/Redundant Connection II.java new file mode 100644 index 0000000..62401a5 --- /dev/null +++ b/Java/Redundant Connection II.java @@ -0,0 +1,90 @@ +H +1534232150 +tags: Tree, DFS, Union Find, Graph + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + +``` +/* +In this problem, a rooted tree is a directed graph such that, +there is exactly one node (the root) for which all other nodes are descendants of this node, +plus every node has exactly one parent, except for the root node which has no parents. + +The given input is a directed graph that started as a rooted tree with N nodes (with distinct values 1, 2, ..., N), +with one additional directed edge added. The added edge has two different vertices chosen from 1 to N, and was not an edge that already existed. + +The resulting graph is given as a 2D-array of edges. Each element of edges is a pair [u, v] that +represents a directed edge connecting nodes u and v, where u is a parent of child v. + +Return an edge that can be removed so that the resulting graph is a rooted tree of N nodes. +If there are multiple answers, return the answer that occurs last in the given 2D-array. + +Example 1: +Input: [[1,2], [1,3], [2,3]] +Output: [2,3] +Explanation: The given directed graph will be like this: + 1 + / \ +v v +2-->3 +Example 2: +Input: [[1,2], [2,3], [3,4], [4,1], [1,5]] +Output: [4,1] +Explanation: The given directed graph will be like this: +5 <- 1 -> 2 + ^ | + | v + 4 <- 3 +Note: +The size of the input 2D-array will be between 3 and 1000. +Every integer represented in the 2D-array will be between 1 and N, where N is the size of the input array. +*/ + +/* +- find inDegree == 2 +- union and return cycle item +- if cycle not exist, return the inDegree==2 edge + +*/ +class Solution { + int[] parent; + public int[] findRedundantDirectedConnection(int[][] edges) { + int n = edges.length; + parent = new int[n + 1]; + + // find the edges where inDegree == 2 + int[] first = null, second = null; + for (int[] edge : edges) { + int x = edge[0], y = edge[1]; + if (parent[y] == 0) { + parent[y] = x; + } else { + first = new int[]{parent[y], y}; + second = new int[]{edge[0], edge[1]}; + edge[1] = 0; // why? + } + } + // re-init unionFind + for (int i = 0; i <= n; i++) parent[i] = i; + + // Union + for (int[] edge : edges) { + int x = edge[0], y = edge[1]; + if (y == 0) continue; + int parentX = find(x), parentY = find(y); + if (parentX == parentY) return first == null ? edge : first; + parent[parentX] = parentY; + } + + return second; + } + + public int find(int x) { + int parentX = parent[x]; + if (parentX == x) return parentX; + return parent[x] = find(parentX); + } +} +``` \ No newline at end of file diff --git a/Java/Redundant Connection.java b/Java/Redundant Connection.java new file mode 100644 index 0000000..149fdaf --- /dev/null +++ b/Java/Redundant Connection.java @@ -0,0 +1,146 @@ +M +1534222102 +tags: Tree, Union Find, Graph, DFS, BFS + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + +``` +/* +In this problem, a tree is an undirected graph that is connected and has no cycles. + +The given input is a graph that started as a tree with N nodes (with distinct values 1, 2, ..., N), with one additional edge added. The added edge has two different vertices chosen from 1 to N, and was not an edge that already existed. + +The resulting graph is given as a 2D-array of edges. Each element of edges is a pair [u, v] with u < v, that represents an undirected edge connecting nodes u and v. + +Return an edge that can be removed so that the resulting graph is a tree of N nodes. If there are multiple answers, return the answer that occurs last in the given 2D-array. The answer edge [u, v] should be in the same format, with u < v. + +Example 1: +Input: [[1,2], [1,3], [2,3]] +Output: [2,3] +Explanation: The given undirected graph will be like this: + 1 + / \ +2 - 3 +Example 2: +Input: [[1,2], [2,3], [3,4], [1,4], [1,5]] +Output: [1,4] +Explanation: The given undirected graph will be like this: +5 - 1 - 2 + | | + 4 - 3 +Note: +The size of the input 2D-array will be between 3 and 1000. +Every integer represented in the 2D-array will be between 1 and N, where N is the size of the input array. + +Update (2017-09-26): +We have overhauled the problem description + test cases and specified clearly the graph is an undirected graph. For the directed graph follow up please see Redundant Connection II). We apologize for any inconvenience caused. + + +*/ + +/* +UnionFind +keyword: tree does not have cycle; if there is cycle, there must be a redundant edge that can be removed. +Goal: find that edge. +Simply write union-find methods. When parents of two nodes are the same, that means they have been visited and in same union: we can break this edge +*/ +class Solution { + int[] parent; + public int[] findRedundantConnection(int[][] edges) { + parent = new int[2001]; // at most 1000 edges, so at most 1000*2 + 1 nodes + // init unionFind + for (int i = 0; i < parent.length; i++) parent[i] = i; + + // Union + for (int[] edge : edges) { + int x = edge[0], y = edge[1]; + int parentX = find(x), parentY = find(y); + if (parentX == parentY) return edge; + parent[parentX] = parentY; + } + + return new int[] {-1, -1}; + } + + public int find(int x) { + int parentX = parent[x]; + if (parentX == x) return parentX; + return parent[x] = find(parentX); + } + +} + +// DFS +class Solution { + public int[] findRedundantConnection(int[][] edges) { + Map> graph = new HashMap<>(); + + for (int[] edge : edges) { + int x = edge[0], y = edge[1]; + if (graph.containsKey(x) && hasCycle(graph, x, y, -1)) return edge; + graph.putIfAbsent(x, new HashSet<>()); + graph.putIfAbsent(y, new HashSet<>()); + graph.get(x).add(y); + graph.get(y).add(x); + } + + return new int[] {-1, -1}; + } + + private boolean hasCycle(Map> graph, int curr, int target, int pre) { + if (graph.get(curr).contains(target)) return true; + for (int num : graph.get(curr)) { + if (num == pre) continue; + if (hasCycle(graph, num, target, curr)) return true; + } + return false; + } +} + +/* +BFS, same concept: keep adding into graph. +If item already added once, then redundant edge detected +*/ +class Solution { + public int[] findRedundantConnection(int[][] edges) { + Map> graph = new HashMap<>(); + + for (int[] edge : edges) { + int x = edge[0], y = edge[1]; + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + queue.offer(x); + + // BFS all possible nodes see if node y has been added + while (!queue.isEmpty()) { + int node = queue.poll(); + if (!graph.containsKey(node)) continue; + if (graph.get(node).contains(y)) return edge; + for (int num : graph.get(node)) { + if (visited.contains(num)) continue; + queue.offer(num); + visited.add(num); + } + } + + // passed test, add graph nodes + graph.putIfAbsent(x, new HashSet<>()); + graph.putIfAbsent(y, new HashSet<>()); + graph.get(x).add(y); + graph.get(y).add(x); + } + + return new int[] {-1, -1}; + } +} +``` \ No newline at end of file diff --git a/Java/Regular Expression Matching.java b/Java/Regular Expression Matching.java new file mode 100644 index 0000000..172413d --- /dev/null +++ b/Java/Regular Expression Matching.java @@ -0,0 +1,88 @@ +H +1534348524 +tags: String, DP, Sequence DP, Double Sequence DP, Backtracking + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + +``` +/* +Implement regular expression matching with support for '.' and '*'. + +'.' Matches any single character. +'*' Matches zero or more of the preceding element. + +The matching should cover the entire input string (not partial). + +The function prototype should be: +bool isMatch(const char *s, const char *p) + +Some examples: +isMatch("aa","a") → false +isMatch("aa","aa") → true +isMatch("aaa","aa") → false +isMatch("aa", "a*") → true +isMatch("aa", ".*") → true +isMatch("ab", ".*") → true +isMatch("aab", "c*a*b") → true +*/ + +/* +Thoughts: +Two sequence, DP, find if possible to match. +The '*' takes effect of preceding element, so we can start matching from end. +DP[i][j]: is it possible to match s[0 ~ i - 1] and p[0 ~ j - 1]. +Check last index of s and p, there can be a few possibilities: +1. s[i-1]==p[j-1] and they are normal characters => && dp[i - 1][j - 1]; +2. p[j-1] == '.', match => dp[i - 1][j - 1] +3. p[j-1] == '*': + a. ignore a* => |= dp[i][j - 2]; + b. use a* => |= dp[i - 1][j]; + +init: +dp[0][j] and dp[i][0] will all be false since there can't be any match. + +*/ +class Solution { + public boolean isMatch(String s, String p) { + if (s == null || p == null) return false; + int m = s.length(), n = p.length(); + boolean[][] dp = new boolean[m + 1][n + 1]; + char[] ss = s.toCharArray(); + char[] pp = p.toCharArray(); + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (i == 0 && j == 0) { + dp[i][j] = true; + continue; + } + if (j == 0) { // When p is empty but s is not empty, should not match + dp[i][j] = false; + continue; + } + + // j >= 1 + dp[i][j] = false; + if (pp[j - 1] != '*') { + if (i >= 1 && (ss[i - 1] == pp[j - 1] || pp[j - 1] == '.')) { + dp[i][j] = dp[i - 1][j - 1]; + } + } else { // tail = '*'. ex: a* + if (j >= 2 ) { // ignore a*, repeat 0 times + dp[i][j] |= dp[i][j - 2]; + } + // repeat the char befeore * for 1 time, so ss[i-1] should match pp[j-2] or pp[j-2] == '.' + if (j >= 2 && i >= 1 && (ss[i - 1] == pp[j - 2] || pp[j - 2] == '.')) { + dp[i][j] |= dp[i - 1][j]; + } + } + } + } + return dp[m][n]; + } +} +``` \ No newline at end of file diff --git a/Java/Rehashing.java b/Java/Rehashing.java old mode 100644 new mode 100755 index 37f69b4..24c6c66 --- a/Java/Rehashing.java +++ b/Java/Rehashing.java @@ -1,5 +1,21 @@ +M +1528088505 +tags: Hash Table + +给一个Hash Table, 是用 linked list 做的. 问题是: capacity太小, collision太多的情况下, 需要double capacity 然后rehash. + +#### Hash Table +- 明白hashCode() function的意义: 拿到hashKey的时候, 用hashKey%capacity 来做hash code +- hashcode就是hash map里面的index +- 明白collision handling 的方式, 和如何double capacity而rehashing +- 都是基本操作, 概念实现 + +``` /* -The size of the hash table is not determinate at the very beginning. If the total size of keys is too large (e.g. size >= capacity / 10), we should double the size of the hash table and rehash every keys. Say you have a hash table looks like below: +The size of the hash table is not determinate at the very beginning. +If the total size of keys is too large (e.g. size >= capacity / 10), +we should double the size of the hash table and rehash every keys. +Say you have a hash table looks like below: size=3, capacity=4 @@ -13,7 +29,9 @@ The size of the hash table is not determinate at the very beginning. If the tota int hashcode(int key, int capacity) { return key % capacity; } -here we have three numbers, 9, 14 and 21, where 21 and 9 share the same position as they all have the same hashcode 1 (21 % 4 = 9 % 4 = 1). We store them in the hash table by linked list. +here we have three numbers, 9, 14 and 21, where 21 and 9 share the same position +as they all have the same hashcode 1 (21 % 4 = 9 % 4 = 1). +We store them in the hash table by linked list. rehashing this hash table, double the capacity, you will get: @@ -31,7 +49,8 @@ int hashcode(int key, int capacity) { Note For negative integer in hash table, the position can be calculated as follow: -C++/Java: if you directly calculate -4 % 3 you will get -1. You can use function: a % b = (a % b + b) % b to make it is a non negative integer. +C++/Java: if you directly calculate -4 % 3 you will get -1. +You can use function: a % b = (a % b + b) % b to make it is a non negative integer. Python: you can directly use -1 % 3, you will get 2 automatically. Tags Expand LintCode Copyright Hash Table @@ -53,74 +72,48 @@ int hashcode(int key, int capacity) { * } * } */ - public class Solution { + +public class Solution { /** * @param hashTable: A list of The first node of linked list * @return: A list of The first node of linked list which have twice size */ public ListNode[] rehashing(ListNode[] hashTable) { - if (hashTable == null || hashTable.length == 0) { - return hashTable; - } - //Find longest size - /* - int longest = 0; - for (int i = 0; i < hashTable.length; i++) { - ListNode node = hashTable[i]; - int count = 0; - while (node != null) { - count++; - node = node.next; - } - longest = Math.max(longest, count); - }*/ - //Calculate new capacity - //Just to clarify, this problem asks to double the hashtable size, rather than 'longest' times longer. - int capacity = hashTable.length * 2; - if (capacity == hashTable.length) { - return hashTable; - } - - ListNode[] rst = new ListNode[capacity]; - for (int i = 0; i < hashTable.length; i++) { - ListNode node = hashTable[i]; - while (node != null) { - ListNode newNode = new ListNode(node.val); - int hCode = hashcode(newNode.val, capacity); - if (rst[hCode] == null) { - rst[hCode] = newNode; - } else { - ListNode move = rst[hCode]; - while (move.next != null) { - move = move.next; - } - move.next = newNode; - } - node = node.next; - } - } - - return rst; + if (hashTable == null || hashTable.length == 0) { + return hashTable; + } + + //Calculate new capacity, double the hashtable size + int capacity = hashTable.length * 2; + + ListNode[] rst = new ListNode[capacity]; + for (int i = 0; i < hashTable.length; i++) { + ListNode node = hashTable[i]; // process one hashkey (a linked list) + while (node != null) { + ListNode newNode = new ListNode(node.val); + int hCode = hashcode(newNode.val, capacity); + if (rst[hCode] == null) { + rst[hCode] = newNode; + } else { + ListNode collisionNode = rst[hCode]; + while (collisionNode.next != null) { + collisionNode = collisionNode.next; + } + collisionNode.next = newNode; + } + node = node.next; + } + } + + return rst; } public int hashcode(int key, int capacity) { - if (key < 0) { - return (key % capacity + capacity) % capacity; - } else { - return key % capacity; - } + if (key < 0) { + return (key % capacity + capacity) % capacity; + } else { + return key % capacity; + } } }; - - - - - - - - - - - - - +``` \ No newline at end of file diff --git a/Java/Remove Duplicate Letters.java b/Java/Remove Duplicate Letters.java new file mode 100644 index 0000000..9b663db --- /dev/null +++ b/Java/Remove Duplicate Letters.java @@ -0,0 +1,118 @@ +H +1528267312 +tags: Stack, Greedy, Hash Table + +#### Hash Table, Greedy +- count[] = int[256], 不需要 `c-'a'` +- boolean visited[]: 一旦一个字母固定了位置后, 再次遇到时候, 直接跳过用过的character +- 如果tail字母可以变小, 那就delete掉tail, 重新接上新字母 (前提条件: 去掉的字母后面还会再出现, set visited[tail] = false) +- Space: O(1) count[], visited[]. +- Time: Go through all letters O(n) + +#### Stack +- Use stack instead of stringBuffer: keep append/remove last added item +- However, stringBuffer appears to be faster than stack. + +``` +/* +Given a string which contains only lowercase letters, +remove duplicate letters so that every letter appear once and only once. +You must make sure your result is the smallest in lexicographical order among all possible results. + +Example 1: + +Input: "bcabc" +Output: "abc" +Example 2: + +Input: "cbacdcbc" +Output: "acdb" + +*/ + +/* +Thoughts: +- Count # of chars in int[26]. +- try all letters of string, and add to stringBuffer sb +- have while loop to remove tail letter from sb, if the curr letter is more suitable && tail letter has more occurance in the future +*/ + +class Solution { + String removeDuplicateLetters(String s) { + // check edge case + if (s == null || s.length() <= 1) { + return s; + } + char[] arr = s.toCharArray(); + // build count array + int[] count = new int[256]; + boolean[] visited = new boolean[256]; + for (char c : arr) { + count[c]++; + } + + // loop over s and aggregate; + StringBuffer sb = new StringBuffer("0"); + for (char c : arr) { + count[c]--; + if (visited[c]) { + continue; + } + char lastChar = sb.charAt(sb.length() - 1); + // Compare with tail; reduce tail letter if necessary && applicable + while (c < lastChar && count[lastChar] > 0) { + sb.deleteCharAt(sb.length() - 1); + visited[lastChar] = false; + lastChar = sb.charAt(sb.length() - 1); + } + sb.append((char)(c)); + visited[c] = true; + } + + return sb.substring(1).toString(); + } + +} + + + +// Using stack over StringBuffer +class Solution { + String removeDuplicateLetters(String s) { + // check edge case + if (s == null || s.length() <= 1) { + return s; + } + char[] arr = s.toCharArray(); + // build count array + int[] count = new int[256]; + boolean[] visited = new boolean[256]; + for (char c : arr) { + count[c]++; + } + + // loop over s and aggregate; + Stack stack = new Stack<>(); + stack.push('0'); + for (char c : arr) { + count[c]--; + if (visited[c]) { + continue; + } + // Compare with tail; reduce tail letter if necessary && applicable + while (c < stack.peek() && count[stack.peek()] > 0) { + visited[stack.pop()] = false; + } + stack.push(c); + visited[c] = true; + } + + StringBuffer sb = new StringBuffer(); + while (!stack.isEmpty()) { + sb.insert(0, stack.pop()); + } + return sb.substring(1).toString(); + } + +} +``` \ No newline at end of file diff --git a/Java/Remove Duplicates from Sorted Array II.java b/Java/Remove Duplicates from Sorted Array II.java new file mode 100644 index 0000000..023c9b7 --- /dev/null +++ b/Java/Remove Duplicates from Sorted Array II.java @@ -0,0 +1,75 @@ +M +1526348390 +tags: Array, Two Pointers + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- 跟 `Remove Duplicates from Sorted Array` 几乎一模一样, 只不过unique index现在可以 validate 2 位 +- 其余一模一样, use index to track unique item; skip if duplicated for more than 2 times +- O(n) time, O(1) space +- 这里也可以真的用2个pointers 写while loop, 但是没有必要, 只是单纯地走一个for loop其实就足够. + +``` +/* +Given a sorted array nums, remove the duplicates in-place such that +duplicates appeared at most twice and return the new length. + +Do not allocate extra space for another array, +you must do this by modifying the input array in-place with O(1) extra memory. + +Example 1: + +Given nums = [1,1,1,2,2,3], + +Your function should return length = 5, +with the first five elements of nums being 1, 1, 2, 2 and 3 respectively. + +It doesn't matter what you leave beyond the returned length. +Example 2: + +Given nums = [0,0,1,1,1,1,2,3,3], + +Your function should return length = 7, with the first seven elements of nums +being modified to 0, 0, 1, 1, 2, 3 and 3 respectively. + +It doesn't matter what values are set beyond the returned length. +Clarification: + +Confused why the returned value is an integer but your answer is an array? + +Note that the input array is passed in by reference, which means modification +to the input array will be known to the caller as well. + +Internally you can think of this: + +// nums is passed in by reference. (i.e., without making a copy) +int len = removeDuplicates(nums); + +// any modification to nums in your function would be known by the caller. +// using the length returned by your function, it prints the first len elements. +for (int i = 0; i < len; i++) { + print(nums[i]); +} +*/ + +class Solution { + public int removeDuplicates(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + + int index = 1; + for (int i = 2; i < nums.length; i++) { + if (nums[i] != nums[index] || (nums[i] == nums[index] && nums[i] != nums[index - 1])) { + index++; + nums[index] = nums[i]; + } + } + return index + 1; + } +} +``` \ No newline at end of file diff --git a/Java/Remove Duplicates from Sorted Array.java b/Java/Remove Duplicates from Sorted Array.java old mode 100644 new mode 100755 index 48c960c..8cddc90 --- a/Java/Remove Duplicates from Sorted Array.java +++ b/Java/Remove Duplicates from Sorted Array.java @@ -1,17 +1,118 @@ -Remove Duplicate from Array 不同于remove from linked list. +E +1526347200 +tags: Array, Two Pointers -LinkedList里面我们是最好不要动node.val的,直接把node去掉。 -而array我们很难直接把node去掉,又不能用新array,那么就要: +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. -把不重复的element一个个放到最前面。 +return unique item 的长度. +#### Two Pointers +- sorted array, 重复元素都在一起 +- Two pointers 其实也可以是一个 for loop pointer, 另一个 dynamic variable. +- track unique index +- skip duplicated items +- O(n) -这个思想跟merge two sorted array (其中一个后续非常长的array可以放下arr1,arr2) 类似。 -就是找个不会事后mess up,不会去动得index,把满足条件的element 填进去。这样保证了in place. - -* 有个反向思维:remove duplicate,实际上也是找unique elements, and insert into original array +#### 思考模式: +- Remove Duplicate from Array 不同于remove from linked list. +- LinkedList里面我们是最好不要动node.val的,直接把node去掉。 +- 而array我们很难直接把node去掉,又不能用新array,那么就要: +- 把不重复的element一个个放到最前面。 +- 这个思想跟merge two sorted array (其中一个后续非常长的array可以放下arr1,arr2) 类似。 +- 就是找个不会事后mess up,不会去动得index,把满足条件的element 填进去。这样保证了in place. +- *反向思维*:remove duplicate, 实际上也是找unique elements, and insert into original array ``` +/* +LeetCode + +Given a sorted array nums, remove the duplicates in-place such that +each element appear only once and return the new length. + +Do not allocate extra space for another array, you must do this by modifying +the input array in-place with O(1) extra memory. + +Example 1: + +Given nums = [1,1,2], + +Your function should return length = 2, with the first two elements of nums being 1 and 2 respectively. + +It doesn't matter what you leave beyond the returned length. +Example 2: + +Given nums = [0,0,1,1,1,2,2,3,3,4], + +Your function should return length = 5, +with the first five elements of nums being modified to 0, 1, 2, 3, and 4 respectively. + +It doesn't matter what values are set beyond the returned length. +Clarification: + +Confused why the returned value is an integer but your answer is an array? + +Note that the input array is passed in by reference, +which means modification to the input array will be known to the caller as well. + +Internally you can think of this: + +// nums is passed in by reference. (i.e., without making a copy) +int len = removeDuplicates(nums); + +// any modification to nums in your function would be known by the caller. +// using the length returned by your function, it prints the first len elements. +for (int i = 0; i < len; i++) { + print(nums[i]); +} + +*/ + +/** +thoughts: +1. track unique index +2. skip any repeated item and record any new item. +O(n) time, O(1) space +*/ +class Solution { + public int removeDuplicates(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int index = 0; + for (int i = 1; i < nums.length; i++) { + if (nums[i] != nums[index]) { + nums[index + 1] = nums[i]; + index++; + } + } + return index + 1; + } +} + +class Solution { + public int removeDuplicates(int[] nums) { + if (nums == null) { + return 0; + } + if (nums.length <= 1) { + return nums.length; + } + int currPos = 0; + int movingPos = 1; + while (movingPos < nums.length) { + while(movingPos < nums.length && nums[currPos] == nums[movingPos]) { + movingPos++; + } + if (movingPos < nums.length && nums[currPos] != nums[movingPos]) { + nums[currPos + 1] = nums[movingPos]; + currPos++; + movingPos++; + } + } + return currPos + 1; + } +} + /*31% Accepted Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length. @@ -56,5 +157,26 @@ public int removeDuplicates(int[] nums) { } } +// Better vairable naming. +class Solution { + public int removeDuplicates(int[] nums) { + if (nums == null) { + return 0; + } + if (nums.length <= 1) { + return nums.length; + } + int currPos = 0; + int movingPos; + for (movingPos = 1; movingPos < nums.length; movingPos++) { + if (nums[currPos] != nums[movingPos]) { + nums[currPos + 1] = nums[movingPos]; + currPos++; + } + } + return currPos + 1; + } +} + ``` \ No newline at end of file diff --git a/Java/Remove Duplicates from Sorted List II.java b/Java/Remove Duplicates from Sorted List II.java old mode 100644 new mode 100755 index 5da207e..8fee7b9 --- a/Java/Remove Duplicates from Sorted List II.java +++ b/Java/Remove Duplicates from Sorted List II.java @@ -1,5 +1,27 @@ -斩草除根。 -多个node,check node.next ?= node.next.next +M +1526353034 +tags: Linked List + +从Linked list 里面摘掉重复元素: 只要重复过, 全部都删掉; 重复出现过得元素一个不留. + +#### Linked List +- sorted list, 重复元素都在一起 +- 运用 dummyHead: 如果要去掉所有重复元素, 就要有个dummyHead作为局外人在开头牵线 +- 只要发现一个 node.val == node.next.val, 就记下这个duplicated val, move forward, 过掉所有重复过的元素 +- 思想: +- 用第二个 inner while loop, 把所有的重复元素都处理干净, 然后再move forward +- 优点: outter while loop 不需要考虑太多case, 在inner loop 都把主要的business logic 解决了. + +##### 注意DummyHead 的使用 +- 当我们有了DummyHead 作为Linked List 的局外线头, 其实可以选择每次遇到duplicate, 就把更加后面的元素 强行assign 给 dummyHead.next +- 下面还尝试过一种做法: 但是需要考虑的edge case 太多了: 不断移动node, 知道不重复, assign prev.next = node. +- 这样的做法比较直白, 但是需要考虑很多edge case, 而且并没有很好利用到 dummy head, 注意规避. + +##### Previous Note +- 斩草除根。 +- 多个node,check node.next ?= node.next.next + + ``` /* 26% Accepted @@ -10,15 +32,6 @@ Given 1->2->3->3->4->4->5, return 1->2->5. Given 1->1->1->2->3, return 2->3. -Tags Expand -Linked List - -Thinking process: -Create a dummyHead -User a pointer node to run through the list -Similar to Remove Duplicates from Sorted List I, but checking next and next.next -If there is a match on head.next && head.next.next, delete head.next node and link to next ones until a different node appear -return dummyHead.next */ @@ -33,22 +46,29 @@ * } * } */ + +/** + +Thgouths: +- Create a dummyHead +- User a pointer node to run through the list +- Similar to Remove Duplicates from Sorted List I, but checking next and next.next +- If there is a match on head.next && head.next.next, delete head.next node and link to next ones until a different node appear +- return dummyHead.next + */ public class Solution { - /** - * @param ListNode head is the head of the linked list - * @return: ListNode head of the linked list - */ - public static ListNode deleteDuplicates(ListNode head) { + public ListNode deleteDuplicates(ListNode head) { if (head == null) { return head; } ListNode dummyHead = new ListNode(0); dummyHead.next = head; - ListNode node = dummyHead; + ListNode node = dummyHead; // node is the leading dummy head, node.next is the head while (node.next != null && node.next.next != null) { if (node.next.val == node.next.next.val) { int duplicatedVal = node.next.val; + // When node.next.val == duplicatedVal, remove all of them, until a new unique node appears. while (node.next != null && node.next.val == duplicatedVal) { node.next = node.next.next; } @@ -62,4 +82,54 @@ public static ListNode deleteDuplicates(ListNode head) { } +/** +直白做法, 太繁琐, 许多edgecase, 没有好好利用dummy head, 不可取. + */ +class Solution { + public ListNode deleteDuplicates(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode prev = new ListNode(-1); + ListNode dummy = prev; + ListNode node = head; + int val = node.next.val; + while (node != null) { + // Skip duplicate until a new element + while (node != null && node.val == val) { + node = node.next; + } + + if (node != null) { + // Delect if new node is also duplicate, if so, move forward + if (node.next != null && node.val == node.next.val) { + val = node.val; + node = node.next; + } else { + // Attach new node, and move forward + prev.next = node; + prev = prev.next; + node = node.next; + // Re-assign val = node.next.val. + if (node != null && node.next != null) { + val = node.next.val; + } else { + // Reach end of list, end loop + prev.next = node; + prev = prev.next; + break; + } + } + } + } + + // Prev.next always catches the last node, need to clean up the tail + if (prev != null) { + prev.next = null; + } + + return dummy.next; + } +} + ``` \ No newline at end of file diff --git a/Java/Remove Duplicates from Sorted List.java b/Java/Remove Duplicates from Sorted List.java old mode 100644 new mode 100755 index 5815642..1bd043b --- a/Java/Remove Duplicates from Sorted List.java +++ b/Java/Remove Duplicates from Sorted List.java @@ -1,24 +1,35 @@ -一旦node.next 和node是重复,跳 +E +1526349154 +tags: Linked List + +从Linked list 里面摘掉重复元素, 只留下unique元素. + +#### Linked List +- sorted list, 重复元素都在一起 +- 知道如何构建Linked List. +- 一点遇到重复元素: node.val == node.next.val, 就去掉. +- 用一个dummy node 来跑路 +- 注意: +- 只有当没有重复的时候, 才node = node.next; +- 有重复的时候, 当后面第三个元素被提上来之后, 还是可能跟当下元素重复, 所以不能前移node. +- ex: A -> A -> A +- while loop 里面check node 和 node.next 比较好, 这样ending position会非常清晰 + ``` /* -40% 通过 Given a sorted linked list, delete all duplicates such that each element appear only once. -样例 -Given 1->1->2, return 1->2. -Given 1->1->2->3->3, return 1->2->3. +Example 1: -标签 Expand -Linked List +Input: 1->1->2 +Output: 1->2 +Example 2: -Thinking process: -check head null -Use dummy node to reserve head -while everything when head.next is not null -compare head.val == head.next.val? -If so, head.next = head.next.next +Input: 1->1->2->3->3 +Output: 1->2->3 */ + /** * Definition for ListNode * public class ListNode { @@ -30,6 +41,53 @@ * } * } */ +class Solution { + public ListNode deleteDuplicates(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode node = head; + while (node != null && node.next != null) { + if (node.val == node.next.val) { + node.next = node.next.next; + } else { + node = node.next; + } + } + return head; + } +} + +/** + +Thinking process: +check head null +Use dummy node to reserve head +while everything when head.next is not null +compare head.val == head.next.val? +If so, head.next = head.next.next + */ +/* +Thoughts: +1. Check head. +2. Remove next if next != null && next.val == node.val +3. Use node to move +*/ +class Solution { + public ListNode deleteDuplicates(ListNode head) { + if (head == null) return head; + + ListNode node = head; + while (node != null) { + while (node.next != null && node.val == node.next.val) { + node.next = node.next.next; + } + node = node.next; + } + return head; + } +} + public class Solution { /** * @param ListNode head is the head of the linked list diff --git a/Java/Remove Duplicates from Unsorted List.java b/Java/Remove Duplicates from Unsorted List.java old mode 100644 new mode 100755 index 1600519..b761b33 --- a/Java/Remove Duplicates from Unsorted List.java +++ b/Java/Remove Duplicates from Unsorted List.java @@ -1,3 +1,6 @@ +M +tags: Linked List + 基本方法: O(n) sapce, time 遍历。 遇到duplicate(可能多个), while直到node.next不是duplicate. diff --git a/Java/Remove Invalid Parentheses.java b/Java/Remove Invalid Parentheses.java new file mode 100644 index 0000000..9729141 --- /dev/null +++ b/Java/Remove Invalid Parentheses.java @@ -0,0 +1,133 @@ +R +1530244778 +tags: DFS, BFS, DP + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- in dfs: remove the incorrect parentheses one at a time +- detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` +- once detected, remove the char from middle of s, and dfs on the rest of the s that has not been tested yet. + +##### Core concept: reverse test +- `if a parenthese string is valid, the reverse of it should also be valid` +- Test s with open='(', close=')' first; **reverse s**, and test it with open=')', close='(' + +##### Minor details +- only procceed to remove invalid parenthese when `count<0`, and also break && return dfs after the recursive calls. +- The above 2 facts eliminates all the redundant results. +- Reverse string before alternating open and close parentheses, so when returning final result, it will return the correct order. +- Open questions: how does it guarantee minimum removals? + +##### Backtracking +- 如果用stringbuffer, 那么久不会每次create new string, 但是需要maintain这个string buffer, 就会backtracking + +##### Complexity +- Seems to be O(n), but need to derive + +#### BFS +TODO + +#### DP + +``` +/* +Remove the minimum number of invalid parentheses in order to make the input string valid. +Return all possible results. + +Note: The input string may contain letters other than the parentheses ( and ). + +Examples: +"()())()" -> ["()()()", "(())()"] +"(a)())()" -> ["(a)()()", "(a())()"] +")(" -> [""] +*/ + +// DFS, 98% +class Solution { + public List removeInvalidParentheses(String s) { + List rst = new ArrayList<>(); + if (s == null || s.length() == 0) { + rst.add(s); + return rst; + } + + dfs(rst, s, 0, 0, '(', ')'); + return rst; + } + + private void dfs(List rst, String s, int x, int y, char open, char close) { + // for loop start from i to validate all chars + for (int count = 0, i = x; i < s.length(); i++) { + if (s.charAt(i) == open) count++; + if (s.charAt(i) == close) count--; + if (count < 0) { + // remove char if invalid: try all candidates from [j ~ i], but skip consecutive close parenthese + for (int j = y; j <= i; j++) { + if (s.charAt(j) == close && (j == y || s.charAt(j - 1) != close)) { + dfs(rst, s.substring(0, j) + s.substring(j+1), i, j, open, close); + } + } + return; + } + } + // reverse s, and reverse open/close, call dfs + String reverse = new StringBuffer(s).reverse().toString(); + if (open == '(') { + dfs(rst, reverse, 0, 0, close, open); + } else { // return result if all validations passed + rst.add(reverse); + } + } +} + +// DFS, StringBuffer, a bit more concise, 98% +class Solution { + public List removeInvalidParentheses(String s) { + List rst = new ArrayList<>(); + if (validateInput(rst, s)) return rst; + + dfs(rst, new StringBuffer(s), 0, 0, '(', ')'); + return rst; + } + + private void dfs(List rst, StringBuffer sb, int x, int y, char open, char close) { + // for loop start from i to validate all chars + for (int count = 0, i = x; i < sb.length(); i++) { + if (sb.charAt(i) == open) count++; + if (sb.charAt(i) == close) count--; + if (count >=0) continue; + // remove char if invalid: try all candidates from [j ~ i], but skip consecutive close parenthese + for (int j = y; j <= i; j++) { + if (sb.charAt(j) == close && (j == y || sb.charAt(j - 1) != close)) { + dfs(rst, sb.deleteCharAt(j), i, j, open, close); + sb.insert(j, close); + } + } + return; + } + // reverse s, and reverse open/close, call dfs + sb.reverse(); + if (open == '(') { + dfs(rst, sb, 0, 0, close, open); + } else { // return result if all validations passed + rst.add(sb.toString()); + } + sb.reverse(); + } + + private boolean validateInput(List rst, String s) { + if (s == null) return true; + if (s.length() == 0) { + rst.add(""); + return true; + } + return false; + } +} + + +// BFS: http://www.cnblogs.com/grandyang/p/4944875.html +``` \ No newline at end of file diff --git a/Java/Remove Linked List Elements.java b/Java/Remove Linked List Elements.java old mode 100644 new mode 100755 index b5aaad8..afc54bf --- a/Java/Remove Linked List Elements.java +++ b/Java/Remove Linked List Elements.java @@ -1,5 +1,13 @@ -如果match. parent.next = node.next. -如果不match, parent 和 node 一起移动 +E +1525415837 +tags: Linked List + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + ``` /* Remove all elements from a linked list of integers that have value val. @@ -17,37 +25,24 @@ While loop through. Maintain a parent, so it can be used to skip current node. */ - -/** - * Definition for singly-linked list. - * public class ListNode { - * int val; - * ListNode next; - * ListNode(int x) { val = x; } - * } - */ -public class Solution { - /** - * @param head a ListNode - * @param val an integer - * @return a ListNode - */ +class Solution { public ListNode removeElements(ListNode head, int val) { - if (head == null) { - return head; - } - ListNode parent = new ListNode(0); - parent.next = head; - ListNode dummy = parent; - while (head != null) { - if (head.val == val) { - parent.next = head.next; - } else { - parent = parent.next; - } - head = head.next; - } - return dummy.next; + if (head == null) { + return head; + } + ListNode dummy = new ListNode(-1); + dummy.next = head; + ListNode node = dummy; + while (head != null) { + if (head.val == val) { + node.next = head.next; + } else { + node = node.next; + } + head = head.next; + } + + return dummy.next; } } diff --git a/Java/Remove Node in Binary Search Tree.java b/Java/Remove Node in Binary Search Tree.java old mode 100644 new mode 100755 index bffbc98..ed31cfb --- a/Java/Remove Node in Binary Search Tree.java +++ b/Java/Remove Node in Binary Search Tree.java @@ -1,5 +1,16 @@ +H +tags: BST + +方法1: Brutle一点。找到target和target的parent. +把target remove时,把target的children nodes 重新排列组成新的BST: inorder traversal, build tree based on inorder traversal list. + +方法2: 分析规律,先找到target和parent, 然后根据性质,把target remove时,移动children nodes, 保证还是BST。 + +``` /* -Given a root of Binary Search Tree with unique value for each node. Remove the node with given value. If there is no such a node with given value in the binary search tree, do nothing. You should keep the tree still a binary search tree after removal. +Given a root of Binary Search Tree with unique value for each node. Remove the node with given value. +If there is no such a node with given value in the binary search tree, do nothing. +You should keep the tree still a binary search tree after removal. Example Given binary search tree: @@ -41,6 +52,101 @@ Tags Expand LintCode Copyright Binary Search Tree + +*/ + +//Recap, 2024.2016 +//Find target. Serialize (inorder) target's children. De-serialize the list, without target. +public class Solution { + public TreeNode removeNode(TreeNode root, int value) { + if (root == null) { + return root; + } + //Find target and target.parent + TreeNode parent = findTargetParent(root, value); + TreeNode target; + if (parent == null) { + return root; + } + if (parent.val == value) { + target = parent; + } else if (parent.left != null && parent.left.val == value) { + target = parent.left; + } else { + target = parent.right; + } + + + //Build inorder traversal based on target node. + ArrayList list = new ArrayList(); + inorderTraversal(target, list, target); + + //Build new subtree and attach to original tree + if (parent.val == value) { + return buildBFS(list, 0, list.size() - 1); + } + if (parent.left != null && parent.left.val == value) { + parent.left = buildBFS(list, 0, list.size() - 1); + } else { + parent.right = buildBFS(list, 0, list.size() - 1); + } + + return root; + } + + //Find and return target, where target.val == value + public TreeNode findTargetParent(TreeNode node, int value) { + if (node == null || node.val == value) { + return node; + } + + if (value < node.val) { + if (node.left != null && value == node.left.val) { + return node; + } + return findTargetParent(node.left, value); + } else { + if (node.right != null && value == node.right.val) { + return node; + } + return findTargetParent(node.right, value); + } + } + + //Traverse target's children and build a inorder list + public void inorderTraversal(TreeNode node, ArrayList rst, TreeNode skipNode) { + if (node.left != null) { + inorderTraversal(node.left, rst, skipNode); + } + if (node.val != skipNode.val) { + rst.add(new TreeNode(node.val)); + } + if (node.right != null) { + inorderTraversal(node.right, rst, skipNode); + } + } + + //Build tree based on given inorder-traversal list + public TreeNode buildBFS(ArrayList list, int start, int end) { + if (start < end) { + int mid = start + (end - start)/2; + TreeNode root = list.get(mid); + root.left = buildBFS(list, start, mid - 1); + root.right = buildBFS(list, mid + 1, end); + return root; + } + if (start == end) { + return list.get(start); + } + + return null; + } + +} + + + +/* Thoughts: We can think of a couple different cases: where is that target node to remove? It can be root, a child (a couple more situations) Note: before going futher, remember the technique to rip off parent node. In a binary tree, L > parent > R, so always find the L's right-most node and replace parent. @@ -56,55 +162,55 @@ */ public class Solution { public TreeNode removeNode(TreeNode root, int value) { - if (root == null || (root.left == null && root.right == null)) { - return null; - } - TreeNode dummy = new TreeNode(0);; - dummy.left = root; - //Find node - TreeNode parent = findTargetParent(dummy, root, value); - TreeNode child; - if (parent.left != null && parent.left.val == value) { - child = parent.left; - } else if (parent.right != null && parent.right.val == value) { - child = parent.right; - } else { - return dummy.left; - } - //Delete that node: - deleteTargetNode(parent, child); - return dummy.left; + if (root == null || (root.left == null && root.right == null)) { + return null; + } + TreeNode dummy = new TreeNode(0);; + dummy.left = root; + //Find node + TreeNode parent = findTargetParent(dummy, root, value); + TreeNode child; + if (parent.left != null && parent.left.val == value) { + child = parent.left; + } else if (parent.right != null && parent.right.val == value) { + child = parent.right; + } else { + return dummy.left; + } + //Delete that node: + deleteTargetNode(parent, child); + return dummy.left; } - //Find target node - public TreeNode findTargetParent(TreeNode parent, TreeNode node, int value){ - if (node == null || node.val == value) { - return parent; - } - - if (value < node.val) { - return findTargetParent(node, node.left, value); - } else { - return findTargetParent(node, node.right, value); - } + //Find target node + public TreeNode findTargetParent(TreeNode parent, TreeNode node, int value){ + if (node == null || node.val == value) { + return parent; + } + + if (value < node.val) { + return findTargetParent(node, node.left, value); + } else { + return findTargetParent(node, node.right, value); + } } //Delete node public void deleteTargetNode(TreeNode parent, TreeNode target) { - //Case1 + case2: (target.L == null && target.R == null) || (target.R == null && target.L != null) - if (target.right == null) { - if (parent.left == target) { - parent.left = target.left; - } else { - parent.right = target.left; - } - } else {//Case3: when target.right != null - TreeNode replaceNode = target.right; - TreeNode replaceParent = target; - while(replaceNode.left != null) { - replaceParent = replaceNode; - replaceNode = replaceNode.left; - } + //Case1 + case2: (target.L == null && target.R == null) || (target.R == null && target.L != null) + if (target.right == null) { + if (parent.left == target) { + parent.left = target.left; + } else { + parent.right = target.left; + } + } else {//Case3: when target.right != null + TreeNode replaceNode = target.right; + TreeNode replaceParent = target; + while(replaceNode.left != null) { + replaceParent = replaceNode; + replaceNode = replaceNode.left; + } //Remove replaceNode from replaceParent if (replaceParent.left == replaceNode) {//Usually it'll be replaceParent.left replaceParent.left = replaceNode.right; @@ -119,9 +225,9 @@ public void deleteTargetNode(TreeNode parent, TreeNode target) { parent.right = replaceNode; } - replaceNode.left = target.left; - replaceNode.right = target.right; - } + replaceNode.left = target.left; + replaceNode.right = target.right; + } } } @@ -133,3 +239,5 @@ public void deleteTargetNode(TreeNode parent, TreeNode target) { + +``` \ No newline at end of file diff --git a/Java/Remove Nth Node From End of List.java b/Java/Remove Nth Node From End of List.java old mode 100644 new mode 100755 index cc8a57a..53d88f9 --- a/Java/Remove Nth Node From End of List.java +++ b/Java/Remove Nth Node From End of List.java @@ -1,3 +1,11 @@ +M +1520009626 +tags: Linked List, Two Pointers + +O(n), one pace, no extra space +找到窗口, 然后平移, 最后pre 和 head之间 skip一个node就好. + +``` /* Given a linked list, remove the nth node from the end of list and return its head. @@ -13,50 +21,50 @@ Tags Expand Two Pointers Linked List -Thinking process: -Very similar to 'Nth to last node'. Except, have a pre pointer to keep track of the previous node of 'nth to last'. -Also have a dummy.next to store the beginning of the list; + */ /** - * Definition for ListNode. + * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; - * ListNode(int val) { - * this.val = val; - * this.next = null; - * } + * ListNode(int x) { val = x; } * } - */ -public class Solution { - /** - * @param head: The first node of linked list. - * @param n: An integer. - * @return: The head of linked list. - */ - ListNode removeNthFromEnd(ListNode head, int n) { - if (head == null || n < 0) { + */ +/* +Thoughts: +Find starting piont of the window, and keep moving forward, until candidate reach end. +*/ +class Solution { + public ListNode removeNthFromEnd(ListNode head, int n) { + if (head == null) { return null; } int count = 0; - ListNode dummy = new ListNode(0); + ListNode dummy = new ListNode(-1); ListNode pre = new ListNode(0); pre.next = head; dummy = pre; - ListNode node = head; - while (node != null && count < n) { - node = node.next; + ListNode endNode = head; // end node + // Establish window + while (count < n) { + endNode = endNode.next; count++; } - while (node != null) { - node = node.next; - head = head.next; + while (endNode != null) { + endNode = endNode.next; pre = pre.next; + head = head.next; } - pre.next = head.next; - return dummy.next; + pre.next = head.next;// skips node between pre and head + return dummy.next; } } +/* - +Previous Notes +Very similar to 'Nth to last node'. Except, have a pre pointer to keep track of the previous node of 'nth to last'. +Also have a dummy.next to store the beginning of the list; + */ +``` \ No newline at end of file diff --git a/Java/Reorder List.java b/Java/Reorder List.java old mode 100644 new mode 100755 index ada5af2..8a23a2e --- a/Java/Reorder List.java +++ b/Java/Reorder List.java @@ -1,18 +1,28 @@ +M +1528091290 +tags: Linked List + +给一个Linked list, reorder: 从head/tail 两个方向 向中间进发, re-order like: one node at a time, + +#### Linked List 功能大集合 +- reverse list, find mid of list, merge two list +- 先find mid, 然后把 mid.next reverse了, 最后merge 两段. +- 注意, 用完mid.next之后, 一定要 mid.next = null, 不然merge会出问题 + +``` + /* -24% 通过 Given a singly linked list L: L0→L1→…→Ln-1→Ln, reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… You must do this in-place without altering the nodes' values. +Example 1: +Given 1->2->3->4, reorder it to 1->4->2->3. +Example 2: -样例 -For example, -Given 1->2->3->4->null, reorder it to 1->4->2->3->null. - -标签 Expand -Linked List +Given 1->2->3->4->5, reorder it to 1->5->2->4->3. Thinking Process: Similar to sort list: @@ -20,53 +30,31 @@ reverse last section merge(head, mid) alternatively by using index % 2. Append whatever left from the 2 lists -Note: re-order in place, does not necessarily mean you can create any variable. As long as the variable is O(1), it should be fine. +Note: re-order in place, does not necessarily mean you can create any variable. +As long as the variable is O(1), it should be fine. */ -/** - * Definition for ListNode. + /** + * Definition for singly-linked list. * public class ListNode { * int val; * ListNode next; - * ListNode(int val) { - * this.val = val; - * this.next = null; - * } + * ListNode(int x) { val = x; } * } - */ + */ public class Solution { - private ListNode reverse(ListNode head) { - ListNode reversedList = null; - while (head != null) { - ListNode temp = head.next; - head.next = reversedList; - reversedList = head; - head = temp; + public void reorderList(ListNode head) { + if (head == null || head.next == null) { + return; } - return reversedList; - } - private void merge(ListNode head1, ListNode head2) { - ListNode dummy = new ListNode(0); - int index = 0; - while (head1 != null && head2 != null) { - if (index % 2 == 0) { - dummy.next = head1; - head1 = head1.next; - } else { - dummy.next = head2; - head2 = head2.next; - } - dummy = dummy.next; - index += 1; - } - if (head1 != null) { - dummy.next = head1; - } else if (head2 != null) { - dummy.next = head2; - } - } + ListNode mid = findMiddle(head); + ListNode tail = reverse(mid.next); + mid.next = null; + merge(head, tail); + } + private ListNode findMiddle(ListNode head) { ListNode slow = head; ListNode fast = head.next; @@ -77,17 +65,33 @@ private ListNode findMiddle(ListNode head) { return slow; } - - public void reorderList(ListNode head) { - if (head == null || head.next == null) { - return; + private ListNode reverse(ListNode head) { + ListNode reversedList = null; + while (head != null) { + ListNode temp = head.next; + head.next = reversedList; + reversedList = head; + head = temp; } - - ListNode mid = findMiddle(head); - ListNode tail = reverse(mid.next); - mid.next = null; - - merge(head, tail); + return reversedList; } -} + private void merge(ListNode headA, ListNode headB) { + ListNode dummy = new ListNode(0); + while (headA != null && headB != null) { + dummy.next = headA; + headA = headA.next; + dummy = dummy.next; + + dummy.next = headB; + headB = headB.next; + dummy = dummy.next; + } + if (headA != null) { + dummy.next = headA; + } else if (headB != null) { + dummy.next = headB; + } + } +} +``` \ No newline at end of file diff --git a/Java/Reshape the Matrix.java b/Java/Reshape the Matrix.java new file mode 100644 index 0000000..b39cad3 --- /dev/null +++ b/Java/Reshape the Matrix.java @@ -0,0 +1,71 @@ +E + +读例子理解题意. +理清counter case. Basic implementation + +``` +/* +In MATLAB, there is a very useful function called 'reshape', which can reshape a matrix into a new one with different size but keep its original data. + +You're given a matrix represented by a two-dimensional array, and two positive integers r and c representing the row number and column number of the wanted reshaped matrix, respectively. + +The reshaped matrix need to be filled with all the elements of the original matrix in the same row-traversing order as they were. + +If the 'reshape' operation with given parameters is possible and legal, output the new reshaped matrix; Otherwise, output the original matrix. + +Example 1: +Input: +nums = +[[1,2], + [3,4]] +r = 1, c = 4 +Output: +[[1,2,3,4]] +Explanation: +The row-traversing of nums is [1,2,3,4]. The new reshaped matrix is a 1 * 4 matrix, fill it row by row by using the previous list. +Example 2: +Input: +nums = +[[1,2], + [3,4]] +r = 2, c = 4 +Output: +[[1,2], + [3,4]] +Explanation: +There is no way to reshape a 2 * 2 matrix to a 2 * 4 matrix. So output the original matrix. +Note: +The height and width of the given matrix is in range [1, 100]. +The given r and c are all positive. +*/ + +/* +Thoughts: +1. Original row * col needs to equal with new r*c: total # of elements should stay the same. Otherwise, return original +2. If the total number of items cannot be shaped into evenly into r/c form, return original +3. Maintain the new row/col index outside of nums for loop. +*/ +class Solution { + public int[][] matrixReshape(int[][] nums, int r, int c) { + int totalElements = nums.length * nums[0].length; + if (totalElements != r * c || totalElements % r != 0) { + return nums; + } + final int[][] result = new int[r][c]; + int newR = 0; + int newC = 0; + for (int i = 0; i < nums.length; i++) { + for (int j = 0; j < nums[i].length; j++) { + result[newR][newC] = nums[i][j]; + newC++; + if (newC == c) { + newC = 0; + newR++; + } + } + } + return result; + } +} + +``` \ No newline at end of file diff --git a/Java/Restore IP Addresses.java b/Java/Restore IP Addresses.java new file mode 100755 index 0000000..26f951e --- /dev/null +++ b/Java/Restore IP Addresses.java @@ -0,0 +1,90 @@ +M +1528094586 +tags: String, Backtracking, DFS + +给一串数字, 检查是否是valid IP, 如果合理, 给出所有valid 的IP组合方式. + +#### Backtracking +- 递归的终点:list.zie() == 3, 解决最后一段 +- 递归在一个index上面, pass s.toCharArray() +- validate string要注意leading '0' +- 注意: 递归的时候可以用一个start/level/index来跑路 +- 但是尽量不要去改变Input source, 会变得非常confusing. +- note: code有点messy, 因为要考虑IP的valid情况 +- 那个'remainValid', 其实是一个对于remain substring的判断优化, 不成立的就不dfs了 + +``` +/* + +Given a string containing only digits, restore it by returning all possible valid IP address combinations. + +For example: +Given "25525511135", + +return ["255.255.11.135", "255.255.111.35"]. (Order does not matter) + +Hide Tags Backtracking String + +*/ + +public class Solution { + public List restoreIpAddresses(String s) { + List rst = new ArrayList(); + if (s == null || s.length() == 0) { + return rst; + } + if (s.length() < 4 || s.length() > 12) { + return rst; + } + List list = new ArrayList<>(); + helper(rst, list, 0, s.toCharArray()); + + return rst; + } + + public void helper(List rst, Listlist, int index, char[] arr) { + if (list.size() == 3) { + StringBuffer sb = new StringBuffer(); + for (int i = index; i < arr.length; i++) { + sb.append(arr[i]); + } + + if (!isValid(sb.toString())) { + return; + } + list.add(sb.toString()); + sb = new StringBuffer(); + for (String str: list) { + sb.append(str + "."); + } + rst.add(sb.substring(0, sb.length() - 1).toString()); + list.remove(list.size() - 1); + return; + } + //run for loop 3 times: one IP spot has at most 3 digits + int end = index + 3; + StringBuffer sb = new StringBuffer(); + for (int i = index; i < end && i < arr.length; i++) { + sb.append(arr[i]); + int remainLength = (arr.length - i - 1); + boolean remainValid = remainLength > 0 && remainLength <= (4 - list.size() - 1) * 3; + if (isValid(sb.toString()) && remainValid) { + list.add(sb.toString()); + helper(rst, list, i + 1, arr); + list.remove(list.size() - 1); + } + } + } + + //Valid the IP [0,255]; cannot start with 0 if it's not 0 + public boolean isValid(String str) { + if (str.charAt(0) == '0') { + return str.equals("0"); + } + int num = Integer.parseInt(str); + return num <= 255 && num >= 0; + } + +} + +``` \ No newline at end of file diff --git a/Java/Reverse Integer.java b/Java/Reverse Integer.java old mode 100644 new mode 100755 index 37c51bc..cd75226 --- a/Java/Reverse Integer.java +++ b/Java/Reverse Integer.java @@ -1,3 +1,71 @@ +E +1520830132 +tags: Math + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + +``` +/* +LeetCode +Given a 32-bit signed integer, reverse digits of an integer. + +Example 1: + +Input: 123 +Output: 321 +Example 2: + +Input: -123 +Output: -321 +Example 3: + +Input: 120 +Output: 21 +Note: +Assume we are dealing with an environment which could only hold integers within the 32-bit signed integer range. +For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows. + */ +class Solution { + public int reverse(int x) { + int result = 0; + + while (x != 0) { + int tail = x % 10; + int newResult = result * 10 + tail; + if ((newResult - tail) / 10 != result) return 0; // check overflow. If newResult overflow, it won't resolve back to result + result = newResult; + x = x / 10; + } + + return result; + } +} + +/* +Thoughts: reverse without extra O(n) space. +Time: O(n) +*/ +class Solution { + public int reverse(int x) { + long rst = 0; + while (x != 0) { + rst = rst * 10 + x % 10; + x /= 10; + if (rst > Integer.MAX_VALUE || rst < Integer.MIN_VALUE) { + return 0; + } + } + return (int) rst; + } +} + /* Reverse digits of an integer. Returns 0 when the reversed integer overflows (signed 32-bit integer). @@ -8,15 +76,42 @@ Tags Expand Integer +*/ +/* +Thoughts: +Approach1: +1. Need a long to store the 32-bit integer, since the reversed can be go over the limit. +2. Save to a string and switch the head/tail digits. + +Approach2: +Operate directly on the number and compute the final result using % +*/ +class Solution { + public int reverse(int x) { + long result = (long) x; + char[] arr = (Math.abs(result) + "").toCharArray(); + int n = arr.length; + for (int i = 0; i < n/2; i++) { + char temp = arr[i]; + arr[i] = arr[n - i - 1]; + arr[n - i - 1] = temp; + } + result = Long.parseLong(String.valueOf(arr)) * (x > 0 ? 1 : -1); + if (result > Integer.MAX_VALUE || result < Integer.MIN_VALUE) { + return 0; + } + return (int) result; + } +} + +/* Thoughts: 1. Use long to capture the result. If > Integer.MAX_VALUE,return 0; 2. Use string to reverse, the conver to long 3. use string builder to reverse string */ - - public class Solution { /** * @param n the integer to be reversed @@ -26,12 +121,14 @@ public int reverseInteger(int n) { long num = (long)n; int sign = n > 0 ? 1 : -1; String rst = new StringBuilder(Math.abs(num)+"").reverse().toString(); - num = Long.parseLong(rst) * sign; - - if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE) { - return 0; - } else { - return (int)num; - } + num = Long.parseLong(rst) * sign; + + if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE) { + return 0; + } else { + return (int)num; + } } } + +``` diff --git a/Java/Reverse Linked List II .java b/Java/Reverse Linked List II .java old mode 100644 new mode 100755 index 6a75f48..b8a4d53 --- a/Java/Reverse Linked List II .java +++ b/Java/Reverse Linked List II .java @@ -1,6 +1,21 @@ -遍历到M前, -存一下那个点, -从M开始, for loop, reverse [m~n]。 然后把三段链接在一起。 +M +1525661850 +tags: Linked List + +reverse 一个 linked list 中 [m ~ n] 的一部分. + +#### Reverse linked list +- 在基本的reverse linked list 上面 多了一层: 找到front node, 接下来的 [m ~ n] node 需要被reverse +- 只需要reverse中间的部分. +- Reverse的时候: 用一个dummyNode, 这道题里面, 其实就用 nodeFront, 那么 dummy.next 就是整个reversed list. + +##### 注意 +- 一定要Mark开头的那个mth node, 最后用它接上 剩下node tail. 不然后面的node会断掉 + +#### Previous notes +- 遍历到M前, +- 存一下那个点, +- 从M开始, for loop, reverse [m~n]。 然后把三段链接在一起。 ``` @@ -19,8 +34,58 @@ Tags Expand Linked List +*/ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +/* +Thoughts: +- Find last node before m +- reverse mth node because it will be the end of the reversed list +- reverse [m ~ n] nodes +- link the 3 parts: front, reversed list, and the tail + +*/ + +public class Solution { + public ListNode reverseBetween(ListNode head, int m, int n) { + if (head == null || m >= n) { + return head; + } + + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode node = dummy; + + for (int i = 1; i < m; i++) { + node = node.next; + } + ListNode nodeFront = node; + ListNode mNode = node.next; //This is the mth node and will be the tail of reversed list. Reserve it for post-linking + + node = node.next; + for (int i = m; i <= n; i++) { + ListNode temp = node.next; + node.next = nodeFront.next; + nodeFront.next = node; + node = temp; + } + // the previous mth node is now at tail, and should link the very next node in the list. + mNode.next = node; -Thinking process: + return dummy.next; + } +} + + + +/** +Previous notes: 0. Use dummy node to store head 1. Find mNode, store the front nodes 2. Reverse from mNode to nNode @@ -29,60 +94,41 @@ - reversedList = 1st item - postNode = 2nd item - store 3rd item in temp: temp = postNode.next -*/ +**/ + -/** - * Definition for ListNode - * public class ListNode { - * int val; - * ListNode next; - * ListNode(int x) { - * val = x; - * next = null; - * } - * } - */ public class Solution { - /** - * @param ListNode head is the head of the linked list - * @oaram m and n - * @return: The head of the reversed ListNode - */ public ListNode reverseBetween(ListNode head, int m, int n) { if (head == null || m >= n) { return head; } - ListNode dummyNode = new ListNode(0); - dummyNode.next = head; - head = dummyNode; - ListNode nodeFront = null; - + ListNode dummy = new ListNode(0); + dummy.next = head; + ListNode node = dummy; - for (int countM = 1; countM < m; countM++) { - if (head == null) { - return head; - } - head = head.next; + for (int i = 1; i < m; i++) { + node = node.next; } - nodeFront = head; - ListNode mNode = head.next; //Head is Mth node. Reserve it + ListNode nodeFront = node; + + ListNode mNode = node.next; //This isthe mth node. Reserve it for post-processing + node = mNode.next; // (m+1)th node ListNode reversedList = mNode; - ListNode postNode = mNode.next; + for (int countN = m; countN < n; countN++) { - ListNode temp = postNode.next; - postNode.next = reversedList; - reversedList = postNode; - postNode = temp; + ListNode temp = node.next; + node.next = reversedList; + reversedList = node; + node = temp; } - //List front, middle and end section + // List front and reversed list nodeFront.next = reversedList; - mNode.next = postNode; + // the current node is the tail + mNode.next = node; - return dummyNode.next; + return dummy.next; } } - - ``` \ No newline at end of file diff --git a/Java/Reverse Linked List.java b/Java/Reverse Linked List.java old mode 100644 new mode 100755 index fde66e4..375cc33 --- a/Java/Reverse Linked List.java +++ b/Java/Reverse Linked List.java @@ -1,7 +1,17 @@ +E +1525646182 +tags: Linked List + +#### Reverse List +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + +``` /* Reverse a linked list. -Have you met this question in a real interview? Yes Example For linked list 1->2->3, the reversed linked list is 3->2->1 @@ -12,49 +22,54 @@ Linked List Facebook Uber */ -public class Solution { - /** - * @param head: The head of linked list. - * @return: The new head of reversed linked list. - */ - public ListNode reverse(ListNode head) { +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +// This is cleaner, and save all reversed result in dummy.next +class Solution { + public ListNode reverseList(ListNode head) { if (head == null || head.next == null) { return head; } - - ListNode dummy = new ListNode(0); - + // dummy 保持不动, 不断把新的node insert到 dummy.next + ListNode dummy = new ListNode(-1); while (head != null) { - //Take head out - ListNode temp = head; - //Head moves on - head = head.next; - //Cut the dummy list, insert temp in front - temp.next = dummy.next; - dummy.next = temp; + ListNode temp = head.next; + head.next = dummy.next; + dummy.next = head; + head = temp; } - return dummy.next; } } -//This is a more easy to 'apply and go' version. -public class Solution { - /** - * @param head: The head of linked list. - * @return: The new head of reversed linked list. - */ - public ListNode reverse(ListNode head) { - if (head == null) { + +/** +Alternatively, we can start from using head as first node of the reversed list. +NOTE: have to mark the headNode, and mark headdNode.next = null at the end. +*/ +class Solution { + public ListNode reverseList(ListNode head) { + if (head == null || head.next == null) { return head; } - ListNode reversedList = null; + ListNode headNode = head; + ListNode reversedList = head; + head = head.next; while (head != null) { - ListNode cutOffPart = head.next; + ListNode temp = head.next; head.next = reversedList; reversedList = head; - head = cutOffPart; + head = temp; } + headNode.next = null; return reversedList; } -} \ No newline at end of file +} + +``` \ No newline at end of file diff --git a/Java/Reverse Pairs.java b/Java/Reverse Pairs.java new file mode 100644 index 0000000..b7952f3 --- /dev/null +++ b/Java/Reverse Pairs.java @@ -0,0 +1,83 @@ +M +1533052458 +tags: Divide and Conquer, Merge Sort, Binary Search Tree, Segment Tree, Binary Indexed Tree + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + +``` +/* +Given an array nums, we call (i, j) an important reverse pair if i < j and nums[i] > 2*nums[j]. + +You need to return the number of important reverse pairs in the given array. + +Example1: + +Input: [1,3,2,3,1] +Output: 2 +Example2: + +Input: [2,4,3,5,1] +Output: 3 +Note: +The length of the given array will not exceed 50,000. +All the numbers in the input array are in the range of 32-bit integer. + +*/ +class Solution { + public int reversePairs(int[] nums) { + if (nums == null || nums.length == 0) return 0; + return mergeSort(nums, 0, nums.length - 1); + } + + private int mergeSort(int[] nums, int start, int end) { + // check end point + if (start >= end) return 0; + + // divide and conquer + int mid = start + (end - start) / 2; + int count = mergeSort(nums, start, mid) + mergeSort(nums, mid + 1, end); + // count pair + for (int i = start, j = mid + 1; i <= mid; i++) { + while (j <= end && nums[i] / 2.0 > nums[j]) j++; + count += j - (mid + 1); + } + + Arrays.sort(nums, start, end + 1); + + return count; + } +} +``` \ No newline at end of file diff --git a/Java/Reverse String.java b/Java/Reverse String.java new file mode 100644 index 0000000..cb5fcd5 --- /dev/null +++ b/Java/Reverse String.java @@ -0,0 +1,36 @@ +E + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + +``` +/* +Write a function that takes a string as input and returns the string reversed. + +Example: +Given s = "hello", return "olleh". +*/ + +/* +Thoughts: +Obvious: new StringBuilder().reverse(). +Or, turn into charArray and reverse +*/ +class Solution { + public String reverseString(String s) { + if (s == null || s.length() <= 1) { + return s; + } + char[] arr = s.toCharArray(); + int length = arr.length; + for (int i = 0; i < length / 2; i++) { + char temp = arr[i]; + arr[i] = arr[length - i - 1]; + arr[length - i - 1] = temp; + } + return String.valueOf(arr); + + //return new StringBuilder(s).reverse().toString(); + } +} +``` \ No newline at end of file diff --git a/Java/Reverse Vowels of a String.java b/Java/Reverse Vowels of a String.java new file mode 100644 index 0000000..7a99250 --- /dev/null +++ b/Java/Reverse Vowels of a String.java @@ -0,0 +1,96 @@ +E +1516344636 +tags: Two Pointers, String + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i vowels = Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'); + final StringBuffer sb = new StringBuffer(s); + + int maxIndex = sb.length() - 1; + int i = 0; + int j = maxIndex; + while (i < j) { + while (i < maxIndex && !vowels.contains(sb.charAt(i))) i++; + while (j > 0 && !vowels.contains(sb.charAt(j))) j--; + + if (i < j && vowels.contains(sb.charAt(i)) && vowels.contains(sb.charAt(j))) { + char letter = sb.charAt(j); + sb.setCharAt(j, sb.charAt(i)); + sb.setCharAt(i, letter); + j--; + i++; + } + } + return sb.toString(); + } +} + + /* +Thoughts: +vowels: a e i o u A E I O U +HashMap, store the and put them back in reverse order. +*/ +class Solution { + + public String reverseVowels(String s) { + if (s == null || s.length() == 0) { + return s; + } + final List vowels = Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'); + final List matches = new ArrayList<>(); + for (int i = 0; i < s.length(); i++) { + if (vowels.contains(s.charAt(i))) { + matches.add(s.charAt(i)); + } + } + final StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s.length(); i++) { + char letter = s.charAt(i); + if (vowels.contains(letter)) { + int lastMatchIndex = matches.size() - 1; + sb.append(matches.get(lastMatchIndex)); + matches.remove(lastMatchIndex); + } else { + sb.append(letter); + } + } + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/Reverse Words in a String II.java b/Java/Reverse Words in a String II.java new file mode 100755 index 0000000..4289eb0 --- /dev/null +++ b/Java/Reverse Words in a String II.java @@ -0,0 +1,111 @@ +M +1528126762 +tags: String + +#### In-place reverse +- reverse用两回. 全局reverse。局部:遇到空格reverse +- 注意ending index: `i == str.length - 1`, 结尾点即使没有' '也要给reverse一下最后一个词 + + +``` +/* +Given an input string, reverse the string word by word. A word is defined as a sequence of non-space characters. + +The input string does not contain leading or trailing spaces and the words are always separated by a single space. + +For example, +Given s = "the sky is blue", +return "blue is sky the". + +Could you do it in-place without allocating extra space? + +Related problem: Rotate Array + +Hide Company Tags Amazon Microsoft +Hide Tags String +Hide Similar Problems (M) Reverse Words in a String (E) Rotate Array + + +*/ + +/* + //1. reverse all. 2. reverse local with 2 pointer. + //build reverse(start,end) +*/ + +public class Solution { + public void reverseWords(char[] str) { + if (str == null || str.length <= 1) { + return; + } + reverse(str, 0, str.length - 1); + int start = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == ' ') { + reverse(str, start, i - 1); + start = i + 1; + } else if (i == str.length - 1) { + reverse(str, start, i); + } + }//end for + } + + public void reverse(char[] s, int start, int end) { + while (start < end) { + char temp = s[start]; + s[start] = s[end]; + s[end] = temp; + start++; + end--; + } + } +} + + +/* +Thoughts: write an example: reverse the whole thing, then reverse each individual word, split by space. + +Note: becase we don't have space at end of the char[], so we will ignore last word. Remember to reverse that one. +*/ +public class Solution { + public void reverseWords(char[] s) { + if (s == null || s.length == 0) { + return; + } + int len = s.length; + //reverse whole + for (int i = 0; i < len / 2; i++) { + char temp = s[i]; + s[i] = s[len - 1 - i]; + s[len - 1 - i] = temp; + } + + //reverse partial + int start = 0; + int mid = 0; + int end = 0; + for (int i = 0; i < len; i++) { + if (s[i] == ' ') { + mid = start + (end - start) / 2; + for (int j = start; j <= mid; j++) { + char temp = s[j]; + s[j] = s[end - (j - start)]; + s[end - (j - start)] = temp; + } + start = i + 1; + } else { + end = i; + } + } + + //Process last word + mid = start + (end - start) / 2; + for (int j = start; j <= mid; j++) { + char temp = s[j]; + s[j] = s[end - (j - start)]; + s[end - (j - start)] = temp; + } + + } +} +``` \ No newline at end of file diff --git a/Java/Reverse Words in a String III.java b/Java/Reverse Words in a String III.java new file mode 100644 index 0000000..3d430f4 --- /dev/null +++ b/Java/Reverse Words in a String III.java @@ -0,0 +1,52 @@ +E +1528127124 +tags: String + +给一个String, 里面的Word被single space split开来, 目的是reverse里面所有的Word, 但preserve Word 和 space order. + +#### Reverse function +- Reverse Words in a String II 的降级版, 去掉第一个overall reverse就好了 + +``` +/* +Given a string, you need to reverse the order of characters +in each word within a sentence while still preserving whitespace and initial word order. + +Example 1: +Input: "Let's take LeetCode contest" +Output: "s'teL ekat edoCteeL tsetnoc" +Note: In the string, each word is separated by single space and there will not be any extra space in the string. + + +*/ + +class Solution { + public String reverseWords(String s) { + if (s == null || s.length() <= 1) { + return s; + } + char[] str = s.toCharArray(); + int start = 0; + for (int i = 0; i < str.length; i++) { + if (str[i] == ' ') { + reverse(str, start, i - 1); + start = i + 1; + } else if (i == str.length - 1) { + reverse(str, start, i); + } + }//end for + + return String.valueOf(str); + } + + public void reverse(char[] s, int start, int end) { + while (start < end) { + char temp = s[start]; + s[start] = s[end]; + s[end] = temp; + start++; + end--; + } + } +} +``` \ No newline at end of file diff --git a/Java/Reverse Words in a String.java b/Java/Reverse Words in a String.java old mode 100644 new mode 100755 index 934ef0b..d82c0de --- a/Java/Reverse Words in a String.java +++ b/Java/Reverse Words in a String.java @@ -1,37 +1,50 @@ -几种不同的方法flip: -坑: 1. 结尾不能有空格。 2. 注意,如果Input是 ‘ ’的话,split以后就啥也没有了。check split以后 length == 0 +M +1528126427 +tags: String + +#### Break by space, then flip +- 结尾不能有空格 +- trim() output +- 如果Input是 ""的话,split以后就啥也没有了 +- 另个题目Reverse Words in String (char[]) 可以in-place, 条件是char[]里面是没有首尾空格. +- Time, Space: O(n) + +#### Other methods +- flip entire string, then flip each individual string (代码有点多, 这道题犯不着) + ``` /* -23% Accepted Given an input string, reverse the string word by word. For example, Given s = "the sky is blue", return "blue is sky the". -Example -Clarification +Update (2015-02-12): +For C programmers: Try to solve it in-place in O(1) space. + +click to show clarification. + +Clarification: What constitutes a word? A sequence of non-space characters constitutes a word. - Could the input string contain leading or trailing spaces? Yes. However, your reversed string should not contain leading or trailing spaces. How about multiple spaces between two words? Reduce them to a single space in the reversed string. -Tags Expand -String -*/ - - +Hide Company Tags Bloomberg +Hide Tags String +Hide Similar Problems (M) Reverse Words in a String II +*/ /* - Thoughts:12.08.2015 + For simplicity of code, try the appending from behind. + Have multiple two other ways to do it: 1. flip all,then flip each individual word; 2. break into parts and append from end to beginning. - For simplicity of code, try the appending from behind. */ public class Solution { public String reverseWords(String s) { @@ -39,17 +52,14 @@ public String reverseWords(String s) { return s; } - String[] strs = s.split(" "); - if (strs.length == 0) { - return s; - } + String[] strs = s.split("\\s+"); StringBuffer sb = new StringBuffer(); - for (int i = strs.length - 1; i >= 0; i--) { - sb.append(strs[i] + " "); + for (String str : strs) { + sb.insert(0, str + " "); } - return sb.substring(0, sb.length() - 1).toString(); + return sb.toString().trim(); } } @@ -57,7 +67,7 @@ public String reverseWords(String s) { /* -Thinking Process: +Thinking Process: 1. Reverse it like reversing a int array 2. Use Split into arrays. 3. When reversing, make sure not empty string "" diff --git a/Java/Robot Room Cleaner.java b/Java/Robot Room Cleaner.java new file mode 100644 index 0000000..7e522b3 --- /dev/null +++ b/Java/Robot Room Cleaner.java @@ -0,0 +1,185 @@ +H +1534395770 +tags: DFS, Backtracking + +#### DFS +- Different from regular dfs to visit all, the robot move() function need to be called, backtrack needs to move() manually and backtracking path shold not be blocked by visited positions +- IMPORTANT: Mark on the way in using set, but `backtrack directly without re-check against set` +- Mark coordinate 'x@y' +- Backtrack: turn 2 times to revert, move 1 step, and turn 2 times to revert back. +- Direction has to be up, right, down, left. +- `int [] dx = {-1, 0, 1, 0};`, `int[] dy = {0, 1, 0, -1};` + +``` +/* +Given a robot cleaner in a room modeled as a grid. + +Each cell in the grid can be empty or blocked. + +The robot cleaner with 4 given APIs can move forward, turn left or turn right. Each turn it made is 90 degrees. + +When it tries to move into a blocked cell, its bumper sensor detects the obstacle and it stays on the current cell. + +Design an algorithm to clean the entire room using only the 4 given APIs shown below. + +interface Robot { + // returns true if next cell is open and robot moves into the cell. + // returns false if next cell is obstacle and robot stays on the current cell. + boolean move(); + + // Robot will stay on the same cell after calling turnLeft/turnRight. + // Each turn will be 90 degrees. + void turnLeft(); + void turnRight(); + + // Clean the current cell. + void clean(); +} +Example: + +Input: +room = [ + [1,1,1,1,1,0,1,1], + [1,1,1,1,1,0,1,1], + [1,0,1,1,1,1,1,1], + [0,0,0,1,0,0,0,0], + [1,1,1,1,1,1,1,1] +], +row = 1, +col = 3 + +Explanation: +All grids in the room are marked by either 0 or 1. +0 means the cell is blocked, while 1 means the cell is accessible. +The robot initially starts at the position of row=1, col=3. +From the top left corner, its position is one row below and three columns right. +Notes: + +The input is only given to initialize the room and the robot's position internally. You must solve this problem "blindfolded". In other words, you must control the robot using only the mentioned 4 APIs, without knowing the room layout and the initial robot's position. +The robot's initial position will always be in an accessible cell. +The initial direction of the robot will be facing up. +All accessible cells are connected, which means the all cells marked as 1 will be accessible by the robot. +Assume all four edges of the grid are all surrounded by wall. +*/ + +/** + * // This is the robot's control interface. + * // You should not implement it, or speculate about its implementation + * interface Robot { + * // Returns true if the cell in front is open and robot moves into the cell. + * // Returns false if the cell in front is blocked and robot stays in the current cell. + * public boolean move(); + * + * // Robot will stay in the same cell after calling turnLeft/turnRight. + * // Each turn will be 90 degrees. + * public void turnLeft(); + * public void turnRight(); + * + * // Clean the current cell. + * public void clean(); + * } + */ +/* +- Different from regular dfs to visit all, the robot move() function need to be called, backtrack needs to move() manually and backtracking path shold not be blocked by visited positions +- Mark on the way in using set, but backtrack directly without re-check against set +- Mark coordinate 'x@y' +- Use degree to mark direction, rather than {0,0,1,-1} +- Backtrack: turn 2 times to revert, move 1 step, and turn 2 times to revert back. +*/ + +class Solution { + int[] dx = {-1, 0, 1, 0}; + int[] dy = {0, 1, 0, -1}; + public void cleanRoom(Robot robot) { + // use 'x@y' mark visited nodes, where x,y are integers tracking the coordinates + dfs(robot, new HashSet<>(), 0, 0, 0); // 0: up, 90: right, 180: down, 270: left + } + + public void dfs(Robot robot, Set visited, int x, int y, int curDir) { + String key = x + "@" + y; + if (visited.contains(key)) return; + visited.add(key); + robot.clean(); + + for (int i = 0; i < 4; i++) { // 4 directions + if(robot.move()) { // can go directly. Find the (x, y) for the next cell based on current direction + dfs(robot, visited, x + dx[curDir], y + dy[curDir], curDir); + backtrack(robot); + } + + // turn to next direction + robot.turnRight(); + curDir += 1; + curDir %= 4; + } + } + + // go back to the starting position + private void backtrack(Robot robot) { + robot.turnLeft(); + robot.turnLeft(); + robot.move(); + robot.turnRight(); + robot.turnRight(); + } +} + + + + + +class Solution { + public void cleanRoom(Robot robot) { + // use 'x@y' mark visited nodes, where x,y are integers tracking the coordinates + dfs(robot, new HashSet<>(), 0, 0, 0); // 0: up, 90: right, 180: down, 270: left + } + + public void dfs(Robot robot, Set visited, int i, int j, int curDir) { + String key = i + "@" + j; + if (visited.contains(key)) return; + visited.add(key); + robot.clean(); + + for (int n = 0; n < 4; n++) { // 4 directions + if(robot.move()) { // can go directly. Find the (x, y) for the next cell based on current direction + int x = i, y = j; + switch(curDir) { + case 0: // go up, reduce i + x = i-1; + break; + case 90: // go right + y = j+1; + break; + case 180: // go down + x = i+1; + break; + case 270: // go left + y = j-1; + break; + default: + break; + } + + dfs(robot, visited, x, y, curDir); + backtrack(robot); + } + + // turn to next direction + robot.turnRight(); + curDir += 90; + curDir %= 360; + } + } + + // go back to the starting position + private void backtrack(Robot robot) { + robot.turnLeft(); + robot.turnLeft(); + robot.move(); + robot.turnRight(); + robot.turnRight(); + } +} + + +``` \ No newline at end of file diff --git a/Java/Roman to Integer.java b/Java/Roman to Integer.java new file mode 100755 index 0000000..0a09671 --- /dev/null +++ b/Java/Roman to Integer.java @@ -0,0 +1,140 @@ +E +1533541868 +tags: Math, String + +#### String +- 熟悉罗马字母规则 +- 1. 'I V X L C D M' 分别代表的数字 +- 2. 列举combo的情况,需要从原sum里面减掉多加的部分: 'IV, IX'减2, 'XL, XC'减20, 'CD, CM'减200. +- Leading `I(1*2)`, `X(10*2)`, `C(100*2)` causes double counting + +https://en.wikipedia.org/wiki/Roman_numerals + +``` +/* +Given a roman numeral, convert it to an integer. + +Input is guaranteed to be within the range from 1 to 3999. + +Hide Company Tags Bloomberg Uber +Hide Tags Math String +Hide Similar Problems (M) Integer to Roman + +*/ + +/* +Thoughts: +Know the rules of roman number: +I: 1 +V: 5 +X: 10 +L: 50 +C: 100 +D: 500 +M: 1000 + +There are usual combinations: +IV: 4 +IX: 9 +XL: 40 +XC: 90 +CD: 400 +CM: 900 +When the combo occurs, for instance: +I V -> 1 + 5 = 6 +IV -> 4 +=> extra '2' +If we sum each individual digits, then we need to extract the extra '2' + +Similar for other examples: +C D -> 100 + 500 = 600 +DC -> 400 +=> extra '200' + +The solution is: +add all digits up, and remove the number of combo occurrances +*/ +class Solution { + public int romanToInt(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int sum = 0; + for (int i = 0; i < s.length(); i++) { + switch(s.charAt(i)) { + case 'I': sum += 1; break; + case 'V': sum += 5; break; + case 'X': sum += 10; break; + case 'L': sum += 50; break; + case 'C': sum += 100; break; + case 'D': sum += 500; break; + case 'M': sum += 1000; break; + default: return 0; + } + } + + sum -= countCombo("IV", s) * 2; + sum -= countCombo("IX", s) * 2; + sum -= countCombo("XL", s) * 2 * 10; + sum -= countCombo("XC", s) * 2 * 10; + sum -= countCombo("CD", s) * 2 * 100; + sum -= countCombo("CM", s) * 2 * 100; + return sum; + } + + private int countCombo(final String pattern, String str) { + int count = 0; + while (str.contains(pattern)) { + count++; + str = str.substring(str.indexOf(pattern) + pattern.length()); + } + return count; + } +} + + +//switch different char, add all together +//Letter representation, and 'less than' special combos + +public class Solution { + public int romanToInt(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int num = 0; + for (int i = 0; i < s.length(); i++) { + switch(s.charAt(i)) { + case 'I': num += 1; break; + case 'V': num += 5; break; + case 'X': num += 10; break; + case 'L': num += 50; break; + case 'C': num += 100; break; + case 'D': num += 500; break; + case 'M': num += 1000; break; + default: + return 0; + } + } + + num -= countMatch(s, "IV") * 2; + num -= countMatch(s, "IX") * 2; + num -= countMatch(s, "XL") * 10 * 2; + num -= countMatch(s, "XC") * 10 * 2; + num -= countMatch(s, "CD") * 100 * 2; + num -= countMatch(s, "CM") * 100 * 2; + + return num; + } + + public int countMatch(String s, String key) { + int count = 0; + while (s.indexOf(key) != -1) { + count++; + int i = s.indexOf(key); + s = s.substring(i + 2); + } + return count; + } + +} +``` diff --git a/Java/Rotate Image.java b/Java/Rotate Image.java new file mode 100755 index 0000000..4ba21fe --- /dev/null +++ b/Java/Rotate Image.java @@ -0,0 +1,54 @@ +M +tags: Array, Enumeration + +#### 找公式规律 +- 找到个转角度的规律公式: r = c; c = (w - r) +- 用temp variable, swap in place. + +``` +/* +You are given an n x n 2D matrix representing an image. +Rotate the image by 90 degrees (clockwise). + +Have you met this question in a real interview? Yes +Example +Given a matrix + +[ + [1,2], + [3,4] +] +rotate it by 90 degrees (clockwise), return + +[ + [3,1], + [4,2] +] +Challenge +Do it in-place. + +Tags Expand +Cracking The Coding Interview Matrix +*/ + + +//in matrix, to find next position: r = c; c = (w - r). Work on the equation => oldR = w - c, oldC = r +//In pace: do loop, change 4 at once. do a quater of the matrix +public class Solution { + public void rotate(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return; + } + int width = matrix.length; + for (int i = 0; i < width/2; i++) { + for (int j = 0; j < Math.ceil(width/2.0); j++) { + int temp = matrix[i][j]; + matrix[i][j] = matrix[width - 1 - j][i]; + matrix[width - 1 - j][i] = matrix[width - 1 - i][width - 1 - j]; + matrix[width - 1 - i][width - 1 - j] = matrix[j][width - 1 - i]; + matrix[j][width - 1 - i] = temp; + } + } + } +} +``` \ No newline at end of file diff --git a/Java/Rotate List.java b/Java/Rotate List.java old mode 100644 new mode 100755 index 7c64d9f..18c1507 --- a/Java/Rotate List.java +++ b/Java/Rotate List.java @@ -1,19 +1,76 @@ +M +1526788922 +tags: Linked List, Two Pointers -/* Given a list, rotate the list to the right by k places, where k is non-negative. +给一个single linked list, 右移k steps. k non-negative. -Example -Given 1->2->3->4->5->null and k=2 -return 4->5->1->2->3->null -Tags Expand -Basic Implementation Linked List +#### Linked List basics +- 记得用dummy.next来存head. +- 特殊: 这里k可能大于list总长. 写一写linked node 移动的步数, 然后 k = k % n. +- 找到newTail, newHead, 然后利用dummy, 换位子 -Thining process: -Two pointers. -First pointer move k steps. -Then 2 pointers start moving together. When 1st pointer reaches the end, then 2nd pointer should be in middle. -Let 2nd pointer be head, and move original head to tail of the list +``` + +/* +Given a linked list, rotate the list to the right by k places, where k is non-negative. + +Example 1: + +Input: 1->2->3->4->5->NULL, k = 2 +Output: 4->5->1->2->3->NULL +Explanation: +rotate 1 steps to the right: 5->1->2->3->4->NULL +rotate 2 steps to the right: 4->5->1->2->3->NULL +Example 2: + +Input: 0->1->2->NULL, k = 4 +Output: 2->0->1->NULL +Explanation: +rotate 1 steps to the right: 2->0->1->NULL +rotate 2 steps to the right: 1->2->0->NULL +rotate 3 steps to the right: 0->1->2->NULL +rotate 4 steps to the right: 2->0->1->NULL */ + +class Solution { + public ListNode rotateRight(ListNode head, int k) { + // edge case + if (head == null || head.next == null || k <= 0) { + return head; + } + + ListNode dummy = new ListNode(0); + dummy.next = head; + + // find length n, store endNode + ListNode node = head; + int n = 1; + while (node.next != null) { + n++; + node = node.next; + } + ListNode endNode = node; // node.next == null + + // mod k, find (n - k - 1)th node + k = k % n; + int step = n - k - 1; + ListNode newTail = head; // newTail.next will be the newHead + while (step > 0) { + newTail = newTail.next; + step--; + } + + // Re-link + endNode.next = dummy.next; // link endNode -> original head + dummy.next = newTail.next; // update dummy.next = newHead, which is just newTail.next + newTail.next = null; // cut tail.next + + return dummy.next; + } +} + + /** * Definition for singly-linked list. * public class ListNode { @@ -62,3 +119,5 @@ public ListNode rotateRight(ListNode head, int k) { return dummy.next; } } + +``` \ No newline at end of file diff --git a/Java/Rotate String.java b/Java/Rotate String.java old mode 100644 new mode 100755 index b1840f9..48487a9 --- a/Java/Rotate String.java +++ b/Java/Rotate String.java @@ -1,7 +1,67 @@ -还是三步rotate. -有个坑:offset可能很长,那么要%length,才能得到真正需要rotate的部分。 -Note: rotate 一个 full length之后,是string 不变 +E +1526570339 +tags: String + +给两个String, 看A rotate之后 能不能变成B + +#### LeetCode +- Basics +- StringBuffer.deleteCharAt(xx), StringBuffer.append(xx) +- O(n) + + +#### LintCode +- Different problem: 给一个char[], 要rotate offset times. +- *三步rotate* +- 有个坑:offset可能很长,那么要%length,才能得到真正需要rotate的部分。 +- Note: rotate 一个 full length之后,是string 不变 + ``` +/* +We are given two strings, A and B. + +A shift on A consists of taking string A and moving the leftmost character to the rightmost position. +For example, if A = 'abcde', then it will be 'bcdea' after one shift on A. +Return True if and only if A can become B after some number of shifts on A. + +Example 1: +Input: A = 'abcde', B = 'cdeab' +Output: true + +Example 2: +Input: A = 'abcde', B = 'abced' +Output: false +Note: + +A and B will have length at most 100. + + */ + +class Solution { + public boolean rotateString(String A, String B) { + // check edge condition + if (A == null || B == null || A.length() != B.length()) { + return false; + } + if (A.equals(B)) { + return true; + } + // move characters, one at a time, and compare + StringBuffer sb = new StringBuffer(A); + for (char c : A.toCharArray()) { + sb.deleteCharAt(0); + sb.append(c); + if (sb.toString().equals(B)) { + return true; + } + } + return false; + } +} + + + + /* 17% Accepted Given a string and an offset, rotate string by offset. (rotate from left to right) diff --git a/Java/Russian Doll Envelopes.java b/Java/Russian Doll Envelopes.java new file mode 100644 index 0000000..a71de20 --- /dev/null +++ b/Java/Russian Doll Envelopes.java @@ -0,0 +1,102 @@ +H +1523597951 +tags: Binary Search, DP, Coordinate DP + +俄罗斯套娃, 这里用envelope来表现. 给一串array, 每一个[x, y] 是envelope 长宽. [[5,4],[6,4],[6,7],[2,3]]. + +看用这些套娃, 可以最多套几个. + +#### DP: 1D Coordinate +- envelopes没有顺序, 先排序 (主要根据第一个index排序) +- 然后观察: 排序过后, 就变成了1D的坐标动态规划. +- max number 取决于上一个成功Russian doll的 max value + 1 +- 上一个index不知道, 所以遍历找上一个index. +- 当下index i 的状态, 取决于前面index j 的状态, 所以遍历两个index. +- O(n^2)的DP, n = envelopes.length; + +#### DP: 2D Coordinate +- 这个方法是自己想出来的, 但是时间复杂度太大, timeout +- 把envelop标记在2D grid上面, 然后像走机器人一样, 求到最右下角的最大 count max. +- count 当下能存在多少Russian doll +- 两种情况: 当下coordinate 没有target, 当下coordinate有target +- 当下coordinate 没有target: 如同机器人走法, Math.max(dp[i - 1][j], dp[i][j - 1]) +- 当下coordinate 有target: dp[i - 1][j - 1] + dp[i][j] +- timeout: O(n^2), n = largest coordinate. + + +``` +/* +You have a number of envelopes with widths and heights given as a pair of integers (w, h). +One envelope can fit into another if and only if both the width and height of one envelope +is greater than the width and height of the other envelope. + +What is the maximum number of envelopes can you Russian doll? (put one inside other) + +Example: +Given envelopes = [[5,4],[6,4],[6,7],[2,3]], +the maximum number of envelopes you can Russian doll is 3 ([2,3] => [5,4] => [6,7]). + +*/ + +// DP, O(n^2), where n = envelopes.length +class Solution { + public int maxEnvelopes(int[][] envelopes) { + if (envelopes == null || envelopes.length == 0 + || envelopes[0] == null || envelopes[0].length == 0) { + return 0; + } + Arrays.sort(envelopes, new Comparator() { + public int compare (int[] a, int[] b) { + if (a[0] == b[0]) { + return a[1] - b[1]; + } else { + return a[0] - b[0]; + } + } + }); + + int n = envelopes.length; + int[] dp = new int[n]; + int max = 0; + for (int i = 0; i < n; i++) { + dp[i] = 1; + for (int j = 0; j < i; j++) { + if(envelopes[j][0] < envelopes[i][0] && envelopes[j][1] < envelopes[i][1]) { + dp[i] = Math.max(dp[i], dp[j] + 1); + } + } + max = Math.max(max, dp[i]); + } + return max; + } +} + +// DP, O(n^2), where n = longest width, timeout +class Solution { + public int maxEnvelopes(int[][] envelopes) { + if (envelopes == null || envelopes.length == 0 + || envelopes[0] == null || envelopes[0].length == 0) { + return 0; + } + // find boundary + int n = 0, m = 0; + for (int[] envelope : envelopes) { + n = Math.max(n, envelope[0]); + m = Math.max(m, envelope[1]); + } + // Mark 1 for existing envelope corrdinate + int[][] dp = new int[n + 1][m + 1]; + for (int[] envelope : envelopes) { + dp[envelope[0]][envelope[1]] = 1; + } + + for (int i = 1; i <= n; i++) { + for (int j = 1; j <= m; j++) { + int temp = dp[i][j] == 1 ? dp[i - 1][j - 1] + dp[i][j] : Math.max(dp[i - 1][j], dp[i][j - 1]); + dp[i][j] = Math.max(temp, dp[i][j]); + } + } + return dp[n][m]; + } +} +``` \ No newline at end of file diff --git a/Java/Same Tree.java b/Java/Same Tree.java new file mode 100644 index 0000000..8b9edf3 --- /dev/null +++ b/Java/Same Tree.java @@ -0,0 +1,74 @@ +E +1519629098 +tags: Tree, DFS + +给两个 binary tree, 看两个tree是否identical. + +#### DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### BFS +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + +``` +/* +Given two binary trees, write a function to check if they are the same or not. + +Two binary trees are considered the same if they are structurally identical +and the nodes have the same value. + + +Example 1: + +Input: 1 1 + / \ / \ + 2 3 2 3 + + [1,2,3], [1,2,3] + +Output: true +Example 2: + +Input: 1 1 + / \ + 2 2 + + [1,2], [1,null,2] + +Output: false +Example 3: + +Input: 1 1 + / \ / \ + 2 1 1 2 + + [1,2,1], [1,1,2] + +Output: false +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Thoughts: +Use the function itself with dfs. +Check p == q, p.left==q.left, p.right==q.right +*/ +class Solution { + public boolean isSameTree(TreeNode p, TreeNode q) { + if (p == null || q == null) { + return p == null && q == null; + } + return p.val == q.val && isSameTree(p.left, q.left) && isSameTree(p.right, q.right); + } +} +``` \ No newline at end of file diff --git a/Java/Scramble String.java b/Java/Scramble String.java new file mode 100644 index 0000000..80b6d51 --- /dev/null +++ b/Java/Scramble String.java @@ -0,0 +1,193 @@ +H +1518594579 +tags: DP, String, Interval DP + +- 给两个string S, T. 检验他们是不是scramble string. +- scramble string 定义: string可以被分拆成binary tree的形式, 也就是切割成substring; +- 旋转了不是leaf的node之后, 形成新的substring, 这就是原来string的 scramble. + + +#### Interval DP 区间型 +- 降维打击, 分割, 按照长度来dp. +- dp[i][j][k]: 数组S从index i 开始, T从index j 开始, 长度为k的子串, 是否为scramble string + +##### Break down +- 一切两半以后, 看两种情况: , 或者不rotate这两半. 对于这些substring, 各自验证他们是否scramble. +- 不rotate分割的两半: S[part1] 对应 T[part1] && S[part2] 对应 T[part2]. +- rotate分割的两半: S[part1] 对应 T[part2] && S[part2] 对应 T[part1]. + +##### Initialization +- len == 1的时候, 其实无法旋转, 也就是看S,T的相对应的index是否字符相等. +- initialization非常非常重要. 很神奇, 这个initailization 打好了DP的基础, 后面一蹴而就, 用数学表达式就算出了结果. +- input s1, s2 在整个题目的主要内容里面, 几乎没有用到, 只是用在initialization时候. +- More details, 看解答 + +``` +/* +Given a string s1, we may represent it as a binary tree by partitioning it +to two non-empty substrings recursively. + +Below is one possible representation of s1 = "great": + + great + / \ + gr eat + / \ / \ +g r e at + / \ + a t +To scramble the string, we may choose any non-leaf node and swap its two children. + +For example, if we choose the node "gr" and swap its two children, +it produces a scrambled string "rgeat". + + rgeat + / \ + rg eat + / \ / \ +r g e at + / \ + a t +We say that "rgeat" is a scrambled string of "great". + +Similarly, if we continue to swap the children of nodes "eat" and "at", +it produces a scrambled string "rgtae". + + rgtae + / \ + rg tae + / \ / \ +r g ta e + / \ + t a +We say that "rgtae" is a scrambled string of "great". + +Given two strings s1 and s2 of the same length, determine if s2 is a scrambled string of s1. +*/ + +/* +Thoughts: +Want to determin the given string S can be scramble/rotate to T. +With the tree formation, it leads us to think of the string in partition: can S[i,j] be scambled to T[i,j]? +If all substrings can be in scrambled format, it'll return true. + +dp[i][j][h][k]: can S(i, j) be scrambled to T(h, k)? + +Reduce it to dp[i][j][k]: starting from index i for S, index j for T, with length k. +Can the substrings be scramble? +End: want dp[0][0][n] to be srambled. + +Need size to be boolean dp[n][n][n + 1] +with len == 0, always false +with len == 1, if S[i]==T[j], then true. + +with len == 2, do dp; +increase length from 1 ~ len, perform dp. + +Consider two conditions: +rotate parent string || not rotating parent string + +*/ +class Solution { + public boolean isScramble(String s1, String s2) { + if (s1 == null || s2 == null) { + return s1 == null && s2 == null; + } + if (s1.length() != s2.length()) { + return false; + } + if (s1.equals(s2)) { + return true; + } + + int n = s1.length(); + boolean[][][] dp = new boolean[n][n][n + 1]; + int len = 1; + // Initialize with len = 1; + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + dp[i][j][1] = s1.charAt(i) == s2.charAt(j); + } + } + + // len = 2 + for (len = 2; len <= n; len++) { + for (int i = 0; i <= n - len; i++) { + for (int j = 0; j <= n - len; j++) { + for (int w = 1; w < len; w++) { // w = length of 1st substring + dp[i][j][len] |= dp[i][j][w] && dp[i + w][j + w][len - w]; // not rotating parent string + dp[i][j][len] |= dp[i][j + (len - w)][w] && dp[i + w][j][len - w]; // not rotating parent string + } + } + } + } + return dp[0][0][n]; + } +} + +/* +Thoughts: +两个string, 代号 S, T +中间砍一刀, 分开两边. 要么是左右交换, 要么是不交换. +首先有了dp[i][j][k][h]: +分隔开来的S[i, j] 跟 T[k, h]是否是scramble string? +want to have dp[0][n][0][n] == true + +变成了4维数组. 优化: +4维数组, 但是自由度只有3: 有3个变量可以确定第四个变量: +因为scramble string的长度相等, 所以 j - I = h - k => h = j + k- I +也就是说, 我们可以降维, 不需要4维数组. + +降维: 最后一个维度省略, 变成了 length为维度: +*** dp[i][j][k]: 数组S从index i 开始, T从index j 开始, 长度为k的子串, 是否为scramble string? + +等式需要细心写出来: +分割scramble过后, 原string定为S, 生成的string定为T +分割开来一刀, 分别割成 S1,S2, 和 T1,T2 +假设S/T的总长都是k, 而分割点割在了位置为w的地方. +两种情况: +子序列不换位置: 要求分割开来的S1和T1是scramble, S2和T2是scramble +S1和T1的关系: dp[i][j][w] => s1 从 index[i] 割 w length, s2从index[j]割了 w length; 是否是scramble string +S2和T2的关系: dp[i + w][j + w][k - w] => S2和T2都在割过w length后的地方, 也就是i+w, j+w; 长度是总长减去w: k - w. + +子序列换位置:要求分开的s1和s2换位置; 也就是s1对应的t1, 现在跑到了后面的位置 +S1和T1的关系: s1在原地, t1换到了后面: t1还是w length, 所以位置变成了 j + (k - w) +S2和T2的关系: s2照旧在i+w的位置, t2换到了最前面: index j + +综合上面的 情况, 并且让w循环[1, k-1] +dp[i][j][k] = OR{dp[i][j][w] && dp[i + w][j + w][k - w]}, where w = [1, k-1] + OR + OR{dp[i][j + k - w][w] && dp[i + w][j][k - w]}, where w = [1, k-1] + */ + + class Solution { + public boolean isScramble(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() != s2.length()) { + return false; + } + int n = s1.length(); + boolean[][][] dp = new boolean[n][n][n + 1]; + + // len = 1 + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) { + dp[i][j][1] = s1.charAt(i) == s2.charAt(j); + } + } + + // len = 2 + for (int len = 2; len <= n; len++) { + for (int i = 0; i <= n - len; i++) { + for (int j = 0; j <= n - len; j++) { + // dp[i][j][len] = false; // default is false as well + for (int w = 1; w < len; w++) { + dp[i][j][len] |= (dp[i][j][w] && dp[i + w][j + w][len - w]); + dp[i][j][len] |= (dp[i][j + len - w][w] && dp[i + w][j][len - w]); + } + } + } + } + return dp[0][0][n]; + } +} +``` \ No newline at end of file diff --git a/Java/Search Insert Position.java b/Java/Search Insert Position.java old mode 100644 new mode 100755 index c259195..119b5ad --- a/Java/Search Insert Position.java +++ b/Java/Search Insert Position.java @@ -1,3 +1,5 @@ +E + 一般的binary search. 在结尾判断该return 哪个position。 ``` diff --git a/Java/Search Range in Binary Search Tree .java b/Java/Search Range in Binary Search Tree .java old mode 100644 new mode 100755 index b7a9423..5d2c081 --- a/Java/Search Range in Binary Search Tree .java +++ b/Java/Search Range in Binary Search Tree .java @@ -1,6 +1,20 @@ +M +1528177257 +tags: BST, Binary Tree + +给一个BST, integer range (k1, k2), 找range 里面所有的integer. + +#### BST +- 等于dfs遍历了所有k1<= x <= k2的x node。 +- dfs left, process root, then dfs right +- 这里, 把 left/right/match的情况全部cover了,然后把k1,k2的边框限制好,中间就全部遍历了。 + +``` /* -37% Accepted -Given two values k1 and k2 (where k1 < k2) and a root pointer to a Binary Search Tree. Find all the keys of tree in range k1 to k2. i.e. print all x such that k1<=x<=k2 and x is a key of given BST. Return all the keys in ascending order. + +Given two values k1 and k2 (where k1 < k2) and a root pointer to a Binary Search Tree. +Find all the keys of tree in range k1 to k2. +i.e. print all x such that k1<=x<=k2 and x is a key of given BST. Return all the keys in ascending order. Example For example, if k1 = 10 and k2 = 22, then your function should print 12, 20 and 22. @@ -21,28 +35,29 @@ Given two values k1 and k2 (where k1 < k2) and a root pointer to a Binary Search Thinking Process: Find lowest and turn around. - If don’t hit the ground, keep digging: - if (root.val > k1) dig into root.left + If don’t hit the ground, keep digging: + if (root.val > k1) dig into root.left Check current Find maximum and turn around. - If don’t hit the ceiling, keep climbing: - if (root.val < k2) climb to root.right + If don’t hit the ceiling, keep climbing: + if (root.val < k2) climb to root.right */ public class Solution { /** - * @param root: The root of the binary search tree. - * @param k1 and k2: range k1 to k2. - * @return: Return all keys that k1<=key<=k2 in ascending order. + * @param root: param root: The root of the binary search tree + * @param k1: An integer + * @param k2: An integer + * @return: return: Return all keys that k1<=key<=k2 in ascending order */ - public ArrayList searchRange(TreeNode root, int k1, int k2) { - ArrayList result = new ArrayList(); + public List searchRange(TreeNode root, int k1, int k2) { + List result = new ArrayList<>(); helper(result, root, k1, k2); return result; } - public void helper(ArrayList result, TreeNode root, int k1, int k2) { + public void helper(List result, TreeNode root, int k1, int k2) { if (root == null) { return; } @@ -58,3 +73,4 @@ public void helper(ArrayList result, TreeNode root, int k1, int k2) { } } +``` \ No newline at end of file diff --git a/Java/Search a 2D Matrix II.java b/Java/Search a 2D Matrix II.java old mode 100644 new mode 100755 index c9c8710..bcd44b0 --- a/Java/Search a 2D Matrix II.java +++ b/Java/Search a 2D Matrix II.java @@ -1,7 +1,95 @@ -根据给定的性质,找个点(left-bottom):每次有任何情况,只能往一个方向运行。 -每次删掉一行,或者一列 +M +1528169466 +tags: Binary Search, Divide and Conquer + +给matrix, 每一行sorted, 每一列从上往下sorted, 找target是否存在 + +#### Binary Search +- 根据给定的性质, 其实点选的极端一点: x = 最下面的row, y = 当下一行里面最小的left position. +- (x,y)在左下角 +- 在此情况下, 只能往一个方向运行: 如果小于target, y++; 如果大于target, 那么只能x-- +- 每次操作, 都是删掉一行, 或者一列, 再也不需要回头看 +- `while (x >= 0 && y < col) {}` 确保不会跑脱 +- 同样的方式: 可以从右上角(0, col - 1) 开始, 代码稍微改一改 + +#### Divide and Conquer? +- TODO + ``` +/** +LeetCode: check existance +Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties: + +Integers in each row are sorted in ascending from left to right. +Integers in each column are sorted in ascending from top to bottom. +Consider the following matrix: + +[ + [1, 4, 7, 11, 15], + [2, 5, 8, 12, 19], + [3, 6, 9, 16, 22], + [10, 13, 14, 17, 24], + [18, 21, 23, 26, 30] +] +Example 1: + +Input: matrix, target = 5 +Output: true +Example 2: + +Input: matrix, target = 20 +Output: false +*/ +// from bottom-left, (x,y) = (row -1, 0) +public class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return false; + } + int row = matrix.length; + int col = matrix[0].length; + int x = row - 1; + int y = 0; + + while (x >= 0 && y < col) { + if (matrix[x][y] < target) { + y++; + } else if (matrix[x][y] > target) { + x--; + } else {//matrix[x][y] == target + return true; + } + } + return false; + } +} + +// from top-right, (x,y) = (0, col - 1) +public class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return false; + } + int row = matrix.length; + int col = matrix[0].length; + int x = 0; + int y = col - 1; + + while (x < row && y >= 0) { + if (matrix[x][y] < target) { + x++; + } else if (matrix[x][y] > target) { + y--; + } else {//matrix[x][y] == target + return true; + } + } + return false; + } +} + /* +LintCode: find # of occurances Write an efficient algorithm that searches for a value in an m x n matrix, return the occurrence of it. This matrix has the following properties: diff --git a/Java/Search a 2D Matrix.java b/Java/Search a 2D Matrix.java old mode 100644 new mode 100755 index 23759e1..05b307a --- a/Java/Search a 2D Matrix.java +++ b/Java/Search a 2D Matrix.java @@ -1,6 +1,13 @@ -一行一行是从小到大,连续的: -2D转1D。 -Binary Search +M +1528167745 +tags: Array, Binary Search + +给2D matrix, 每行sorted, 每行的首位都大于上一行的末尾. goal: find target from matrix + +#### 2D matrix 转1D array +- 一行一行是从小到大, sorted, 连续的, 可以看做1D sorted array +- Binary Search + ``` /* Write an efficient algorithm that searches for a value in an m x n matrix. diff --git a/Java/Search for a Range.java b/Java/Search for a Range.java old mode 100644 new mode 100755 index e498cb5..fc6173b --- a/Java/Search for a Range.java +++ b/Java/Search for a Range.java @@ -1,4 +1,15 @@ -前后跑2个while loop。 找first/last occurance +M +1528173258 +tags: Array, Binary Search + +给sorted array, 有重复数字, 找跟target重合所在的range. + +#### Binary Search +- 2个while loop +- 找first/last occurance +- TODO: Can the code be simplified? + + ``` /* Given a sorted array of integers, find the starting and ending position of a given target value. @@ -62,10 +73,9 @@ public int[] searchRange(int[] A, int target) { } else { return rst; } - rst[0] = first; //check last occurance - int last = 0; + int last = first; start = first; end = A.length - 1; while (start + 1 < end) { @@ -88,9 +98,9 @@ public int[] searchRange(int[] A, int target) { last = mid; } else if (A[start] == target) { last = start; - } else { - last = first; } + + rst[0] = first; rst[1] = last; return rst; @@ -106,6 +116,8 @@ public int[] searchRange(int[] A, int target) { + + //Older solution: public class Solution { /** diff --git a/Java/Search in Rotated Sorted Array II.java b/Java/Search in Rotated Sorted Array II.java new file mode 100755 index 0000000..60e2ed0 --- /dev/null +++ b/Java/Search in Rotated Sorted Array II.java @@ -0,0 +1,95 @@ +M +tags: Array, Binary Search + +Allow duplicates之后: +因为最终binary search的结果也是O(n) +所以这道题要记得: 既然是O(n), 那来个简单的for loop 也就好了。 + +当然,要跟面试官提起来原因。别一上来就只有for。。。 +``` +/* +LeetCode +Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. + +(i.e., [0,0,1,2,2,5,6] might become [2,5,6,0,0,1,2]). + +You are given a target value to search. If found in the array return true, otherwise return false. + +Example 1: + +Input: nums = [2,5,6,0,0,1,2], target = 0 +Output: true +Example 2: + +Input: nums = [2,5,6,0,0,1,2], target = 3 +Output: false +Follow up: + +This is a follow up problem to Search in Rotated Sorted Array, where nums may contain duplicates. +Would this affect the run-time complexity? How and why? + + +*/ + + + + +/* +Follow up for "Search in Rotated Sorted Array": +What if duplicates are allowed? + +Would this affect the run-time complexity? How and why? + +Write a function to determine if a given target is in the array. + +Example +Tags Expand +Binary Search Sorted Array Array + +Thinking process: +This seems results in O(n) when allowing duplicates. Other than that, it's quite similar to Search In Rotated Sorted Array I. +*/ + +public class Solution { + /** + * param A : an integer ratated sorted array and duplicates are allowed + * param target : an integer to be search + * return : a boolean + */ + public boolean search(int[] A, int target) { + // write your code here + if (A.length == 0) { + return false; + } + + int start = 0; + int end = A.length - 1; + int mid; + + while (start + 1 < end) { + mid = start + (end - start) / 2; + if (A[mid] == target) {//Check central point + return true; + } + if (A[start] < A[mid]){//1st section is continous + if (A[start] <= target && target <= A[mid]) {//target in 1st section? + end = mid; + } else { + start = mid; + } + } else {//2nd section is continous + if (A[mid] <= target && target <= A[end]) {//target in 2nd section? + start = mid; + } else { + end = mid; + } + } + }//While + + return (A[start] == target) || (A[end] == target); + } +} + + + +``` \ No newline at end of file diff --git a/Java/Search in Rotated Sorted Array.java b/Java/Search in Rotated Sorted Array.java new file mode 100755 index 0000000..0c00e2f --- /dev/null +++ b/Java/Search in Rotated Sorted Array.java @@ -0,0 +1,147 @@ +M +1532072230 +tags: Array, Binary Search +time: log(n) +space: O(1) + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 +- 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` +- `start mid`: start = mid; +- 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- `mid < target < end`: start = mid; +- `target < mid`: end = mid; + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + +``` +/* +Suppose an array sorted in ascending order is rotated at some pivot unknown to you beforehand. + +(i.e., [0,1,2,4,5,6,7] might become [4,5,6,7,0,1,2]). + +You are given a target value to search. If found in the array return its index, otherwise return -1. + +You may assume no duplicate exists in the array. + +Your algorithm's runtime complexity must be in the order of O(log n). + +Example 1: + +Input: nums = [4,5,6,7,0,1,2], target = 0 +Output: 4 +Example 2: + +Input: nums = [4,5,6,7,0,1,2], target = 3 +Output: -1 +*/ + + + +/* + Just 1 binary search: this is the better solution + //Observation: + //1. There is only one break point + //2. There has to be a side that's continous, either first section or second section. + //3. Need to locate that continous section, then check if target is part of the continous section + +*/ + +class Solution { + public int search(int[] nums, int target) { + if (nums == null || nums.length == 0) return -1; + int start = 0, end = nums.length - 1; + while (start + 1 < end) { + int mid = start + (end - start) / 2; + if (nums[mid] == target) return mid; + if (nums[start] < nums[mid]) { //Land in 1st continous section + if (nums[start] <= target && target <= nums[mid]) { //target in 1st section? + end = mid; + } else { + start = mid; + } + } else { //Land in 2nd continous section + if (nums[mid] <= target && target <= nums[end]) { //target in 2nd section? + start = mid; + } else { + end = mid; + } + } + } + if (nums[start] == target) return start; + if (nums[end] == target) return end; + + return -1; + } +} + +// Solution2: Find the break point, then decide which section to binary search +public class Solution { + + public int search(int[] A, int target) { + if (A == null || A.length == 0) { + return -1; + } + int p1 = 0; + int p2 = A.length - 1; + int start = p1; + int end = p2; + int mid = start + (end - start)/2; + //find break point + int breakPoint = 0; + while (start + 1 < end) { + mid = start + (end - start)/2; + if (A[mid] == target) { + return mid; + } else if (A[mid] >= A[p1]) { + start = mid; + } else { + end = mid; + } + } + if (A[start] > A[end]) { + breakPoint = start; + } else { + breakPoint = end; + } + + if (A[p1] <= target && target <= A[breakPoint]) { + start = p1; + end = breakPoint; + } else { + start = breakPoint + 1; + end = p2; + } + + //search for target + while (start + 1 < end) { + mid = start + (end - start)/2; + if (A[mid] == target) { + return mid; + } else if (A[mid] > target) { + end = mid; + } else { + start = mid; + } + } + + if (A[start] == target) { + return start; + } else if (A[end] == target) { + return end; + } + return -1; + } +} + + + + +``` \ No newline at end of file diff --git a/Java/Segment Tree Build II.java b/Java/Segment Tree Build II.java old mode 100644 new mode 100755 index 4c8b25f..1dcc523 --- a/Java/Segment Tree Build II.java +++ b/Java/Segment Tree Build II.java @@ -1,38 +1,52 @@ -给的是Array。注意找区间内的max, assign给区间。 -想:区间break到底,像segment tree build I 里面一样最终也就是 start==end。也就是max=A[start] 或者A[end] -往上一层,其实max就是比较左右孩子。然后一次递推。每次找max其实都是在两个sub-tree里面比较sub-tree的max。 -这就好做了: -先分,找到left/right,比较max,在create current node,再append到当前node上面。 +M +1527867789 +tags: Segment Tree, Divide and Conquer, Binary Tree, Lint + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 -实际上是depth-first, 自底向上建立起的。 ``` /* -The structure of Segment Tree is a binary tree which each node has two attributes start and end -denote an segment / interval. +The structure of Segment Tree is a binary tree which each node +has two attributes start and end denote an segment / interval. + start and end are both integers, they should be assigned in following rules: -The root's start and end is given by build method. -The left child of node A has start=A.left, end=(A.left + A.right) / 2. -The right child of node A has start=(A.left + A.right) / 2 + 1, end=A.right. -if start equals to end, there will be no children for this node. -Implement a build method with a given array, so that we can create a corresponding segment tree -with every node value represent the corresponding interval max value in the array, -return the root of this segment tree. +- The root's start and end is given by build method. +- The left child of node A has start=A.left, end=(A.left + A.right) / 2. +- The right child of node A has start=(A.left + A.right) / 2 + 1, end=A.right. +- if start equals to end, there will be no children for this node. + +Implement a build method with a given array, so that we can +create a corresponding segment tree with every node value represent +the corresponding interval max value in the array, return the root of this segment tree. + +Clarification +Segment Tree (a.k.a Interval Tree) is an advanced data structure which can support queries like: +- which of these intervals contain a given point +- which of these points are in a given interval Example Given [3,2,1,4]. The segment tree will be: + [0, 3] (max = 4) / \ [0, 1] (max = 3) [2, 3] (max = 4) / \ / \ [0, 0](max = 3) [1, 1](max = 2)[2, 2](max = 1) [3, 3] (max = 4) -Clarification -Segment Tree (a.k.a Interval Tree) is an advanced data structure which can support queries like: -which of these intervals contain a given point -which of these points are in a given interval -See wiki: Segment Tree Interval Tree -Tags Expand -Segment Tree + */ /* diff --git a/Java/Segment Tree Build.java b/Java/Segment Tree Build.java old mode 100644 new mode 100755 index 32660c8..b876b4c --- a/Java/Segment Tree Build.java +++ b/Java/Segment Tree Build.java @@ -1,17 +1,37 @@ -按定义: -左孩子:(A.left, (A.left+A.rigth)/2) -右孩子:((A.left+A.rigth)/2+1, A.right) +M +1527867171 +tags: Segment Tree, Binary Tree, Divide and Conquer, Lint + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + ``` /* -The structure of Segment Tree is a binary tree which each node has two attributes start and end denote an segment / interval. +The structure of Segment Tree is a binary tree which each node +has two attributes start and end denote an segment / interval. start and end are both integers, they should be assigned in following rules: -The root's start and end is given by build method. -The left child of node A has start=A.left, end=(A.left + A.right) / 2. -The right child of node A has start=(A.left + A.right) / 2 + 1, end=A.right. -if start equals to end, there will be no children for this node. -Implement a build method with two parameters start and end, so that we can create a corresponding segment tree with every node has the correct start and end value, return the root of this segment tree. +- The root's start and end is given by build method. +- The left child of node A has start=A.left, end=(A.left + A.right) / 2. +- The right child of node A has start=(A.left + A.right) / 2 + 1, end=A.right. +- if start equals to end, there will be no children for this node. + +Implement a build method with two parameters start and end, +so that we can create a corresponding segment tree +with every node has the correct start and end value, +return the root of this segment tree. + +Clarification +Segment Tree (a.k.a Interval Tree) is an advanced data structure +which can support queries like: + +- which of these intervals contain a given point +- which of these points are in a given interval Example Given start=0, end=3. The segment tree will be: @@ -46,9 +66,10 @@ Segment Tree (a.k.a Interval Tree) is an advanced data structure which can suppo */ public class Solution { - /** - *@param start, end: Denote an segment / interval - *@return: The root of Segment Tree + /* + * @param start: start value. + * @param end: end value. + * @return: The root of Segment Tree. */ public SegmentTreeNode build(int start, int end) { if (start > end) { @@ -62,4 +83,17 @@ public SegmentTreeNode build(int start, int end) { return node; } } + +/** + * Definition of SegmentTreeNode: + * public class SegmentTreeNode { + * public int start, end; + * public SegmentTreeNode left, right; + * public SegmentTreeNode(int start, int end) { + * this.start = start, this.end = end; + * this.left = this.right = null; + * } + * } + */ + ``` \ No newline at end of file diff --git a/Java/Segment Tree Modify.java b/Java/Segment Tree Modify.java old mode 100644 new mode 100755 index f506afe..083ec9a --- a/Java/Segment Tree Modify.java +++ b/Java/Segment Tree Modify.java @@ -1,12 +1,24 @@ -在segment tree里面找index. -找到就update it with value. -每次下一层以后,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下。 -最后轮回到头顶,头顶一下包括头顶,就全部都是max了。 +M +1527895515 +tags: Segment Tree, Binary Tree, Divide and Conquer, DFS, Lint + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + ``` /* -For a Maximum Segment Tree, which each node has an extra value max to store the maximum value in this node's interval. +For a Maximum Segment Tree, which each node has an extra value max +to store the maximum value in this node's interval. + +Implement a modify function with three parameter root, index and value +to change the node's value with [start, end] = [index, index] to the new given value. +Make sure after this change, every node in segment tree still has the max attribute with the correct value. -Implement a modify function with three parameter root, index and value to change the node's value with [start, end] = [index, index] to the new given value. Make sure after this change, every node in segment tree still has the max attribute with the correct value. +Do Segment Tree build, Query problems first Example For segment tree: @@ -42,6 +54,51 @@ Do it in O(h) time, h is the height of the segment tree. LintCode Copyright Binary Tree Segment Tree */ +/* +Change value at leaf, and update all the parents' max till root. +1. find the target node recursively + compare target against root.start, root.end. + choose dfs + compare && update +2. in each dfs, update max + end case: start/end == target index, update node value, then return. + +*/ +class SegmentTreeNode { + SegmentTreeNode left, right; + int start, end, max; + public SegmentTreeNode(int start, int end, int max) { + this.start = start; + this.end = end; + this.max = max; + } +} +class Solution { + public void modify(SegmentTreeNode root, int target, int value) { + // check edge case + if (root == null) { + return; + } + if (root.start == root.end && root.start == target) { + root.max = value; + return; + } + + // dfs: find desired target direction, call modify() + if (target <= root.left.end) { + modify(root.left, target, value); + } else if (target >= root.right.start){ + modify(root.right, target, value); + } else { + return; + } + + // update max + root.max = Math.max(root.left.max, root.right.max); + } +} + + /* Thought: Renew index x with new value, and update the max value alone the way. diff --git a/Java/Segment Tree Query II.java b/Java/Segment Tree Query II.java old mode 100644 new mode 100755 index 30b6ea6..441c9f3 --- a/Java/Segment Tree Query II.java +++ b/Java/Segment Tree Query II.java @@ -1,11 +1,21 @@ -和 Segment Tree Query I 以及其他Segment Tree问题没啥区别。这个就是return个count。 -这个题目考的是:validate input source... -搞不清楚LintCode出这个题目干啥。 +M +1527896821 +tags: Segment Tree, Binary Tree, Divide and Conquer, DFS, Lint + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + ``` /* -For an array, we can build a SegmentTree for it, each node stores an extra attribute count to denote the number of elements in the the array which value is between interval start and end. (The array may not fully filled by elements) +For an array, we can build a SegmentTree for it, each node stores an extra attribute count to +denote the number of elements in the the array which value is between interval start and end. +(The array may not fully filled by elements) -Design a query method with three parameters root, start and end, find the number of elements in the in array's interval [start, end] by the given root of value SegmentTree. +Design a query method with three parameters root, start and end, +find the number of elements in the in array's interval [start, end] by the given root of value SegmentTree. Example For array [0, 2, 3], the corresponding value Segment Tree is: @@ -50,13 +60,7 @@ * this.left = this.right = null; * } * } - */ -public class Solution { - /** - *@param root, start, end: The root of segment tree and - * an segment / interval - *@return: The count number in the interval [start, end] - */ + */public class Solution { public int query(SegmentTreeNode root, int start, int end) { if (root == null || start > end) { return 0; @@ -64,16 +68,21 @@ public int query(SegmentTreeNode root, int start, int end) { if (root.start == start && root.end == end) { return root.count; } - //Check if over range - if ((start < root.start && end > root.end) || - (start < root.start && end < root.start) || + + //Validation1: Check if out range. If so, set border to root[start,end] + if ((start < root.start && end < root.start) || (start > root.end && end > root.end)) { return 0; - } else if (start < root.start && end <= root.end) { + } + + // Validate2: if partially overlap, set correct border + if (start < root.start) { start = root.start; - } else if (start >= root.start && end > root.end) { + } + if (end > root.end) { end = root.end; } + int mid = (root.start + root.end)/2; if (end <= mid) { return query(root.left, start, end); @@ -81,6 +90,7 @@ public int query(SegmentTreeNode root, int start, int end) { if (start > mid) { return query(root.right, start, end); } + //mid in between [start, end] return query(root.left, start, root.left.end) + query(root.right, root.right.start, end); } } diff --git a/Java/Segment Tree Query.java b/Java/Segment Tree Query.java old mode 100644 new mode 100755 index 05f9749..9987ffb --- a/Java/Segment Tree Query.java +++ b/Java/Segment Tree Query.java @@ -1,15 +1,23 @@ -[start,end]跟mid相比,可能: -全在mid左 -全在mid右 -包含了mid: 这里要特别break into 2 query method +M +1527868675 +tags: Segment Tree, Binary Tree, Divide and Conquer, DFS, Lint + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] -按定义: -mid = (root.start + root.end)/2 ``` /* -For an integer array (index from 0 to n-1, where n is the size of this array), in the corresponding SegmentTree, each node stores an extra attribute max to denote the maximum number in the interval of the array (index from start to end). +For an integer array (index from 0 to n-1, where n is the size of this array), +in the corresponding SegmentTree, each node stores an extra attribute max to denote +the maximum number in the interval of the array (index from start to end). -Design a query method with three parameters root, start and end, find the maximum number in the interval [start, end] by the given root of segment tree. +Design a query method with three parameters root, start and end, + find the maximum number in the interval [start, end] by the given root of segment tree. Example For array [1, 4, 2, 3], the corresponding Segment Tree is: diff --git a/Java/Serialize and Deserialize Binary Tree.java b/Java/Serialize and Deserialize Binary Tree.java new file mode 100755 index 0000000..9602fcd --- /dev/null +++ b/Java/Serialize and Deserialize Binary Tree.java @@ -0,0 +1,401 @@ +H +1527781232 +tags: Tree, Design, DFS, BFS, Divide and Conquer, Deque + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + +``` +/** +LeetCode +Serialization is the process of converting a data structure or object into a sequence of bits +so that it can be stored in a file or memory buffer, +or transmitted across a network connection link to be reconstructed later in the same or another computer environment. + +Design an algorithm to serialize and deserialize a binary tree. +There is no restriction on how your serialization/deserialization algorithm should work. +You just need to ensure that a binary tree can be serialized to a string +and this string can be deserialized to the original tree structure. + +Example: + +You may serialize the following tree: + + 1 + / \ + 2 3 + / \ + 4 5 + +as "[1,2,3,null,null,4,5]" +Clarification: The above format is the same as how LeetCode serializes a binary tree. +You do not necessarily need to follow this format, +so please be creative and come up with different approaches yourself. + +Note: Do not use class member/global/static variables to store states. +Your serialize and deserialize algorithms should be stateless. + +*/ + + +/* +Design an algorithm and write code to serialize and deserialize a binary tree. +Writing the tree to a file is called 'serialization' and +reading back from the file to reconstruct the exact same binary tree is 'deserialization'. + +There is no limit of how you deserialize or serialize a binary tree, +you only need to make sure you can serialize a binary tree to a string and +deserialize this string to the original structure. + +Example +An example of testdata: Binary tree {3,9,20,#,#,15,7}, denote the following structure: + + 3 + / \ + 9 20 + / \ + 15 7 +Our data serialization use bfs traversal. +This is just for when you got wrong answer and want to debug the input. + +You can use other method to do serializaiton and deserialization. + +Tags Expand +Binary Tree +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + // Just use queue to parse pre-order seralized string + // Trick: use '#' to mark null node, so recursive function know to stop. + public class Codec { + private final String DELI = ","; + private final String NULL = "#"; + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + StringBuffer sb = new StringBuffer(); + buildString(root, sb); + return sb.toString(); + } + private void buildString(TreeNode node, StringBuffer sb) { + if (node == null) { + sb.append(NULL).append(DELI); + } else { + sb.append(node.val).append(DELI); + buildString(node.left, sb); + buildString(node.right, sb); + } + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + Queue nodes = new LinkedList<>(Arrays.asList(data.split(DELI))); + return buildTree(nodes); + } + + private TreeNode buildTree(Queue nodes) { + String val = nodes.poll(); + if (val.equals(NULL)) return null; + TreeNode node = new TreeNode(Integer.parseInt(val)); + node.left = buildTree(nodes); + node.right = buildTree(nodes); + return node; + } +} + +// DFS, Recursive +public class Codec { + private final String DELI = ","; + private final String NULL = "#"; + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + StringBuffer sb = new StringBuffer(); + buildString(root, sb); + return sb.toString(); + } + private void buildString(TreeNode node, StringBuffer sb) { + if (node == null) { + sb.append(NULL).append(DELI); + } else { + sb.append(node.val).append(DELI); + buildString(node.left, sb); + buildString(node.right, sb); + } + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + Deque nodes = new LinkedList<>(); + nodes.addAll(Arrays.asList(data.split(DELI))); + return buildTree(nodes); + } + + private TreeNode buildTree(Deque nodes) { + String val = nodes.remove(); + if (val.equals(NULL)) return null; + TreeNode node = new TreeNode(Integer.parseInt(val)); + node.left = buildTree(nodes); + node.right = buildTree(nodes); + return node; + } +} + +public class Codec { + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + if (root == null) { + return "#,"; + } + String mid = root.val + ","; + String left = serialize(root.left); + String right = serialize(root.right); + mid += left + right; + return mid; + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + StringBuffer sb = new StringBuffer(data); + return dfs(sb); + } + + private TreeNode dfs(StringBuffer sb) { + if (sb.indexOf("#,") == 0) { + truncateData(sb); + return null; + } + + int val = Integer.parseInt(sb.substring(0, sb.indexOf(","))); + TreeNode mid = new TreeNode(val); + truncateData(sb); + + // each dfs works on global data string, and will end untill it hits leaf node + mid.left = dfs(sb); + mid.right = dfs(sb); + return mid; + } + + private void truncateData(StringBuffer sb) { + sb.delete(0, sb.indexOf(",") + 1); + } +} + + + +//BFS. store as string, separated by ',' +//Note: need to record null node as well. but be careful don't push null into queue +public class Codec { + public String serialize(TreeNode root) { + String rst = ""; + if (root == null) { + return rst; + } + Queue queue = new LinkedList<>(); + queue.offer(root); + int size = 0; + while (!queue.isEmpty()) { + size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + if (node.val == Integer.MIN_VALUE) { + rst += "#,"; + } else { + rst += node.val + ","; + TreeNode left = node.left == null ? + new TreeNode(Integer.MIN_VALUE) : node.left; + queue.offer(left); + TreeNode right = node.right == null ? + new TreeNode(Integer.MIN_VALUE) : node.right; + queue.offer(right); + } + } + } + return rst; + } + + public TreeNode deserialize(String data) { + if (data == null || data.length() == 0) { + return null; + } + StringBuffer sb = new StringBuffer(data); + TreeNode root = new TreeNode(0); + root.val = Integer.parseInt(findValue(sb)); + truncateData(sb); + + Queue queue = new LinkedList<>(); + queue.offer(root); + int size = 0; + while (!queue.isEmpty()) { + size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + String nextVal = findValue(sb); + if (!nextVal.equals("#")) { + node.left = new TreeNode(Integer.parseInt(nextVal)); + queue.offer(node.left); + } + truncateData(sb); + + nextVal = findValue(sb); + if (!nextVal.equals("#")) { + node.right = new TreeNode(Integer.parseInt(nextVal)); + queue.offer(node.right); + } + truncateData(sb); + } + } + + return root; + } + + private void truncateData(StringBuffer sb) { + sb.delete(0, sb.indexOf(",") + 1); + } + + private String findValue(StringBuffer sb) { + return sb.substring(0, sb.indexOf(",")); + } +} + +// Previous notes: + +// DFS with global data string +public class Codec { + + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + if (root == null) { + return "#,"; + } + String mid = root.val + ","; + String left = serialize(root.left); + String right = serialize(root.right); + mid += left + right; + return mid; + } + + // Decodes your encoded data to tree. + private String data = ""; + + public TreeNode deserialize(String data) { + this.data = data; + return dfs(); + } + + private TreeNode dfs() { + if (checkLeaf()) { + truncateData(); + return null; + } + + TreeNode mid = new TreeNode(findValue()); + truncateData(); + + // each dfs works on global data string, and will end untill it hits leaf node + mid.left = dfs(); + mid.right = dfs(); + return mid; + } + + private void truncateData() { + this.data = this.data.substring(this.data.indexOf(",") + 1); + } + + private int findValue() { + return Integer.parseInt(this.data.substring(0, this.data.indexOf(","))); + } + + private boolean checkLeaf() { + return this.data.indexOf("#,") == 0; + } +} + +// Your Codec object will be instantiated and called as such: +// Codec codec = new Codec(); +// codec.deserialize(codec.serialize(root)); + +/** +1. Carefully turn the binary tree into a string: use pre-order in this example. +2. Use a global variable to track the data(data string will be cut in different levels of recursion). +The concept is very easy tho, just need to carefully code it up. +*/ +class Solution { + /** + * This method will be invoked first, you should design your own algorithm + * to serialize a binary tree which denote by a root node to a string which + * can be easily deserialized by your own "deserialize" method later. + */ + public String serialize(TreeNode root) { + if (root == null) { + return "#,"; + } + String mid = root.val + ","; + String left = serialize(root.left); + String right = serialize(root.right); + mid += left + right; + return mid; + } + + private String data = ""; + /** + * This method will be invoked second, the argument data is what exactly + * you serialized at method "serialize", that means the data is not given by + * system, it's given by your own serialize method. So the format of data is + * designed by yourself, and deserialize it here as you serialize it in + * "serialize" method. + */ + public TreeNode deserialize(String data) { + this.data = data; + return desHelper(); + } + + public TreeNode desHelper() { + if (this.data.indexOf("#,") == 0) { + this.data = this.data.substring(this.data.indexOf(",") + 1); + return null; + } + String midVal = this.data.substring(0, this.data.indexOf(",")); + TreeNode mid = new TreeNode(Integer.parseInt(midVal)); + this.data = this.data.substring(this.data.indexOf(",") + 1); + TreeNode left = desHelper(); + TreeNode right = desHelper(); + mid.left = left; + mid.right = right; + return mid; + } +} + + +``` \ No newline at end of file diff --git a/Java/Shortest Distance from All Buildings.java b/Java/Shortest Distance from All Buildings.java new file mode 100644 index 0000000..3538cc8 --- /dev/null +++ b/Java/Shortest Distance from All Buildings.java @@ -0,0 +1,46 @@ +H +1533539906 +tags: BFS + +给Walls and Gates很像, 不同的是, 这道题要选一个 coordinate, having shortest sum distance to all buildings (marked as 1). + +#### BFS +- BFS 可以 mark shortest distance from bulding -> any possible spot. +- Try each building (marked as 1) -> BFS cover all 0. +- time: O(n^2) * # of building; use new visited[][] to mark visited for each building. +- O(n^2) find smallest point/aggregation value. +- 注意, 这道题我们update grid[][] sum up with shortest path value from building. +- 最后找个min value 就好了, 甚至不用return coordinate. +- 分析过, 还没有写. + +``` + +/* +You want to build a house on an empty land which reaches all buildings +in the shortest amount of distance. You can only move up, down, left and right. +You are given a 2D grid of values 0, 1 or 2, where: + +Each 0 marks an empty land which you can pass by freely. +Each 1 marks a building which you cannot pass through. +Each 2 marks an obstacle which you cannot pass through. +Example: + +Input: [[1,0,2,0,1],[0,0,0,0,0],[0,0,1,0,0]] + +1 - 0 - 2 - 0 - 1 +| | | | | +0 - 0 - 0 - 0 - 0 +| | | | | +0 - 0 - 1 - 0 - 0 + +Output: 7 + +Explanation: Given three buildings at (0,0), (0,4), (2,2), and an obstacle at (0,2), + the point (1,2) is an ideal empty land to build a house, as the total + travel distance of 3+3+1=7 is minimal. So return 7. +Note: +There will be at least one building. +If it is not possible to build such house according to the above rules, return -1. + + */ +``` \ No newline at end of file diff --git a/Java/Shortest Palindrome.java b/Java/Shortest Palindrome.java new file mode 100644 index 0000000..27b0d48 --- /dev/null +++ b/Java/Shortest Palindrome.java @@ -0,0 +1,81 @@ +H +1528350988 +tags: String, KMP + +#### Divide by mid point, Brutle +- check (mid, mid+1), or (mid-1, mid+1). +- If the two position matches, that is a palindrome candidate +- 比较front string 是否是 end string 的substring +- O(n^2) +- timeout on last case: ["aaaaaa....aaaacdaaa...aaaaaa"] + +#### KMP +- TODO + +``` +/* +Given a string s, you are allowed to convert it to a palindrome by adding characters in front of it. +Find and return the shortest palindrome you can find by performing this transformation. + +Example 1: + +Input: "aacecaaa" +Output: "aaacecaaa" +Example 2: + +Input: "abcd" +Output: "dcbabcd" + +*/ + + +/* +Thoughts: +1. Find mid (based on odd or even). n / 2 or n / 2 - 1 +2. loop mid -> 0: check (mid, mid+1), or (mid-1, mid+1) +3. If match, do front.reverse() check if it's substring of end +4. if so, return end.reverse() + (mid) + end + +Timeout on last case: ["aaaaaa"] +*/ + +class Solution { + public String shortestPalindrome(String s) { + // edge case + if (s == null || s.length() <= 1) { + return s; + } + + // find mid + int n = s.length(); + int mid = n / 2 + (n % 2 == 0 ? -1 : 0); + + // loop from mid -> 0, have buffer to store end + // perform 2 checks, and substring check + for (int i = mid; i >= 0; i--) { + String end = s.substring(i + 1); + String reversedEnd = new StringBuffer(s.substring(i + 1)).reverse().toString(); + // check (mid, mid+1) + if (validateSubstring(s, i, i + 1)) { + return reversedEnd + end; + } + // check (mid-1, mid+1) + if (i >= 1 && validateSubstring(s, i - 1, i + 1)) { + return reversedEnd + s.charAt(i) + end; + } + } + + return new StringBuffer(s.substring(1)).reverse().toString() + s; + } + + private boolean validateSubstring(String s, int i, int j) { + String end = s.substring(j).toString(); + String front = new StringBuffer(s.substring(0, i + 1)).reverse().toString(); + if (front.length() <= end.length() && end.indexOf(front) == 0) { + return true; + } + return false; + } +} + +``` \ No newline at end of file diff --git a/Java/Shortest Word Distance.java b/Java/Shortest Word Distance.java new file mode 100644 index 0000000..670d397 --- /dev/null +++ b/Java/Shortest Word Distance.java @@ -0,0 +1,49 @@ +E + +找short distance, wordB可以在wordA的前后;而同一时间,只需要计算一个最近的up to date的distance。 +greedy不断变更A/B index再做比较即可。 + +``` +/* +Given a list of words and two words word1 and word2, return the shortest distance between these two words in the list. + +For example, +Assume that words = ["practice", "makes", "perfect", "coding", "makes"]. + +Given word1 = “coding”, word2 = “practice”, return 3. +Given word1 = "makes", word2 = "coding", return 1. + +Note: +You may assume that word1 does not equal to word2, and word1 and word2 are both in the list. +*/ + +/* +Thoughts: +For word A and B. +At one time, A can only be most close to two possible B's from left or right. +For the current A, left-B is known and right-B is unkown, but will encounter in the future. +Therefore, we always only have to keep the two index: indexA, indexB updated and always try to calculate the latest amount the two. +This is quite Greedy. + +O(n) +*/ + +class Solution { + public int shortestDistance(String[] words, String word1, String word2) { + int indexWord1 = -1; + int indexWord2 = -1; + int distance = Integer.MAX_VALUE; + for (int i = 0; i < words.length; i++) { + if (words[i].equals(word1)) { + indexWord1 = i; + } else if (words[i].equals(word2)) { + indexWord2 = i; + } + if (indexWord1 >= 0 && indexWord2 >= 0) { + distance = Math.min(distance, Math.abs(indexWord2 - indexWord1)); + } + } + return distance; + } +} +``` \ No newline at end of file diff --git a/Java/Shuffle an Array.java b/Java/Shuffle an Array.java new file mode 100644 index 0000000..f24c3a9 --- /dev/null +++ b/Java/Shuffle an Array.java @@ -0,0 +1,165 @@ +M +1524032851 +tags: Permutation + +像shuffle music 一样, 做一套shuffle array的functions: + +shuffle() 给出random的permutation + +reset() 给出最初的nums + +#### Permutation +- Permutation 实际上就是在list/array/... 上面给元素换位置 +- 硬换位置, 每次换的位置不同, 用random来找到要换的index +- 维持同一个random seed +- O(n) + +##### Note +- compute all permutations 太慢, 不可行. + +``` + +/* +Shuffle a set of numbers without duplicates. + +Example: + +// Init an array with set 1, 2, and 3. +int[] nums = {1,2,3}; +Solution solution = new Solution(nums); + +// Shuffle the array [1,2,3] and return its result. +Any permutation of [1,2,3] must equally likely to be returned. +solution.shuffle(); + +// Resets the array back to its original configuration [1,2,3]. +solution.reset(); + +// Returns the random shuffling of array [1,2,3]. +solution.shuffle(); + +*/ + +/* +Thoughts: +1. Store original = nums; +2. Use universal Random object +3. Randomly pick n different indexes to swap around, which produces permutation +*/ +class Solution { + private int[] original; + private Random random; + + public Solution(int[] nums) { + if (nums == null || nums.length == 0) { + original = new int[]{ }; + return; + } + int n = nums.length; + original = Arrays.copyOf(nums, n); + random = new Random(); + } + + /** Resets the array to its original configuration and return it. */ + public int[] reset() { + return original; + } + + /** Returns a random shuffling of the array. */ + public int[] shuffle() { + if (original == null || original.length == 0) { + return original; + } + int n = original.length; + int[] output = Arrays.copyOf(original, n); + for (int i = 0; i < n; i++) { + int offset = random.nextInt(n - i); + swap(output, i, i + offset); + } + return output; + } + + private void swap(int[] nums, int x, int y) { + int temp = nums[y]; + nums[y] = nums[x]; + nums[x] = temp; + } +} + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(nums); + * int[] param_1 = obj.reset(); + * int[] param_2 = obj.shuffle(); + */ + +/* +It's not necessary to compute all permutations during initialization, not efficient. +Thoughts: +1. Store a original versiion +2. Find all permutations +3. Return a random item from the list +*/ +class Solution { + private int[] original; + private List permutation; + public Solution(int[] nums) { + if (nums == null || nums.length == 0) { + original = null; + permutation = null; + } + int n = nums.length; + // Copy + original = Arrays.copyOf(nums, n); + + // Permutation + Arrays.sort(nums); + permutation = new ArrayList<>(); + permutation.add(nums); + + for (int pos = 0; pos < n; pos++) { + for (int i = permutation.size() - 1; i >= 0; i--) { + int[] arr = permutation.get(i); + Arrays.sort(arr, pos, n); + + for (int j = pos + 1; j < n; j++) { + if (arr[j] == arr[j - 1]) { + continue; + } + swap(arr, pos, j); + permutation.add(Arrays.copyOf(arr, n)); + swap(arr, pos, j); + } + } + } + } + + private void swap(int[] nums, int x, int y) { + int temp = nums[y]; + nums[y] = nums[x]; + nums[x] = temp; + } + + /** Resets the array to its original configuration and return it. */ + public int[] reset() { + return original; + } + + /** Returns a random shuffling of the array. */ + public int[] shuffle() { + if (permutation == null || permutation.size() == 0 || original == null || original.length == 0) { + return null; + } + Random rd = new Random(); + int index = rd.nextInt(original.length); + return permutation.get(index); + } +} + +/** + * Your Solution object will be instantiated and called as such: + * Solution obj = new Solution(nums); + * int[] param_1 = obj.reset(); + * int[] param_2 = obj.shuffle(); + */ +``` \ No newline at end of file diff --git a/Java/Simplify Path.java b/Java/Simplify Path.java new file mode 100644 index 0000000..5ba9cd7 --- /dev/null +++ b/Java/Simplify Path.java @@ -0,0 +1,69 @@ +M +1531693483 +tags: String, Stack +time: O(n) +space: O(n) + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path 的情况, 不懂得要问: +- 1. `.` 代表current directory, 可以忽略. +- 2. `../` 表示previous level. +- 3. double slash 可以忽略. +- 4. empty string 要output `/` +- 最终就是用stack (`上一个加进去的item, 用来备选pop() out`), 遇到 `../` pop()掉上一个加上去的item, 其余加进stack +- 最终用 '/' 把所有item连接起来. + +``` +/* +Given an absolute path for a file (Unix-style), simplify it. + +For example, +path = "/home/", => "/home" +path = "/a/./b/../../c/", => "/c" + +Corner Cases: + +Did you consider the case where path = "/../"? +In this case, you should return "/". +Another corner case is the path might contain multiple slashes '/' together, such as "/home//foo/". +In this case, you should ignore redundant slashes and return "/home/foo". + +*/ + +class Solution { + public String simplifyPath(String path) { + if (validate(path)) return path; + + // build stack, may skip + Stack stack = new Stack<>(); + String[] parts = path.split("/"); + for (String s : parts) { + if (s.isEmpty() || s.equals(".")) continue; + if (s.equals("..")) { + if (!stack.isEmpty()) stack.pop(); + continue; + } + stack.push(s); + } + + // build output + StringBuffer sb = new StringBuffer(); + while (!stack.isEmpty()) { + sb.insert(0, "/" + stack.pop()); + } + if (sb.length() == 0) sb.append("/"); + + return sb.toString(); + } + + private boolean validate(String path) { // '../' case in begining + if (path == null || path.length() == 0) return true; + if (path.charAt(0) != '/') return true; + return false; + } +} + + +``` \ No newline at end of file diff --git a/Java/Single Number II.java b/Java/Single Number II.java old mode 100644 new mode 100755 index 695efc8..6e791dd --- a/Java/Single Number II.java +++ b/Java/Single Number II.java @@ -1,3 +1,32 @@ +M +tags: Bit Manipulation + +一串数字里面, 所有数字都重复三次, 除了一个数字. 找到这个数字, linear time, without extrace space (constant space) + +TODO: bits + +``` +/* +Given a non-empty array of integers, every element appears three times except for one, +which appears exactly once. Find that single one. + +Note: + +Your algorithm should have a linear runtime complexity. Could you implement it without using extra memory? + +Example 1: + +Input: [2,2,3,2] +Output: 3 +Example 2: + +Input: [0,1,0,1,0,1,99] +Output: 99 + + +*/ + + /* Given 3*n + 1 numbers, every numbers occurs triple times except one, find it. Example @@ -14,24 +43,26 @@ 4. When each bit’s XOR process finishes, bit OR it with result */ -public class Solution { - public int singleNumberII(int[] A) { +class Solution { + public int singleNumber(int[] A) { if (A == null || A.length == 0) { return -1; } - //present the XOR results in binary format + //present the XOR results in binary format int[] bits = new int[32]; int rst = 0; for (int i = 0; i < 32; i++) { for (int j = 0; j < A.length; j++){ - //XOR the numbers in a 3-base fashion. Whenever bit[i] has a number 3, set it back to 0. + //XOR the numbers in a 3-base fashion. Whenever bit[i] has a number 3, set it back to 0. bits[i] += A[j] >> i & 1; bits[i] %= 3; } - //OR it to the result. However, each time only the i - spot is updated with the bits[i]. + //OR it to the result. However, each time only the i - spot is updated with the bits[i]. rst |= bits[i] << i; } return rst; } } + +``` \ No newline at end of file diff --git a/Java/Single Number III.java b/Java/Single Number III.java old mode 100644 new mode 100755 index 556f15b..e4a32e7 --- a/Java/Single Number III.java +++ b/Java/Single Number III.java @@ -1,3 +1,9 @@ +M +tags: Bit Manipulation + +TODO: wut? +``` + /* Given 2*n + 2 numbers, every numbers occurs twice except two, find them. @@ -49,3 +55,5 @@ public List singleNumberIII(int[] A) { } } + +``` \ No newline at end of file diff --git a/Java/Single Number.java b/Java/Single Number.java old mode 100644 new mode 100755 index f67f2e2..cb08036 --- a/Java/Single Number.java +++ b/Java/Single Number.java @@ -1,3 +1,9 @@ +E + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + +``` /* 62% Accepted Given 2*n + 1 numbers, every numbers occurs twice except one, find it. @@ -14,27 +20,37 @@ Manipulate bits: Thinking process: One-pass and constant extra space. -since all numbers appears twice, consider them as in bits format. Two identical number XOR will be zero. If we XOR everything double-numbers together, it will be zero. At the end, we use o XOR our target number, the result is actually the target number. +since all numbers appears twice, consider them as in bits format. Two identical number XOR will be zero. +If we XOR everything double-numbers together, it will be zero. +At the end, we use o XOR our target number, the result is actually the target number. Very smart trick to use bits. In order to compare from index 0 to the end, we need to extract index 0 first as result before for loop. And start for loop at i = 1. */ -public class Solution { - /** - *@param A : an integer array - *return : a integer - */ - public int singleNumber(int[] A) { - if (A == null || A.length == 0) { - return 0; +class Solution { + public int singleNumber(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; } - int rst = A[0]; - for (int i = 1; i < A.length; i++) { - rst = rst ^ A[i]; + for (int i = 1; i < nums.length; i++) { + nums[0] = nums[0] ^ nums[i]; } - return rst; - } + return nums[0]; + } } +class Solution { + public int singleNumber(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int result = nums[0]; + for (int i = 1; i < nums.length; i++) { + result = result ^ nums[i]; + } + return result; + } +} +``` \ No newline at end of file diff --git a/Java/Singleton.java b/Java/Singleton.java old mode 100644 new mode 100755 index 7a5b4d2..e186cb8 --- a/Java/Singleton.java +++ b/Java/Singleton.java @@ -1,7 +1,19 @@ +E +1525415182 +tags: Design + +让一个class 是 singleton + +``` /* -Singleton is a most widely used design pattern. If a class has and only has one instance at every moment, we call this design as singleton. For example, for class Mouse (not a animal mouse), we should design it in singleton. +Singleton is a most widely used design pattern. +If a class has and only has one instance at every moment, +we call this design as singleton. + +For example, for class Mouse (not a animal mouse), we should design it in singleton. -You job is to implement a getInstance method for given class, return the same instance of this class every time you call this method. +You job is to implement a getInstance method for given class, +return the same instance of this class every time you call this method. Example @@ -17,17 +29,21 @@ Singleton is a most widely used design pattern. If a class has and only has one Tags Expand LintCode Copyright OO Design -Thoughts: -... -Was not clear to me. Need to loop up more on synchronized/volatile -Good reference: -http://www.cnblogs.com/EdwardLiu/p/4443230.html - */ +class Solution { + public static Solution instance = null; + public static Solution getInstance() { + if (instance == null) { + instance = new Solution(); + } + return instance; + } +}; +// Thread safe: class Solution { - public static volatile Solution solution = null; + public static Solution solution = null; /** * @return: The same instance of this class every time */ @@ -43,3 +59,5 @@ public static Solution getInstance() { return solution; } }; + +``` \ No newline at end of file diff --git a/Java/Sliding Puzzle.java b/Java/Sliding Puzzle.java new file mode 100644 index 0000000..7a3b739 --- /dev/null +++ b/Java/Sliding Puzzle.java @@ -0,0 +1,133 @@ +H +1534211551 +tags: BFS, Graph + +``` +/* +On a 2x3 board, there are 5 tiles represented by the integers 1 through 5, +and an empty square represented by 0. + +A move consists of choosing 0 and a 4-directionally adjacent number and swapping it. + +The state of the board is solved if and only if the board is [[1,2,3],[4,5,0]]. + +Given a puzzle board, return the least number of moves required so that the +state of the board is solved. If it is impossible for the state of the board to be solved, return -1. + +Examples: + +Input: board = [[1,2,3],[4,0,5]] +Output: 1 +Explanation: Swap the 0 and the 5 in one move. +Input: board = [[1,2,3],[5,4,0]] +Output: -1 +Explanation: No number of moves will make the board solved. +Input: board = [[4,1,2],[5,0,3]] +Output: 5 +Explanation: 5 is the smallest number of moves that solves the board. +An example path: +After move 0: [[4,1,2],[5,0,3]] +After move 1: [[4,1,2],[0,5,3]] +After move 2: [[0,1,2],[4,5,3]] +After move 3: [[1,0,2],[4,5,3]] +After move 4: [[1,2,0],[4,5,3]] +After move 5: [[1,2,3],[4,5,0]] +Input: board = [[3,2,4],[1,5,0]] +Output: 14 +Note: + +board will be a 2 x 3 array as described above. +board[i][j] will be a permutation of [0, 1, 2, 3, 4, 5]. + +*/ + +/* +1. convert input +2. create node (x, y, nums) +3. start bfs, track step; enumerate 4 directions +4. swap(nums, x, y, newX, newY), evaluate(nums) +*/ +class Solution { + class Node { + int x, y; + int[] nums; + public Node(int x, int y, int[] nums) { + this.x = x; + this.y = y; + this.nums = nums; + } + } + int[] dx = {1, - 1, 0, 0}; + int[] dy = {0, 0, 1, - 1}; + + public int slidingPuzzle(int[][] board) { + // Convert + int x = 0, y = 0; + int[] nums = new int[6]; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + nums[i * 3 + j] = board[i][j]; + if (board[i][j] == 0) { + x = i; + y = j; + } + } + } + + // Init and process queue + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + queue.offer(new Node(x, y, nums)); + + int count = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + Node node = queue.poll(); + if (evaluate(node.nums)) return count; + for (int j = 0; j < 4; j++) { + int mX = node.x + dx[j]; + int mY = node.y + dy[j]; + if (mX >= 0 && mX < 2 && mY >= 0 && mY < 3) { + int[] newBoard = (int[]) node.nums.clone(); + swap(newBoard, node.x, node.y, mX, mY); + String serializedBoard = boardToString(newBoard); + if (!visited.contains(serializedBoard)) { + visited.add(serializedBoard); + queue.offer(new Node(mX, mY, newBoard)); + } + } + } + } + count++; + } + return -1; + } + + private void swap(int[] nums, int x, int y, int mX, int mY) { + int oldIndex = x * 3 + y; + int newIndex = mX * 3 + mY; + int temp = nums[oldIndex]; + nums[oldIndex] = nums[newIndex]; + nums[newIndex] = temp; + } + + private boolean evaluate(int[] nums) { + for (int i = 0; i < nums.length; i++) { + if (i + 1 != nums[i]) { + if (i == 5 && nums[i] == 0) continue; + return false; + } + } + return true; + } + + private String boardToString(int[] nums) { + StringBuffer sb = new StringBuffer(); + for (int num : nums) { + sb.append(num + "#"); + } + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/Sliding Window Maximum.java b/Java/Sliding Window Maximum.java old mode 100644 new mode 100755 index bb9bde0..0b96465 --- a/Java/Sliding Window Maximum.java +++ b/Java/Sliding Window Maximum.java @@ -1,8 +1,98 @@ -妙:用deque数据结构(实际上采用LinkedList的形式)来做一个递减的queue. -每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. -为啥可以不管不无地剔除? -因为我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +H +1533615292 +tags: Sliding Window, Deque, Heap + +#### Deque, Monotonous queue +- 维持monotonuous queue: one end is always at max and the other end is min. Always need to return the max end of queue. +- when adding new elements x: start from small-end of the queue, drop all smaller elements and append to first element larger than x. +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`. +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! + ``` +/* +LeetCode: +Given an array nums, there is a sliding window of size k which is moving +from the very left of the array to the very right. +You can only see the k numbers in the window. +Each time the sliding window moves right by one position. + +For example, +Given nums = [1,3,-1,-3,5,3,6,7], and k = 3. + +Window position Max +--------------- ----- +[1 3 -1] -3 5 3 6 7 3 + 1 [3 -1 -3] 5 3 6 7 3 + 1 3 [-1 -3 5] 3 6 7 5 + 1 3 -1 [-3 5 3] 6 7 5 + 1 3 -1 -3 [5 3 6] 7 6 + 1 3 -1 -3 5 [3 6 7] 7 +Therefore, return the max sliding window as [3,3,5,5,6,7]. + +Note: +You may assume k is always valid, ie: 1 ≤ k ≤ input array's size for non-empty array. + +Follow up: +Could you solve it in linear time? + +*/ + +/* +Thoughts: +Brutle way: for every k window, calculate the max O(nk) +Can use priority queue to track max => O(nLogK) => O(nLogN) +Linear time: O(n)? + +Want to maintain the window, a deque, where right-most element in the window is the lowest value in the deque. +1. when adding new element, we need to clean up the deque bottom +2. when removing item, simply just match see if any top max is match; if match, remove it. + +Deque always have all unique max values, with right-most window element as smallest max in the deque. + +*/ +class Solution { + public int[] maxSlidingWindow(int[] nums, int k) { + if (nums == null || nums.length < k || k <= 0) return new int[0]; + int n = nums.length; + + int[] rst = new int[n - k + 1]; + Deque deque = new ArrayDeque<>(); + for (int i = 0; i < k - 1; i++) { // add first k items + inQueue(deque, nums[i]); + } + + for (int i = k - 1; i < n; i++) { // slide window on remain items + inQueue(deque, nums[i]); // add to maxHeap, if applicable + rst[i - k + 1] = deque.peekFirst(); + outQueue(deque, nums[i - k + 1]); // remove top max if the num === max + } + return rst; + } + /* + monotonous queue: top = max. + Remove all smaller items from queue, and only maintain max. + */ + private void inQueue(Deque deque, int num) { + while (!deque.isEmpty() && deque.peekLast() < num) { // top + deque.pollLast(); + } + deque.offerLast(num); + } + /* + if target is at top/max, remove due to sliding window + if target is not at top, it must not in the queue anyway, skip. + */ + private void outQueue(Deque deque, int num) { + if (deque.peekFirst() == num) { + deque.pollFirst(); + } + } +} + + + /* Given an array of n integer with duplicate number, and a moving window(size k), move the window at each iteration from the start of the array, find the maximum number inside the window at each moving. diff --git a/Java/Sliding Window Median.java b/Java/Sliding Window Median.java old mode 100644 new mode 100755 index dd954a3..5bd4c6c --- a/Java/Sliding Window Median.java +++ b/Java/Sliding Window Median.java @@ -1,110 +1,126 @@ -Median还是用min-heap 和 max-heap。 -移动窗口2step: -1. 加一个数。 -2. 减一个数。 -加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 -抽完balance一下。 +H +1533183594 +tags: Heap, Design, MaxHeap, MinHeap, Sliding Window -记得: -左边的maxHeap总有 x+1或者x个数字。 -后边minHeap应该一直有x个数字。 +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) -``` -/* -Given an array of n integer, and a moving window(size k), move the window at each iteration from the start of the array, find the median of the element inside the window at each moving. (If there are even numbers in the array, return the N/2-th number after sorting the element in the window. ) +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 -Example -For array [1,2,7,8,5], moving window size k = 3. return [2,7,7] +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 -At first the window is at the start of the array like this +``` +/** +Median is the middle value in an ordered integer list. +If the size of the list is even, there is no middle value. +So the median is the mean of the two middle value. -[ | 1,2,7 | ,8,5] , return the median 2; +Examples: +[2,3,4] , the median is 3 -then the window move one step forward. +[2,3], the median is (2 + 3) / 2 = 2.5 -[1, | 2,7,8 | ,5], return the median 7; +Given an array nums, there is a sliding window of size k which is moving +from the very left of the array to the very right. +You can only see the k numbers in the window. +Each time the sliding window moves right by one position. +Your job is to output the median array for each window in the original array. -then the window move one step forward again. +For example, +Given nums = [1,3,-1,-3,5,3,6,7], and k = 3. -[1,2, | 7,8,5 | ], return the median 7; +Window position Median +--------------- ----- +[1 3 -1] -3 5 3 6 7 1 + 1 [3 -1 -3] 5 3 6 7 -1 + 1 3 [-1 -3 5] 3 6 7 -1 + 1 3 -1 [-3 5 3] 6 7 3 + 1 3 -1 -3 [5 3 6] 7 5 + 1 3 -1 -3 5 [3 6 7] 6 +Therefore, return the median sliding window as [1,-1,-1,3,5,6]. -Challenge -O(nlog(n)) time +Note: +You may assume k is always valid, ie: k is always smaller than input array's size for non-empty array. + */ -Tags Expand -LintCode Copyright Heap -*/ -//NOT DONE /* Thoughts: -1. from 0 ~k, populate the maxHeap and minHeap -2. i = k ~ nums.length, add one value and minus one value, calculate median +Similar to Stream Data Median: addNum(num), findMedian(), removeNum(num) */ - -public class Solution { - PriorityQueue minHeap; - PriorityQueue maxHeap; - - /** - * @param nums: A list of integers. - * @return: The median of the element inside the window at each moving. - */ - public ArrayList medianSlidingWindow(int[] nums, int k) { - ArrayList rst = new ArrayList(); - if (nums == null || nums.length == 0 || k <= 0) { - return rst; - } - minHeap = new PriorityQueue(); - maxHeap = new PriorityQueue(10, new Comparator(){ - public int compare(Integer x, Integer y){ - return y - x; - } - }); - maxHeap.offer(nums[0]); - - for (int i = 1; i < k; i++) { - add(nums[i]); - } - - rst.add(maxHeap.peek()); - for (int i = k; i < nums.length; i++) { - add(nums[i]); - remove(nums[i - k]); - rst.add(maxHeap.peek()); - } - return rst; +class Solution { + PriorityQueue minHeap = new PriorityQueue<>(); + PriorityQueue maxHeap = new PriorityQueue<>(10, Collections.reverseOrder()); + public double[] medianSlidingWindow(int[] nums, int k) { + if (nums == null || nums.length == 0 || k <= 0) { + return null; + } + + int n = nums.length; + double[] rst = new double[n - k + 1]; + // Build median structure with window + for (int i = 0; i < k - 1; i++) { + addNum(nums[i]); + } + + // Calculate + for (int i = 0; i < n - k + 1; i++) { + addNum(nums[i + k - 1]); + rst[i] = findMedian(); + removeNum(nums[i]); + } + + return rst; } - - public void add(int val) { - int preMedian = maxHeap.peek(); - if (val > preMedian) { - minHeap.offer(val); - } else { - maxHeap.offer(val); - } - balance(); + + private double findMedian() { + if (maxHeap.size() == minHeap.size()) { + return maxHeap.peek() / 2.0 + minHeap.peek() / 2.0; // be careful with Integer.MAX_VALUE, consider breaking or use long + } else { + return maxHeap.peek(); + } } - - public void remove(int val) { - int preMedian = maxHeap.peek(); - if (val > preMedian) { - minHeap.remove(val); + + private void addNum(int num) { + if (maxHeap.isEmpty()) { + maxHeap.add(num); + return; + } + int currentMedian = maxHeap.peek(); + if (num <= currentMedian) { + maxHeap.offer(num); } else { - maxHeap.remove(val); + minHeap.offer(num); } - balance(); + balance(); } - - public void balance() { - if (maxHeap.size() > minHeap.size() + 1) { - minHeap.offer(maxHeap.poll()); - } else if (maxHeap.size() < minHeap.size()) { - maxHeap.offer(minHeap.poll()); - } + + private void removeNum(int num) { + int currentMedian = maxHeap.peek(); + if (num <= currentMedian) { + maxHeap.remove(num); + } else { + minHeap.remove(num); + } + balance(); + } + + // helper + private void balance() { + if (maxHeap.size() > minHeap.size() + 1) { + minHeap.offer(maxHeap.poll()); + } else if (maxHeap.size() < minHeap.size()) { + maxHeap.offer(minHeap.poll()); + } } - } - ``` \ No newline at end of file diff --git a/Java/Smallest Subtree with all the Deepest Nodes.java b/Java/Smallest Subtree with all the Deepest Nodes.java new file mode 100644 index 0000000..1db1260 --- /dev/null +++ b/Java/Smallest Subtree with all the Deepest Nodes.java @@ -0,0 +1,124 @@ +M +1531605756 +tags: Tree, Divide and Conquer, DFS +time: O(n) +space: O(n) + +给一个tree, 按照题意找最一个node满足: +1. 这个node的subtree涵盖最深level的所有leaves. +2. 这个node必须是能找到的最deep那个 + +条件2的需求是因为: root本身就是满足条件1的node, 还有很多Higher-level node也是如此, 所以要找那个deepest. + + +#### DFS on tree +- 分析题目, 思想是: 看到tree里面所有的leaves, 找到他们最deep的 common ancestor +- Maintain a map +- Recursively dfs: return deepest node that has all leaves by these comparisons: +- 1. If left,right child same depth, return root: they need common ancestor +- 2. If not same depth, return the one with larger depth +- 被传送去上一个level的, 永远都是subtree里面符合题意的: the node containing all leaf nodes +- Visit all nodes once O(n), space O(n) + +#### BFS +- Find all leaves at deepest level +- Use map to track each node-parent +- Backtrack all nodes to find common ancestor + +``` +/* + +Given a binary tree rooted at root, the depth of each node is the shortest distance to the root. + +A node is deepest if it has the largest depth possible among any node in the entire tree. + +The subtree of a node is that node, plus the set of all descendants of that node. + +Return the node with the largest depth such that it contains all the deepest nodes in its subtree. + + + +Example 1: + +Input: [3,5,1,6,2,0,8,null,null,7,4] +Output: [2,7,4] +Explanation: + + + +We return the node with value 2, colored in yellow in the diagram. +The nodes colored in blue are the deepest nodes of the tree. +The input "[3, 5, 1, 6, 2, 0, 8, null, null, 7, 4]" is a serialization of the given tree. +The output "[2, 7, 4]" is a serialization of the subtree rooted at the node with value 2. +Both the input and output have TreeNode type. + + +Note: + +The number of nodes in the tree will be between 1 and 500. +The values of each node are unique. + +*/ + + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +1. Recursively dfs, find max depth leaf-parent +2. If left,right child same depth, return root +3. If not same depth, return the one with larger depth +*/ +class Solution { + public TreeNode subtreeWithAllDeepest(TreeNode root) { + return dfs(root, 0, new HashMap<>()); + } + + public TreeNode dfs(TreeNode node, int depth, Map map) { + if (node == null) return node; + if (node.left == null && node.right == null) { + map.put(node, depth); + return node; + } + // find leaf-parent, just return + TreeNode left = dfs(node.left, depth + 1, map); + TreeNode right = dfs(node.right, depth + 1, map); + if (left == null || right == null) { + int maxChildDepth = left == null ? map.get(right) : map.get(left); + map.put(node, maxChildDepth); + return left == null ? right : left; + } + if (map.get(left) == map.get(right)) { + map.put(node, map.get(left)); + return node; + } + + if (map.get(left) > map.get(right)) { + map.put(node, map.get(left)); + return left; + } else { + map.put(node, map.get(right)); + return right; + } + } +} +/* +1. BFS find leaf, store child->parent in map +2. Find common ancestor for the leaves with the map +*/ +class Solution { + public TreeNode subtreeWithAllDeepest(TreeNode root) { + // check input + + // find leaves with queue, build map + + // find common ancestor of all leaves + } +} +``` \ No newline at end of file diff --git a/Java/Sort Colors II.java b/Java/Sort Colors II.java old mode 100644 new mode 100755 index a272ffe..6559767 --- a/Java/Sort Colors II.java +++ b/Java/Sort Colors II.java @@ -1,5 +1,19 @@ +M +1531086337 +tags: Sort, Two Pointers, Partition, Quick Sort + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + +``` /* -Given an array of n objects with k different colors (numbered from 1 to k), sort them so that objects of the same color are adjacent, with the colors in the order 1, 2, ... k. +LintCode +Given an array of n objects with k different colors (numbered from 1 to k), +sort them so that objects of the same color are adjacent, with the colors in the order 1, 2, ... k. Example GIven colors=[3, 2, 2, 1, 4], k=4, your code should sort colors in-place to [1, 2, 2, 3, 4]. @@ -14,6 +28,9 @@ Given an array of n objects with k different colors (numbered from 1 to k), sort Tags Expand Two Pointers Sort +*/ + +/* Thoughts (Need to revist and think about this, very interesting) Doing quick sort partition for K -1 times. @@ -23,13 +40,45 @@ Given an array of n objects with k different colors (numbered from 1 to k), sort 4. Result: only swap when low and high have disagreement on the pivot value. */ +class Solution { + public void sortColors2(int[] colors, int k) { + if (colors == null || colors.length == 0 || k <= 0) return; + int start = 0; + for (int i = 0; i < k - 1; i++) { + start = partition(colors, start, colors.length - 1, i); + } + + /* + int end = colors.length - 1; + for (int i = 0; i < k - 1; i++) { + end = helper(colors, 0, end, k - i - 1); + } + */ + + } + + // quick sort partion function template + private int partition(int[] nums, int start, int end, int pivot) { + int low = start, high = end; + while (low <= high) { + while(low < high && nums[low] <= pivot) low++; + while(high > 0 && nums[high] > pivot) high--; + + if (low <= high) swap(nums, low++, high--); + } + return low - 1; + } + + private void swap(int[] nums, int x, int y){ + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + +// Previous version class Solution { - /** - * @param colors: A list of integer - * @param k: An integer - * @return: nothing - */ public void sortColors2(int[] colors, int k) { if (colors == null || colors.length == 0 || k <= 0) { return; @@ -67,18 +116,4 @@ public int helper(int[] colors, int start, int end, int pivot) { } - - - - - - - - - - - - - - - +``` diff --git a/Java/Sort Colors.java b/Java/Sort Colors.java new file mode 100755 index 0000000..d835170 --- /dev/null +++ b/Java/Sort Colors.java @@ -0,0 +1,121 @@ +M +1531084797 +tags: Array, Two Pointers, Sort, Partition, Quick Sort + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + +``` + +/* +Given an array with n objects colored red, white or blue, +sort them in-place so that objects of the same color are adjacent, +with the colors in the order red, white and blue. + +Here, we will use the integers 0, 1, and 2 to represent the color red, white, and blue respectively. + +Note: You are not suppose to use the library's sort function for this problem. + +Example: + +Input: [2,0,2,1,1,0] +Output: [0,0,1,1,2,2] +Follow up: + +A rather straight forward solution is a two-pass algorithm using counting sort. +First, iterate the array counting number of 0's, 1's, and 2's, +then overwrite array with total number of 0's, then 1's and followed by 2's. + +Could you come up with a one-pass algorithm using only constant space? + +*/ + +/* +Thoughts; +A easier version of Sort ColorII. Using the exact same code with different k number. +Note, now k starts from 0. +*/ +// Partition, base of quick sort, 100% +class Solution { + public void sortColors(int[] nums) { + if (nums == null || nums.length == 0) { + return; + } + + // Sort [0, end] + int k = 2, end = nums.length - 1; + for (int i = 0; i < k; i++) { // 3 different colors + end = partition(nums, 0, end, k - i - 1); + } + + /** + // Sort [start, n - 1] + int k = 2, start = 0; + for (int i = 0; i < k; i++) { + start = partition(nums, start, nums.length - 1, i); + } + */ + } + // quick sort partion function template + private int partition(int[] nums, int start, int end, int pivot) { + int low = start, high = end; + while (low <= high) { + while(low < high && nums[low] <= pivot) low++; + while(high > 0 && nums[high] > pivot) high--; + + if (low <= high) swap(nums, low++, high--); + } + return low - 1; + } + + private void swap(int[] nums, int x, int y){ + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + +/* +One pass, O(n) +Since there are only 3 colors: [0, 1, 2] +- left reprents the last color==0, right reprents the first color==2. +- Move pointer: swap color0 with left, swap color2 with right. +- when index i meet right, there is not need to move +*/ +class Solution { + public void sortColors(int[] nums) { + if (nums == null || nums.length == 0) return; + int left = 0, right = nums.length - 1; + for (int i = 0; i <= right; i++) { + if (nums[i] == 0) swap(nums, i, left++); // we know any index < left is accurate + else if (nums[i] == 2) swap(nums, i--, right--); // not sure what on index right, need to re-consider i, so i-- + } + } + + private void swap(int[] nums, int x, int y){ + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + +``` diff --git a/Java/Sort Letters by Case.java b/Java/Sort Letters by Case.java old mode 100644 new mode 100755 index c136c97..256399e --- a/Java/Sort Letters by Case.java +++ b/Java/Sort Letters by Case.java @@ -1,3 +1,29 @@ +M +1531112557 +tags: Sort, Two Pointers, String, Partition + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + +``` /* Given a string which contains only letters. Sort it by lower case first and upper case second. @@ -13,6 +39,10 @@ Tags Expand String Two Pointers LintCode Copyright Sort +*/ + +/* + Thoughts: Another two pointer sorting. Difference: use a ASCII code 'a' as the pivot. all the letters that from a ~ z @@ -21,44 +51,67 @@ NOTE: in the 2 while loop, the it's always having ">=' */ - - public class Solution { - /** - *@param chars: The letter array you should sort by Case - *@return: void - */ public void sortLetters(char[] chars) { - if (chars == null || chars.length == 0) { - return; - } + if (chars == null || chars.length == 0) return; + char pivot = 'a'; - int start = 0; int end = chars.length - 1; + int start = 0, end = chars.length - 1; + while (start <= end) { - while (start <= end && chars[start] >= pivot) { - start++; - } - while (start <= end && chars[end] < pivot) { - end--; - } - if (start <= end) { - char temp = chars[end]; - chars[end] = chars[start]; - chars[start] = temp; - start++; - end--; - } + while (start <= end && chars[start] >= pivot) start++; + while (start <= end && chars[end] < pivot) end--; + + if (start <= end) swap(chars, start++, end--); } } -} - - + private void swap(char[] chars, int x, int y) { + char temp = chars[x]; + chars[x] = chars[y]; + chars[y] = temp; + } +} +// while loop instead of for loop +class Solution { + public void sortLetters(char[] chars) { + if (chars == null || chars.length == 0) return; + int i = 0, left = 0, right = chars.length - 1; + while(i <= right) { + if (chars[i] >= 'a') swap(chars, i++, left++); // we know any index < left is accurate, i++ + else swap(chars, i, right--); // not sure what on index right, need to re-consider i + } + } + + private void swap(char[] chars, int x, int y) { + char temp = chars[x]; + chars[x] = chars[y]; + chars[y] = temp; + } +} +// Pure Two pointers with for loop +class Solution { + public void sortLetters(char[] chars) { + if (chars == null || chars.length == 0) return; + int left = 0, right = chars.length - 1; + for (int i = 0; i <= right; i++) { + if (chars[i] >= 'a') swap(chars, i, left++); // we know any index < left is accurate + else swap(chars, i--, right--); // not sure what on index right, need to re-consider i, so i-- + } + } + + private void swap(char[] chars, int x, int y) { + char temp = chars[x]; + chars[x] = chars[y]; + chars[y] = temp; + } +} +``` \ No newline at end of file diff --git a/Java/Sort List.java b/Java/Sort List.java old mode 100644 new mode 100755 index 78c4b97..c7067a4 --- a/Java/Sort List.java +++ b/Java/Sort List.java @@ -1,28 +1,31 @@ -Merge sort: - 1. find middle. 快慢指针 - 2. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 - 3. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段。 - 然后mege. - 要recursively call itself. +M +1528213775 +tags: Linked List, Sort, Merge Sort, Divide and Conquer -Quick sort: -想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. -但是quick sort不建议用在list上面。 - -排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ ``` /* -28% Accepted + Sort a linked list in O(n log n) time using constant space complexity. -Example -Given 1-3->2->null, sort it to 1->2->3->null. +Example 1: -Tags Expand -Linked List +Input: 4->2->1->3 +Output: 1->2->3->4 +Example 2: +Input: -1->5->3->4->0 +Output: -1->0->3->4->5 */ @@ -38,11 +41,7 @@ Sort a linked list in O(n log n) time using constant space complexity. * } */ - - - /* - Recap:12.09.2015. practice merge sort Thinking process: 1.Divide and conquer 2. can use merge sort or quick sort. Used merge sort here. @@ -52,10 +51,18 @@ Sort a linked list in O(n log n) time using constant space complexity. Note: when checking null, node != null should be in front of node.next != null. Because if node is alreay null, node.next gives exception. */ - - - public class Solution { + public ListNode sortList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode mid = findMiddle(head); + ListNode right = sortList(mid.next); + mid.next = null; + ListNode left = sortList(head); + return merge(left, right); + } + public ListNode findMiddle(ListNode head) { ListNode slow = head; ListNode fast = head.next; @@ -86,24 +93,6 @@ public ListNode merge(ListNode left, ListNode right) { } return dummy.next; } - - /* - * @param head: The head of linked list. - * @return: You should return the head of the sorted linked list, - using constant space complexity. - */ - public ListNode sortList(ListNode head) { - if (head == null || head.next == null) { - return head; - } - ListNode mid = findMiddle(head); - ListNode left = sortList(mid.next); - mid.next = null; - ListNode right = sortList(head); - return merge(left, right); - } - - } @@ -121,15 +110,4 @@ in list. So it can be worse to o(n^2). Merge sort is prefered for list sort */ -public class Solution { - /* - * @param head: The head of linked list. - * @return: You should return the head of the sorted linked list, - using constant space complexity. - */ - public ListNode sortList(ListNode head) { - // write your code here - } -} - ``` \ No newline at end of file diff --git a/Java/Space Replacement.java b/Java/Space Replacement.java old mode 100644 new mode 100755 index cd8ca45..4ad4b5d --- a/Java/Space Replacement.java +++ b/Java/Space Replacement.java @@ -1,5 +1,10 @@ +M +tags: String + +``` /* -Write a method to replace all spaces in a string with %20. The string is given in a characters array, you can assume it has enough space for replacement and you are given the true length of the string. +Write a method to replace all spaces in a string with %20. +The string is given in a characters array, you can assume it has enough space for replacement and you are given the true length of the string. Example Given "Mr John Smith", length = 13. @@ -53,3 +58,5 @@ public int replaceBlank(char[] string, int length) { return length + count; } } + +``` \ No newline at end of file diff --git a/Java/Sparse Matrix Multiplication.java b/Java/Sparse Matrix Multiplication.java new file mode 100644 index 0000000..03f7708 --- /dev/null +++ b/Java/Sparse Matrix Multiplication.java @@ -0,0 +1,125 @@ +M +1531810563 +tags: Hash Table +time: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` +space: O(1) extra + +给两个matrics, 做乘积. 注意, 是sparse matrix (特点: 很多0). + +#### Hash Table +- Recall matric multiplication rules: result[i][j] = sum(A-row[i] * B-col[j]) +- `sparse matric: lots positions are zero` +- 平白地写matric multiplication 没有意义, 重点就是optimization: +- `optimization`: for A-zero-row, and B-zero-col, there is no need to calculate, just return 0. +- 1. Find A-zero-rows and store in setA, same for setB +- 2. during multiplication, reduce time complexity. +- Base: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` + +#### Matrices +- 乘法规则: result[i][j] = sum(A-row[i] * B-col[j]) +- A column size == B row size. 并且: 计算顺序是iterate over A column size + +``` +/* +Given two sparse matrices A and B, return the result of AB. + +You may assume that A's column number is equal to B's row number. + +Example: + +Input: + +A = [ + [ 1, 0, 0], + [-1, 0, 3] +] + +B = [ + [ 7, 0, 0 ], + [ 0, 0, 0 ], + [ 0, 0, 1 ] +] + +Output: + + | 1 0 0 | | 7 0 0 | | 7 0 0 | +AB = | -1 0 3 | x | 0 0 0 | = | -7 0 3 | + | 0 0 1 | + +*/ + +// Faster solution +class Solution { + public int[][] multiply(int[][] A, int[][] B) { + if (validate(A, B)) { + return new int[][]{}; + } + + // iterate over A, B, create setA, setB for reduce row/col + int m = A.length, n = B[0].length, index = B.length; + int[][] rst = new int[m][n]; + + // base loop, reduce + for (int i = 0; i < m; i++) { + for (int ind = 0; ind < index; ind++) { // index = A col number or B row number + if (A[i][ind] == 0) continue; + for (int j = 0; j < n; j++) { + if (B[ind][j] == 0) continue; + rst[i][j] += A[i][ind] * B[ind][j]; + } + } + } + + return rst; + } + + private boolean validate(int[][] A, int[][] B) { + if (A == null || B == null) return true; + if (A[0].length != B.length) return true; + return false; + } +} + +// Original solution, a bit slower +// Trying to reduce empty A row, or empty B column by marking their row #, col # in set +class Solution { + public int[][] multiply(int[][] A, int[][] B) { + if (validate(A, B)) { + return new int[][]{}; + } + + // iterate over A, B, create setA, setB for reduce row/col + int m = A.length, n = B[0].length, index = B.length; + int[][] rst = new int[m][n]; + Set setA = new HashSet<>(); + Set setB = new HashSet<>(); + + // base loop, reduce + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (setA.contains(i) || setB.contains(j)) { + rst[i][j] = 0; + continue; + } + boolean zeroA = true, zeroB = true; + for (int ind = 0; ind < index; ind++) { // index = A col number or B row number + rst[i][j] += A[i][ind] * B[ind][j]; + zeroA = zeroA && A[i][ind] == 0; + zeroB = zeroB && B[ind][j] == 0; + } + if (zeroA) setA.add(i); + if (zeroB) setB.add(j); + } + } + + return rst; + } + + private boolean validate(int[][] A, int[][] B) { + if (A == null || B == null) return true; + if (A[0].length != B.length) return true; + return false; + } +} + +``` \ No newline at end of file diff --git a/Java/Spiral Matrix.java b/Java/Spiral Matrix.java new file mode 100644 index 0000000..c56e020 --- /dev/null +++ b/Java/Spiral Matrix.java @@ -0,0 +1,93 @@ +M +1528298716 +tags: Array, Enumeration + +从(0,0)坐标, 走完spiral matrix, 把结果存在list里. + +#### DX, DY +- Basic implementation, array, enumeration +- 写一下position前进的方向: RIGHT->DOWN->LEFT->UP +- 用一个direction status 确定方向 +- 写一个compute direction function 改变方向 `(direction + 1) % 4` +- `boolean[][] visited` 来track走过的地方 + +``` +/* +Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in spiral order. + +Example 1: + +Input: +[ + [ 1, 2, 3 ], + [ 4, 5, 6 ], + [ 7, 8, 9 ] +] +Output: [1,2,3,6,9,8,7,4,5] +Example 2: + +Input: +[ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9,10,11,12] +] +Output: [1,2,3,4,8,12,11,10,9,5,6,7] +*/ + +/* +Thoughts: +- Keep visited +- keep moving until hit visited, then turn +*/class Solution { + int[] dx = {0, 1, 0, -1}; // RIGHT->DOWN->LEFT->UP + int[] dy = {1, 0, -1, 0}; + + public List spiralOrder(int[][] matrix) { + // check edge case + List rst = new ArrayList<>(); + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { + return rst; + } + + int m = matrix.length; + int n = matrix[0].length; + // handle single col case + if (n == 1) { + for (int i = 0; i < m; i++) { + rst.add(matrix[i][0]); + } + return rst; + } + + // construct boolean visited[][], dx{}, dy{} + boolean[][] visited = new boolean[m][n]; + + // while keep moving until count == m*n + int i = 0, x = 0, y = 0; + int direction = 0; + while (i < m * n) { + i++; + rst.add(matrix[x][y]); + visited[x][y] = true; + + // compute x/y based on current direction + direction = computeDirection(visited, x, y, direction); + x += dx[direction]; + y += dy[direction]; + } + + return rst; + } + + private int computeDirection(boolean[][] visited, int x, int y, int currDirection) { + int nextX = x + dx[currDirection]; + int nextY = y + dy[currDirection]; + if (nextX >= 0 && nextX < visited.length && nextY >= 0 && nextY < visited[0].length && !visited[nextX][nextY]) { + return currDirection; + } + return (currDirection + 1) % 4; + } +} + +``` \ No newline at end of file diff --git a/Java/Sqrt(x).java b/Java/Sqrt(x).java old mode 100644 new mode 100755 index 7a50108..1cb03a1 --- a/Java/Sqrt(x).java +++ b/Java/Sqrt(x).java @@ -1,3 +1,18 @@ +E +1520917410 +tags: Math, Binary Search + +#### s- qrt(int x) +- 理解题意, 从[0, x]找一个可以m*m=x的值. +- 注意, 如果找不到, 最后问考官该return一个什么值:按道理,因为return int, 会取整,那么return一个平方最close to x就可以. +- 注意 mid 用 long, 因为很可能超过最大int. + +#### sqrt(double x) +- 二分float number, 应该用精度来定义结尾. +- 还是二分, 但是判断条件变成: while ( end - start > eps) +- eps = 1e-12,也就是精度到1e-12 + +``` /* Implement int sqrt(int x). @@ -21,25 +36,85 @@ Implement int sqrt(int x). Binary search. While loop until the head and tail meets. */ +/* +Thoughts: +Binary Search a candidate between [0~x] +*/ class Solution { - /** - * @param x: An integer - * @return: The sqrt of x - */ - public int sqrt(int x) { - long start = 0; - long end = x; - while (end >= start) { - long mid = start + (end - start) / 2; - if (mid * mid > x) { - end = mid - 1; - } else if (mid * mid < x) { - start = mid + 1; - } else { - return (int)mid; - } - } - //When start > end, while loop ends. That means, end must be the largest possible integer that end^2 is closest to x. - return (int)end; + public int mySqrt(int x) { + long start = 0; + long end = x; + while (start + 1 < end) { + long mid = (start + end) >> 1; + if (mid * mid < x) { + start = mid; + } else if (mid * mid > x) { + end = mid; + } else { + return (int)mid; + } + } + if (end * end <= x) { + return (int)end; + } else { + return (int)start; + } } } + +/* +Thoughts: +We need to assume x is positive. There must be m such that m*m = x. Find m using binary search. +Note, naturally m will be in [0, x] +*/ +class Solution { + public int mySqrt(int x) { + /* + // 之后的逻辑都包含 + if (x <= 0) { + return 0; + }*/ + long start = 0; + long end = x; + while(start <= end) { + long mid = (start + end) / 2; // Or: long mid = start + (end - start) / 2; + + if (mid * mid < x) { + start = mid + 1; + } else if (mid * mid > x){ + end = mid - 1; + } else { + return (int)mid; + } + } + //When start > end, while loop ends. That means, end must be the largest possible integer that end^2 is closest to x. + return (int)end; + } +} + +/* +What if sqrt(double)? +*/ +class Solution { + public double mySqrt(double x) { + double start = 0; + double end = x; + double eps = 1e-12; + + while (end - start > eps) { + double mid = start + (end - start) / 2; + if (mid * mid < x) { + start = mid; + } else { + end = mid; + } + } + if (end * end <= x) { + return end; + } else { + return start; + } + } +} + +``` \ No newline at end of file diff --git a/Java/Stone Game.java b/Java/Stone Game.java old mode 100644 new mode 100755 index 7ebbf4d..6147aa6 --- a/Java/Stone Game.java +++ b/Java/Stone Game.java @@ -1,8 +1,11 @@ +M +tags: DP + 这个DP有点诡异. 需要斟酌。 NOT DONE YET ``` /* -There is a stone game.At the beginning of the game the player picks n piles of stones in a line. +There is a stone game. At the beginning of the game the player picks n piles of stones in a line. The goal is to merge the stones in one pile observing the following rules: diff --git a/Java/String Permutation.java b/Java/String Permutation.java new file mode 100644 index 0000000..dd77a13 --- /dev/null +++ b/Java/String Permutation.java @@ -0,0 +1,38 @@ +E + +把#of occurrences 存进HashMap, 第一个string 做加法,第二个string做减法。最后看是否有不等于0的作判断。 + +``` +public class Solution { + /* + * @param A: a string + * @param B: a string + * @return: a boolean + */ + public boolean Permutation(String A, String B) { + if (A == null || B == null || A.length() != B.length()) { + return false; + } + final Map strMap = new HashMap<>(); + for (int i = 0; i < A.length(); i++) { + final char charA = A.charAt(i); + final char charB = B.charAt(i); + if (!strMap.containsKey(charA)) { + strMap.put(charA, 0); + } + strMap.put(charA, strMap.get(charA) + 1); + if (!strMap.containsKey(charB)) { + strMap.put(charB, 0); + } + strMap.put(charB, strMap.get(charB) - 1); + } + for (Map.Entry entry : strMap.entrySet()) { + if (entry.getValue() != 0) { + return false; + } + } + return true; + } +} + +``` diff --git a/Java/String to Integer (atoi).java b/Java/String to Integer (atoi).java new file mode 100755 index 0000000..2f5bbc5 --- /dev/null +++ b/Java/String to Integer (atoi).java @@ -0,0 +1,169 @@ +M +1533541354 +tags: Math, String + +#### String +- check sign, leading-0, overall size > 11, check max/min in Long format +- if passed all tests, parseInt() + +#### regular expression +- if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + +``` + + + +/* +02.02.2016 from leetcode + +Implement atoi which converts a string to an integer. + +The function first discards as many whitespace characters as necessary +until the first non-whitespace character is found. Then, starting from this character, +takes an optional initial plus or minus sign followed by as many numerical digits as possible, +and interprets them as a numerical value. + +The string can contain additional characters after those that form the integral number, +which are ignored and have no effect on the behavior of this function. + +If the first sequence of non-whitespace characters in str is not a valid integral number, +or if no such sequence exists because either str is empty or it contains only whitespace characters, +no conversion is performed. + +If no valid conversion could be performed, a zero value is returned. + +Note: + +Only the space character ' ' is considered as whitespace character. +Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. +If the numerical value is out of the range of representable values, INT_MAX (231 − 1) or INT_MIN (−231) is returned. +Example 1: + +Input: "42" +Output: 42 +Example 2: + +Input: " -42" +Output: -42 +Explanation: The first non-whitespace character is '-', which is the minus sign. + Then take as many numerical digits as possible, which gets 42. +Example 3: + +Input: "4193 with words" +Output: 4193 +Explanation: Conversion stops at digit '3' as the next character is not a numerical digit. +Example 4: + +Input: "words and 987" +Output: 0 +Explanation: The first non-whitespace character is 'w', which is not a numerical + digit or a +/- sign. Therefore no valid conversion could be performed. +Example 5: + +Input: "-91283472332" +Output: -2147483648 +Explanation: The number "-91283472332" is out of the range of a 32-bit signed integer. + Thefore INT_MIN (−231) is returned. + +*/ +public class Solution { + public int myAtoi(String str) { + if (str == null) return 0; + str = str.trim(); + if (str.length() == 0) return 0; + + StringBuffer sb = new StringBuffer(); + String sign = ""; + for (int i = 0; i < str.length(); i++) { + char c = str.charAt(i); + if (i == 0 && (c == '+' || c == '-')) { + sign += c; + } else if (c >= '0' && c <= '9') { + sb.append(c); + } else { // c < '0' || c > '9' + break; + } + } + while (sb.length() > 0 && sb.charAt(0) == '0') { + sb.deleteCharAt(0); + } + String rst = sign + sb.toString(); + if (rst.length() == 0 || rst.equals("+") || rst.equals("-")) return 0; + + if (rst.length() > 11) { // max number has 11-digit: +/-2147483648 + return rst.charAt(0) == '-' ? Integer.MIN_VALUE : Integer.MAX_VALUE; + } + long largeNum = Long.parseLong(rst); + if (largeNum > Integer.MAX_VALUE || largeNum < Integer.MIN_VALUE) { + return largeNum > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + return Integer.parseInt(rst); + } +} + + +/* +LintCode +Implement function atoi to convert a string to an integer. + +If no valid conversion could be performed, a zero value is returned. + +If the correct value is out of the range of representable values, INT_MAX (2147483647) or INT_MIN (-2147483648) is returned. + +Example +"10" => 10 + +"-1" => -1 + +"123123123123123" => 2147483647 + +"1.0" => 1 + +Tags Expand +Basic Implementation String + +*/ + +/* + +Thoughts: +First idea: why not using Integer.parseInt(str)? Maybe that's too costly, and maybe it does not over all possible integers? +Can we just use a Long.parseLong(str) ? + +Issues to check: +Long is not enough, because we might be getting decimal point. So we can use Double here. +String might have space: remove all " " +String might have char of other kind: check each individual char if isNaN() + +It looks like somewhere we'd need to use regular exp to search for pattern, and remove space. + +Note: need to ask if things like '21lintcode' can be considered as partial-integer and return 21. This is a more complex case, even after reg exp. + +*/ + +public class Solution { + /** + * @param str: A string + * @return An integer + */ + public int atoi(String str) { + if (str == null || str.length() == 0) { + return 0; + } + str = str.replaceAll("\\s",""); + if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")) { + return 0; + } + double rst = Double.parseDouble(str); + if (rst > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } else if (rst < Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } else { + return (int)rst; + } + } +} + + +``` \ No newline at end of file diff --git a/Java/String to Integer(atoi).java b/Java/String to Integer(atoi).java deleted file mode 100644 index 649e926..0000000 --- a/Java/String to Integer(atoi).java +++ /dev/null @@ -1,58 +0,0 @@ -/* -Implement function atoi to convert a string to an integer. - -If no valid conversion could be performed, a zero value is returned. - -If the correct value is out of the range of representable values, INT_MAX (2147483647) or INT_MIN (-2147483648) is returned. - -Example -"10" => 10 - -"-1" => -1 - -"123123123123123" => 2147483647 - -"1.0" => 1 - -Tags Expand -Basic Implementation String - -Thoughts: -First idea: why not using Integer.parseInt(str)? Maybe that's too costly, and maybe it does not over all possible integers? -Can we just use a Long.parseLong(str) ? - -Issues to check: -Long is not enough, because we might be getting decimal point. So we can use Double here. -String might have space: remove all " " -String might have char of other kind: check each individual char if isNaN() - -It looks like somewhere we'd need to use regular exp to search for pattern, and remove space. - -Note: need to ask if things like '21lintcode' can be considered as partial-integer and return 21. This is a more complex case, even after reg exp. - -*/ - - -public class Solution { - /** - * @param str: A string - * @return An integer - */ - public int atoi(String str) { - if (str == null || str.length() == 0) { - return 0; - } - str = str.replaceAll("\\s",""); - if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")) { - return 0; - } - double rst = Double.parseDouble(str); - if (rst > Integer.MAX_VALUE) { - return Integer.MAX_VALUE; - } else if (rst < Integer.MIN_VALUE) { - return Integer.MIN_VALUE; - } else { - return (int)rst; - } - } -} \ No newline at end of file diff --git a/Java/Strobogrammatic Number II.java b/Java/Strobogrammatic Number II.java new file mode 100755 index 0000000..5b85060 --- /dev/null +++ b/Java/Strobogrammatic Number II.java @@ -0,0 +1,155 @@ +M +1528732676 +tags: Math, DFS, Sequence DFS, Enumeration + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + +``` +/* +A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside down). + +Find all strobogrammatic numbers that are of length = n. + +For example, +Given n = 2, return ["11","69","88","96"]. + +Hint: +Try to use recursion and notice that it should recurse with n - 2 instead of n - 1. + +Tags: Math Recursion +Similar Problems: (E) Strobogrammatic Number, (H) Strobogrammatic Number III + +*/ + +class Solution { + List singleDigitList = new ArrayList<>(Arrays.asList("0", "1", "8")); + char[][] digitPair = {{'0', '0'}, {'1', '1'}, {'8', '8'}, {'6', '9'}, {'9', '6'}}; + public List findStrobogrammatic(int n) { + return dfs(n, n); + } + + public List dfs(int n, int max) { + if (n <= 0) return new ArrayList(Arrays.asList("")); + if (n == 1) return singleDigitList; + + List subList = dfs(n - 2, max); + List list = new ArrayList<>(); + for (String str : subList) { + if (n != max) list.add("0" + str + "0"); + for (int i = 1; i < digitPair.length; i++) { + list.add(digitPair[i][0] + str + digitPair[i][1]); + } + } + return list; + } +} + +/* +Thoughts: +The items will be in three pattern: +1. Single digits: 0, 1, 8 +2. Double digits: 00, 11, 88, 69, 96 +3. More digits: adding digit-pair on front/tail sides on given digit +Note: validate that '0' appears on front/tail won't be counted. +Recursion untill n reaches 1 +*/ +class Solution { + List singleDigitList = new ArrayList<>(Arrays.asList("0", "1", "8")); + List doubleDigitList = new ArrayList<>(Arrays.asList("00", "11", "88", "69", "96")); + char[][] digitPair = {{'0', '0'}, {'1', '1'}, {'8', '8'}, {'6', '9'}, {'9', '6'}}; + public List findStrobogrammatic(int n) { + List result = dfs(n); + for (int i = 0; i < result.size(); i++) { + String num = result.get(i); + if ((Long.parseLong(num) + "").length() != n) { + result.remove(i); + i--; + } + } + return result; + } + + public List dfs(int n) { + List list = new ArrayList<>(); + if (n <= 0) return list; + if (n == 1) return singleDigitList; + if (n == 2) return doubleDigitList; + + List subList = dfs(n - 2); + for (String str : subList) { + for (int i = 0; i < digitPair.length; i++) { + list.add(digitPair[i][0] + str + digitPair[i][1]); + } + } + return list; + } +} + +/* +Thoughts: +For n, there can be k kinds of combination. Save it to map(n,k-list) +For n+2, there can be x + k-kinds-of-inner-number + y; +Treat n=0,1,2 differently. Then recurse on rest, layer by layer +At end end, do a O(n) scan to remove non-wanted items. +*/ +public class Solution { + private HashMap candidate = new HashMap(); + public List findStrobogrammatic(int n) { + List rst = new ArrayList(); + candidate.put("0", "0"); + candidate.put("1", "1"); + candidate.put("8", "8"); + candidate.put("6", "9"); + candidate.put("9", "6"); + rst = searchAndCombine(n); + for (int i = 0; i < rst.size(); i++) { + if ((Long.parseLong(rst.get(i))+"").length() != n) { + rst.remove(i); + i--; + } + } + return rst; + } + + public List searchAndCombine(int n) { + List list = new ArrayList(); + if (n <= 0) { + return list; + } else if (n == 1) { + list.add("0"); + list.add("1"); + list.add("8"); + return list; + } else if (n == 2){ + list.add("69"); + list.add("11"); + list.add("88"); + list.add("96"); + list.add("00"); + return list; + }else {//n >= 2 + List temp = searchAndCombine(n - 2); + for (String str : temp) { + for (Map.Entry entry : candidate.entrySet()) { + list.add(entry.getKey() + str + entry.getValue()); + } + } + } + return list; + } +} +``` \ No newline at end of file diff --git a/Java/Strobogrammatic Number.java b/Java/Strobogrammatic Number.java new file mode 100755 index 0000000..1e6e2b8 --- /dev/null +++ b/Java/Strobogrammatic Number.java @@ -0,0 +1,126 @@ +E +1533542248 +tags: Hash Table, Math, Enumeration + +根据题意枚举, 再根据规则basic implementation + +#### Alter input + +#### HashTable + Two Pointer + +``` +/* +A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside down). +Write a function to determine if a number is strobogrammatic. The number is represented as a string. +For example, the numbers "69", "88", and "818" are all strobogrammatic. +Tags: Hash Table Math +Similar Problems: (M) Strobogrammatic Number II, (H) Strobogrammatic Number III +*/ +/* +Thoughts: +The exact same number is 1 and 8. +The flip-rotate-then-same number is 6 and 9. +All we need to do is to flip number (6 and 9), and then reverse the string, see if the string is the same. +*/ +class Solution { + public boolean isStrobogrammatic(String num) { + if (num == null || num.length() == 0) { + return true; + } + if (num.length() == 1) { + return num.equals("0") || num.equals("1") || num.equals("8"); + } + List candidate = new ArrayList<>(Arrays.asList('0', '1', '8', '6', '9')); + char[] charArray = num.toCharArray(); + for (int i = 0; i < charArray.length; i++) { + if (!candidate.contains(charArray[i])) { + return false; + } + if (charArray[i] == '6') { + charArray[i] = '9'; + } else if (charArray[i] == '9') { + charArray[i] = '6'; + } + } + StringBuffer sb = new StringBuffer(String.valueOf(charArray)); + String upsideDownNum = sb.reverse().toString(); + return num.equals(upsideDownNum); + } +} + +/* +OPTS 11.04.2015 +Thoughts: +Because the symmetric pairs are: +1-1, 8-8,0-0,6-9,9-6, we make a hashmap of it. +Create left/right pointer, where each compare has to match the pair in hashmap. + +Note: +On map.containsKey() line, need to check (right), or whichever item that map is going to map.get(...) afterwards. +If containsKey fails, return false; only when it passes through, then proceed to mpa.get() +*/ +public class Solution { + public boolean isStrobogrammatic(String num) { + if (num == null || num.length() == 0) { + return true; + } + HashMap map = new HashMap(); + map.put('0','0'); + map.put('1','1'); + map.put('8','8'); + map.put('6','9'); + map.put('9','6'); + int left = 0; + int right = num.length() - 1; + while (left <= right) { + if (!map.containsKey(num.charAt(right)) || num.charAt(left) != map.get(num.charAt(right))) { + return false; + } + left++; + right--; + } + return true; + } +} + +/* +Thoughts: +Compare digits to the symmetric postion; special care for (6,9) pair, mark it after comparision. +Elimite the cases before the for-loop run through(can do it in or as well, but that just make the code a bit complex) +Note: +Didn't use HashMap. I beleive hash map is used to mark the spot? +*/ +public class Solution { + public boolean isStrobogrammatic(String num) { + if (num == null || num.length() == 0) { + return true; + } + //Any non-strobogrammatic + if (num.indexOf("2") >= 0 || num.indexOf("3") >= 0 || + num.indexOf("4") >= 0 || num.indexOf("5") >= 0 || + num.indexOf("7") >= 0) { + return false; + } + //If only 6 or 9 exist: + if ((num.indexOf("6") >= 0 && num.indexOf("9") < 0) || + (num.indexOf("9") >= 0 && num.indexOf("6") < 0)) { + return false; + } + //Check if (6,9) or other strobogrammatic # are appearing at symmetric position + char[] arr = num.toCharArray(); + int leng = num.length(); + for (int i = 0; i < leng; i++) { + if (arr[i] == '6' || arr[i] == '9') { + if ((arr[i] == '6' && arr[leng - i - 1] != '9') || + (arr[i] == '9' && arr[leng - i - 1] != '6')) { + return false; + } + arr[i] = arr[leng - i - 1] = 'M';//marker + } else if (arr[i] != 'M' && arr[i] != arr[leng - i - 1]) { + return false; + } + } + return true; + } +} +``` diff --git a/Java/Subarray Sum Closest.java b/Java/Subarray Sum Closest.java old mode 100644 new mode 100755 index 8422953..ffc203c --- a/Java/Subarray Sum Closest.java +++ b/Java/Subarray Sum Closest.java @@ -1,5 +1,27 @@ +M +1531118637 +tags: Sort, PreSum, Subarray, PriorityQueue +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + +``` /* -Given an integer array, find a subarray with sum closest to zero. Return the indexes of the first number and last number. +Given an integer array, find a subarray with sum closest to zero. +Return the indexes of the first number and last number. Example Given [-3, 1, 1, -3, 5], return [0, 2], [1, 3], [1, 1], [2, 2] or [0, 4] @@ -15,7 +37,103 @@ Credits should be given to: http://rafal.io/posts/subsequence-closest-to-t.html */ +// PriorityQueue +public class Solution { + class PreSumNode { + int val, index; + public PreSumNode(int val, int index) { + this.val = val; + this.index = index; + } + } + public int[] subarraySumClosest(int[] nums) { + int[] rst = new int[2]; + if(nums == null || nums.length <= 1) return rst; + + int n = nums.length; + PriorityQueue queue = buildPreSumNodes(nums); + + int start = 0, end = 0, min = Integer.MAX_VALUE; + while (!queue.isEmpty()) { + PreSumNode p = queue.poll(); + if (!queue.isEmpty()) { + int temp = queue.peek().val - p.val; + if (temp <= min) { + min = temp; + start = p.index; + end = queue.peek().index; + } + } + } + if (start < end) { + rst[0] = start + 1; + rst[1] = end; + } else { + rst[0] = end + 1; + rst[1] = start; + } + return rst; + } + + private PriorityQueue buildPreSumNodes(int[] nums) { + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(p -> p.val)); + int preSum = 0; + for (int i = 0; i < nums.length; i++) { + preSum += nums[i]; + queue.offer(new PreSumNode(preSum, i)); + } + return queue; + } +} + +// Use a class point to track both preSum value and index +public class Solution { + class Point { + int val, index; + public Point(int val, int index) { + this.val = val; + this.index = index; + } + } + public int[] subarraySumClosest(int[] nums) { + int[] rst = new int[2]; + if(nums == null || nums.length <= 1) return rst; + + int n = nums.length; + Point[] points = buildPoints(nums); + + int start = 0, end = 0, min = Integer.MAX_VALUE; + for (int i = 0; i < n - 1; i++) { + int temp = points[i + 1].val - points[i].val; + if (temp <= min) { + min = temp; + start = points[i].index; + end = points[i + 1].index; + } + } + if (start < end) { + rst[0] = start + 1; + rst[1] = end; + } else { + rst[0] = end + 1; + rst[1] = start; + } + return rst; + } + + private Point[] buildPoints(int[] nums) { + int n = nums.length; + Point[] points = new Point[n]; + points[0] = new Point(nums[0], 0); + for (int i = 1; i < n; i++) { + points[i] = new Point(points[i - 1].val + nums[i], i); + } + Arrays.sort(points, Comparator.comparing(p -> p.val)); + return points; + } +} +// use 2D array, same concept, a bit messy class CustomComparator implements Comparator { public int compare(int[] a, int[] b) { return Integer.compare(a[0], b[0]); @@ -23,12 +141,6 @@ public int compare(int[] a, int[] b) { } public class Solution { - - /** - * @param nums: A list of integers - * @return: A list of integers includes the index of the first number - * and the index of the last number - */ public ArrayList subarraySumClosest(int[] nums) { ArrayList rst = new ArrayList(); if(nums == null || nums.length == 0) { @@ -145,3 +257,5 @@ public static void main(String[] args){ + +``` \ No newline at end of file diff --git a/Java/Subarray Sum Equals K.java b/Java/Subarray Sum Equals K.java new file mode 100644 index 0000000..6d5ffb5 --- /dev/null +++ b/Java/Subarray Sum Equals K.java @@ -0,0 +1,95 @@ +M +1531625267 +tags: Array, Hash Table, PreSum, Subarray +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + +``` +/* +Given an array of integers and an integer k, +you need to find the total number of continuous subarrays whose sum equals to k. + +Example 1: +Input:nums = [1,1,1], k = 2 +Output: 2 +Note: +The length of the array is in range [1, 20,000]. +The range of numbers in the array is [-1000, 1000] and the range of the integer k is [-1e7, 1e7]. +*/ + +//O(n) space, O(n) time +class Solution { + public int subarraySum(int[] nums, int k) { + if (nums == null || nums.length == 0) return 0; + + Map preSumFreq = new HashMap<>(); + int n = nums.length, sum = 0, count = 0; + preSumFreq.put(0, 1); + for (int num : nums) { + sum += num; + int priorSum = sum - k; + if (preSumFreq.containsKey(priorSum)) { // # ways to sum up to priorSum. + count += preSumFreq.get(priorSum); // each way of priorSum will extend later elements to reach sum (the later elemnts will sum up to k) + } + preSumFreq.put(sum, preSumFreq.getOrDefault(sum, 0) + 1); + } + + return count; + } +} + +// PreSum, +// time O(n^2), space O(n) +class Solution { + public int subarraySum(int[] nums, int k) { + // check input + if (nums == null || nums.length == 0) return 0; + + // double for loop to calculate k + int n = nums.length, count = 0; + int[] preSum = calcPreSum(nums); + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + if (i == 0) count += preSum[j] == k ? 1 : 0; + else count += (preSum[j] - preSum[i - 1]) == k ? 1 : 0; + } + } + + return count; + } + + private int[] calcPreSum(int[] nums) { + int[] preSum = new int[nums.length]; + preSum[0] = nums[0]; + for (int i = 1; i < nums.length; i++) { + preSum[i] += preSum[i - 1] + nums[i]; + } + return preSum; + } +} + + + +``` \ No newline at end of file diff --git a/Java/Subarray Sum II.java b/Java/Subarray Sum II.java new file mode 100644 index 0000000..ca7aed6 --- /dev/null +++ b/Java/Subarray Sum II.java @@ -0,0 +1,29 @@ +H +tags: Two Pointers, Array, Binary Search + +``` +/* +Given an integer array, find a subarray where the sum of numbers is in a given interval. +Your code should return the number of possible answers. (The element in the array should be positive) + +Example +Given [1,2,3,4] and interval = [1,3], return 4. The possible answers are: + +[0, 0] +[0, 1] +[1, 1] +[2, 2] +*/ + +/* +Thoughts: +Brutle O(n2) with preSum can do. Do better: O(nlogn) +- presum +- iterate over start point, which will be the 'must have' index in result +- find the largest end point, where preSum[end] - preSum[start - 1] <= max. Binary Search since the presum will be increasing +- count += end - start + 1 +- move on to next start +- Overall it should be O(nlogn) +- should write the binary search as a separate function +*/ +``` \ No newline at end of file diff --git a/Java/Subarray Sum.java b/Java/Subarray Sum.java old mode 100644 new mode 100755 index ab42c31..3d5e2fd --- a/Java/Subarray Sum.java +++ b/Java/Subarray Sum.java @@ -1,5 +1,24 @@ +E +1529941153 +tags: Array, Hash Table, PreSum, Subarray +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + +``` /* -Given an integer array, find a subarray where the sum of numbers is zero. Your code should return the index of the first number and the index of the last number. +Given an integer array, find a subarray where the sum of numbers is zero. +Your code should return the index of the first number and the index of the last number. Example Given [-3, 1, 2, -3, 4], return [0, 2] or [1, 3]. @@ -9,39 +28,40 @@ Tags Expand Subarray Hash Table - -Thougths: -Record the sum from (0 ~ a). -Check sum on each index i, when found an existing sum in the hashMap, we are done. -Reason: -If adding all the numbers together, for example if sum[0 ~ a] = -3, ... sum[0 - b] = -3 again, a subarraySum(int[] nums) { - ArrayList rst = new ArrayList(); + public List subarraySum(int[] nums) { + List rst = new ArrayList<>(); if (nums == null || nums.length == 0) { return rst; } - int sum = 0; - HashMap map = new HashMap(); - map.put(0, -1); + int preSum = 0; + Map map = new HashMap<>(); + map.put(preSum, -1); //we know that sub-array (a,b) has zero sum if SUM(0 ... a-1) = SUM(0 ... b) for (int i = 0; i < nums.length; i++) { - sum += nums[i]; - if (map.containsKey(sum)) { - rst.add(map.get(sum) + 1); + preSum += nums[i]; + if (map.containsKey(preSum)) { + rst.add(map.get(preSum) + 1); rst.add(i); return rst; } - map.put(sum, i); - }//for + map.put(preSum, i); + } return rst; } } + + +/* +Thougths: +Record the sum from (0 ~ a). +Check sum on each index i, when found an existing sum in the hashMap, we are done. +Reason: +If adding all the numbers together, for example if sum[0 ~ a] = -3, ... sum[0 - b] = -3 again, aindex; if diff re-appears, that means sum of 0 has occurred +- sequence of calculation: 1. iterate over start row. 2. iterate over end row. 3. iterate over col number (this is where hashmap is stored based on) +- the iteration over col is like a screening: find previous sum and determine result +- Note: 其实并没有真的去找 `== 0` 的解答,而是根据特性来判断 `剩下的/后来加上的一定是0` + +``` +/* +Given an integer matrix, find a submatrix where the sum of numbers is zero. Your code should return the coordinate of the left-up and right-down number. + +Example: +[ + [1 ,5 ,7], + [3 ,7 ,-8], + [4 ,-8 ,9], +] +return [(1,1), (2,2)] + +Challenge +O(n^3) time. +*/ + + +class Solution { + public int[][] submatrixSum(int[][] nums) { + int[][] rst = new int[2][2]; + // check input + if (validateInput(nums)) { + return rst; + } + + int m = nums.length, n = nums[0].length; + + // calculate presum + int[][] preSum = new int[m + 1][n + 1]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + preSum[i+1][j+1] = preSum[i][j+1] + preSum[i+1][j] + nums[i][j] - preSum[i][j]; + } + } + + // iterations + for (int start = 0; start < m; start++) { + for (int end = start + 1; end <= m; end++) { + Map map = new HashMap<>(); + for (int col = 0; col <= n; col++) { + int diff = preSum[end][col] - preSum[start][col]; + if (map.containsKey(diff)) { + rst[0][0] = start; + rst[0][1] = map.get(diff); + rst[1][0] = end - 1; + rst[1][1] = col - 1; + return rst; + } + map.put(diff, col); + } + } + } + + return rst; + } + + private boolean validateInput(int[][] nums) { + if (nums == null || nums.length == 0 || nums[0] == null || nums[0].length == 0) { + return true; + } + return false; + } +} + + + +``` \ No newline at end of file diff --git a/Java/Subsets II.java b/Java/Subsets II.java old mode 100644 new mode 100755 index b73756c..6fda821 --- a/Java/Subsets II.java +++ b/Java/Subsets II.java @@ -1,14 +1,51 @@ -递归:找准需要pass along的几个数据结构。 +M +1531544611 +tags: Array, Backtracking, DFS, BFS +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + -Iterative: 写一写,用个Queue. ``` /* -Given a list of numbers that may has duplicate numbers, return all possible subsets +Given a collection of integers that might contain duplicates, nums, +return all possible subsets (the power set). -Have you met this question in a real interview? Yes -Example -If S = [1,2,2], a solution is: +Note: The solution set must not contain duplicate subsets. +Example: + +Input: [1,2,2] +Output: [ [2], [1], @@ -17,18 +54,174 @@ [1,2], [] ] -Note -Each element in a subset must be in non-descending order. -The ordering between two subsets is free. -The solution set must not contain duplicate subsets. -Challenge -Can you do it in both recursively and iteratively? - -Tags Expand -Recursion + + +*/ + +/* +Thoughts: +- have to sort the list, in order to skip items +- using the for loop approach: pick ONE item from the list at a time, with index i, then dfs with i + 1 +- skip case: if we've picked an item for once at (i - 1), then in this particular for loop, we should not pick the same item +- thought, important: it can be picked in the next level of dfs +- subset, either take or not take : 2^n space, 2^n time */ +class Solution { + public List> subsetsWithDup(int[] nums) { + List> result = new ArrayList<>(); + if (nums == null || nums.length == 0) return result; // edge case + + Arrays.sort(nums); + List list = new ArrayList<>(); + + // dfs with depth = 0 + result.add(new ArrayList<>(list)); + dfs(result, list, nums, 0); + return result; + } + + private void dfs(List> result, List list, int[] nums, int depth) { + for (int i = depth; i < nums.length; i++) { + if (i > depth && nums[i] == nums[i - 1]) continue; // IMPORTANT, skip duplicate: i > depth && nums[i] == nums[i - 1] + list.add(nums[i]); + result.add(new ArrayList<>(list)); + dfs(result, list, nums, i + 1); + list.remove(list.size() - 1); + } + } +} + +// BFS, Queue +class Solution { + public List> subsetsWithDup(int[] nums) { + List> result = new ArrayList<>(); + // edge, init result + if (nums == null || nums.length == 0) { + return result; + } + Arrays.sort(nums); + // init queue + Queue> queue = new LinkedList<>(); + List initialList = new ArrayList<>(); + queue.offer(initialList); + + // while queue not empty + while (!queue.isEmpty()) { + int size = queue.size(); + + while (size > 0) { + List indexRow = queue.poll(); + // add result + List list = new ArrayList<>(); + for (int index : indexRow) { + list.add(nums[index]); + } + result.add(list); + + // populate queue with index + int endIndex = indexRow.size() == 0 ? + 0 : indexRow.get(indexRow.size() - 1) + 1; + for (int i = endIndex; i < nums.length; i++) { + if (i > endIndex && nums[i] == nums[i - 1]) { // skip duplicated records + continue; + } + indexRow.add(i); + queue.offer(new ArrayList<>(indexRow)); + indexRow.remove(indexRow.size() - 1); + } + size--; + } + } + return result; + } +} + /* +Thoughts: +- Convert list to key, and validate before adding into the result +- to remove duplicates, make sure nums is sorted at beginning. +- Sort: O(nlogn), 2^n possible subsets => overall time, O(2^n) +- using extra space O(2^n) +*/ +class Solution { + Set set = new HashSet<>(); + public List> subsetsWithDup(int[] nums) { + List> result = new ArrayList<>(); + // edge case, init result + if (nums == null || nums.length == 0) { + return result; + } + Arrays.sort(nums); + List list = new ArrayList<>(); + + // dfs with depth = 0 + dfs(result, list, nums, 0); + return result; + } + + private void dfs(List> result, List list, int[] nums, int depth) { + // check closure case + if (depth >= nums.length) { + String key = list.toString(); // O(n), not optimal + if (!set.contains(key)) { + result.add(new ArrayList<>(list)); + set.add(key); + } + return; + } + + // pick + list.add(nums[depth]); + dfs(result, list, nums, depth + 1); + + // backtracking, and move to the not-pick option + list.remove(list.size() - 1); + dfs(result, list, nums, depth + 1); + } +} + +/* +03.10.2016 +Once sorted at beginning, same num are ajacent. +With for loop solution, we are jumping to next position in one dfs call, so this could happen: +skip current and take next. if current == next, that generates duplicates. +Image we've moved one step forward already, so we are not on given index, also current == prev: +that means previous position nums[i-1] must have been used. so we skip the current. +if (i != index && nums[i] == nums[i - 1]) { + continue; +} +*/ +public class Solution { + public List> subsetsWithDup(int[] nums) { + List> rst = new ArrayList>(); + if (nums == null || nums.length == 0) { + return rst; + } + + Arrays.sort(nums); + dfs(rst, new ArrayList(), 0, nums); + + return rst; + } + + public void dfs(List> rst, ArrayList list, int index, int[] nums) { + rst.add(new ArrayList(list)); + + for (int i = index; i < nums.length; i++) { + if (i != index && nums[i] == nums[i - 1]) { + continue; + } + list.add(nums[i]); + dfs(rst, list, i + 1, nums); + list.remove(list.size() - 1); + + } + } +} + +/* + Not recommended, Again, rst.contains() cost too much. Thoughts: 12.07.2015 try to do non-recursive - iterative @@ -40,10 +233,6 @@ */ class Solution { - /** - * @param S: A set of numbers. - * @return: A list of lists. All valid subsets. - */ public ArrayList> subsetsWithDup(ArrayList S) { ArrayList> rst = new ArrayList>(); if (S == null || S.size() == 0) { @@ -62,7 +251,7 @@ public ArrayList> subsetsWithDup(ArrayList S) { list = queue.poll(); //Pick list.add(num); - if (!rst.contains(list)) { + if (!rst.contains(list)) {//O(n) rst.add(new ArrayList(list)); } queue.offer(new ArrayList(list)); @@ -80,16 +269,13 @@ public ArrayList> subsetsWithDup(ArrayList S) { /* + Not recommended, Again, rst.contains() cost too much. Thoughts: 12.07.2015. Do regular subset recursion: pick curr or not pick curr, (rst, list, level, S) Use a HashMap to mark if the cmobination exists already Recursive. */ class Solution { - /** - * @param S: A set of numbers. - * @return: A list of lists. All valid subsets. - */ public ArrayList> subsetsWithDup(ArrayList S) { ArrayList> rst = new ArrayList>(); if (S == null || S.size() == 0) { @@ -104,7 +290,7 @@ public ArrayList> subsetsWithDup(ArrayList S) { public void helper(ArrayList> rst, ArrayList list, ArrayList S, int level) { - if (!rst.contains(list)) { + if (!rst.contains(list)) {//Costly, O(n) each time rst.add(new ArrayList(list)); } if (level == S.size()) { @@ -125,37 +311,31 @@ public void helper(ArrayList> rst, ArrayList list, -//Older version, with for loop: - - -class Solution { - /** - * @param S: A set of numbers. - * @return: A list of lists. All valid subsets. - */ - public ArrayList> subsetsWithDup(ArrayList source) { - // write your code here - ArrayList> output = new ArrayList>(); - ArrayList newList = new ArrayList(); - Collections.sort(source); - subsetHelper(0, source, newList, output); - return output; +//Older version, with for loop, use rst.contains()... which is very costly, O(n) each time +// Not recommended, Again, rst.contains() cost too much. +public class Solution { + public List> subsetsWithDup(int[] nums) { + List> rst = new ArrayList>(); + if (nums == null || nums.length == 0) { + return rst; + } + + Arrays.sort(nums); + dfs(rst, new ArrayList(), 0, nums); + + return rst; } - - public void subsetHelper(int pos, - ArrayList source, ArrayList newList, - ArrayList> output){ - if (!output.contains(newList)){ - output.add(new ArrayList(newList)); + public void dfs(List> rst, ArrayList list, int index, int[] nums) { + if (!rst.contains(list)) {//Costly, O(n) + rst.add(new ArrayList(list)); } - for (int i = pos; i < source.size(); i++){ - newList.add(source.get(i)); - subsetHelper(i + 1, source, newList, output); - newList.remove(newList.size() - 1); + for (int i = index; i < nums.length; i++) { + list.add(nums[i]); + dfs(rst, list, i + 1, nums); + list.remove(list.size() - 1); } - } } ``` \ No newline at end of file diff --git a/Java/Subsets.java b/Java/Subsets.java new file mode 100755 index 0000000..00b8dd2 --- /dev/null +++ b/Java/Subsets.java @@ -0,0 +1,275 @@ +M +1531543789 +tags: Array, Backtracking, DFS, BFS, Bit Manipulation +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + +``` +/* +Given a set of distinct integers, nums, return all possible subsets (the power set). + +Note: The solution set must not contain duplicate subsets. + +Example: + +Input: nums = [1,2,3] +Output: +[ + [3], + [1], + [2], + [1,2,3], + [1,3], + [2,3], + [1,2], + [] +] + + */ +// pick&&skip dfs, backtracking, +// bottom-up: reach leaf to save result +class Solution { + public List> subsets(int[] nums) { + List> result = new ArrayList<>(); + if (nums == null || nums.length == 0) return result; + + dfs(result, new ArrayList<>(), nums, 0); // dfs with depth = 0 + return result; + } + + private void dfs(List> result, List list, int[] nums, int depth) { + if (depth >= nums.length) { // closure case + result.add(new ArrayList<>(list)); + return; + } + // pick + list.add(nums[depth]); + dfs(result, list, nums, depth + 1); + + // backtracking, and move to the not-pick option + list.remove(list.size() - 1); + dfs(result, list, nums, depth + 1); + } +} + + +// for loop dfs: +// top-down, add each step as solution, as see fit +class Solution { + public List> subsets(int[] nums) { + List> result = new ArrayList<>(); + if (nums == null || nums.length == 0) return result; // edge case + + List list = new ArrayList<>(); + result.add(new ArrayList<>(list)); + + // dfs with depth = 0 + dfs(result, list, nums, 0); + return result; + } + + private void dfs(List> result, List list, int[] nums, int depth) { + for (int i = depth; i < nums.length; i++) { + list.add(nums[i]); + result.add(new ArrayList<>(list)); + dfs(result, list, nums, i + 1); + list.remove(list.size() - 1); + } + } +} + + +// Bit manipulation +class Solution { + public List> subsets(int[] nums) { + List> result = new ArrayList<>(); + // edge case + if (nums == null || nums.length == 0) { + return result; + } + // set bit range + int n = nums.length; + long maxRange = (long) Math.pow(2, n) - 1; + + // for loop for all integer representation of the bit map + for (int i = 0; i <= maxRange; i++) { + List list = new ArrayList<>(); + int index = 0; + int num = i; + // bit & each index to find if that index is picked + while (num != 0) { + if ((num & 1) == 1) { + list.add(nums[index]); + } + num = num >> 1; + index++; + } + result.add(list); + } + return result; + } +} + + +// BFS, Queue +class Solution { + public List> subsets(int[] nums) { + List> result = new ArrayList<>(); + // edge, init result + if (nums == null || nums.length == 0) { + return result; + } + // init queue + Queue> queue = new LinkedList<>(); + List initialList = new ArrayList<>(); + queue.offer(initialList); + + // while queue not empty + while (!queue.isEmpty()) { + int size = queue.size(); + + while (size > 0) { + List indexRow = queue.poll(); + // add result + List list = new ArrayList<>(); + for (int index : indexRow) { + list.add(nums[index]); + } + result.add(list); + + // populate queue with index + int endIndex = indexRow.size() == 0 ? + 0 : indexRow.get(indexRow.size() - 1) + 1; + for (int i = endIndex; i < nums.length; i++) { + indexRow.add(i); + queue.offer(new ArrayList<>(indexRow)); + indexRow.remove(indexRow.size() - 1); + } + size--; + } + } + return result; + } +} + + +/* +LintCode +Given a set of distinct integers, return all possible subsets. + +Example +If S = [1,2,3], a solution is: + +[ + [3], + [1], + [2], + [1,2,3], + [1,3], + [2,3], + [1,2], + [] +] +Note +Elements in a subset must be in non-descending order. +The solution set must not contain duplicate subsets. +Challenge +Can you do it in both recursively and iteratively? + +Tags Expand +Recursion Facebook Uber +*/ +/* +Previous Notes: + +最基本的递归题目。 +坑:记得一开头sort一下 nums。 因为要升序。那么整体就是O(nlogn) + + + */ +/* + 12.06.2015 recap. + Choose or not choose + When level == nums.length, return a entry. +*/ +class Solution { + public List> subsets(int[] nums) { + List> rst = new ArrayList>(); + if (nums == null || nums.length == 0) { + return rst; + } + Arrays.sort(nums); + ArrayList list = new ArrayList(); + helper(rst, list, nums, 0); + + return rst; + } + public void helper(List> rst, ArrayList list, + int[] nums, int level) { + if (level == nums.length) { + rst.add(new ArrayList(list)); + return; + } + //pick curr num + list.add(nums[level]); + helper(rst, list, nums, level + 1); + list.remove(list.size() - 1); + + //not pick curr num + helper(rst, list, nums, level + 1); + } +} + + +//Back tracking, DFS, recursive: recursive call of (rst, list, nums, index). +class Solution { + public List> subsets(int[] nums) { + List> rst = new ArrayList>(); + if (nums == null || nums.length == 0) { + return rst; + } + Arrays.sort(nums); + ArrayList list = new ArrayList(); + dfs(rst, list, nums, 0); + return rst; + } + + public void dfs(List> rst, ArrayList list, int[] nums, int index){ + rst.add(new ArrayList<>(list)); + + for( int i = index; i < nums.length; i++){ + list.add(nums[i]); + dfs(rst, list, nums, i+1); + list.remove(list.size() - 1); + } + } +} + +``` \ No newline at end of file diff --git a/Java/Subtree of Another Tree.java b/Java/Subtree of Another Tree.java new file mode 100644 index 0000000..fe77e42 --- /dev/null +++ b/Java/Subtree of Another Tree.java @@ -0,0 +1,89 @@ +E +1533279340 +tags: Tree, DFS, Divide and Conquer + +#### Tree +- Traverse tree: left, right +- Concept of partial compare vs. whole compare + +``` +/* +Given two non-empty binary trees s and t, check whether tree t has exactly +the same structure and node values with a subtree of s. + +A subtree of s is a tree consists of a node in s and all of this node's descendants. +The tree s could also be considered as a subtree of itself. + +Example 1: +Given tree s: + + 3 + / \ + 4 5 + / \ + 1 2 +Given tree t: + 4 + / \ + 1 2 +Return true, because t has the same structure and node values with a subtree of s. +Example 2: +Given tree s: + + 3 + / \ + 4 5 + / \ + 1 2 + / + 0 +Given tree t: + 4 + / \ + 1 2 +Return false. + */ + + + /** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +class Solution { + public boolean isSubtree(TreeNode s, TreeNode t) { + if (s == null || t == null) return s == null && t == null; + + return sameTree(s, t) || isSubtree(s.left, t) || isSubtree(s.right, t); + } + + private boolean sameTree(TreeNode s, TreeNode t) { + if (s == null || t == null) return s == null && t == null; + + return s.val == t.val && sameTree(s.left, t.left) && sameTree(s.right, t.right); + } +} + +/* +Thoughts: similar to compare identical trees. +Except: only start compare if s.val == t.val, otherwise, keep dfs. +*/ +class Solution { + public boolean isSubtree(TreeNode s, TreeNode t) { + if (s == null || t == null) return s == null && t == null; + + return (s.val == t.val && sameTree(s, t)) || isSubtree(s.left, t) || isSubtree(s.right, t); + } + + private boolean sameTree(TreeNode s, TreeNode t) { + if (s == null || t == null) return s == null && t == null; + + return s.val == t.val && sameTree(s.left, t.left) && sameTree(s.right, t.right); + } +} +``` \ No newline at end of file diff --git a/Java/Subtree.java b/Java/Subtree.java old mode 100644 new mode 100755 index f049e3b..858f04b --- a/Java/Subtree.java +++ b/Java/Subtree.java @@ -1,5 +1,77 @@ +E +1525671122 +tags: Tree, DFS + +给一个binary tree s, 和一个binary tree t, 检查t是不是s的subtree. + +#### DFS +- 跟 identical binary tree的写法很像 +- 只有 current s.val = t.val 的时候才需要compare same tree. +- 其他情况, 继续recursively isSubtree +- 注意:即使找到T1 == T2, 但很可能只是数字相同(这里不是binary search tree!!), 而children不同 +- 所以同时要继续recursively isSubtree(T1.left, T2) ...etc. + +``` +/** +Given two non-empty binary trees s and t, check whether tree t has exactly +the same structure and node values with a subtree of s. +A subtree of s is a tree consists of a node in s and all of this node's descendants. +The tree s could also be considered as a subtree of itself. + +Example 1: +Given tree s: + + 3 + / \ + 4 5 + / \ + 1 2 +Given tree t: + 4 + / \ + 1 2 +Return true, because t has the same structure and node values with a subtree of s. +Example 2: +Given tree s: + + 3 + / \ + 4 5 + / \ + 1 2 + / + 0 +Given tree t: + 4 + / \ + 1 2 +Return false. + */ +class Solution { + public boolean isSubtree(TreeNode s, TreeNode t) { + if (s == null || t == null) { + return s == null && t == null; + } + return (s.val == t.val && sameTree(s, t)) || isSubtree(s.left, t) || isSubtree(s.right, t); + } + + private boolean sameTree(TreeNode s, TreeNode t) { + if (s == null || t == null) { + return s == null && t == null; + } + return s.val == t.val && sameTree(s.left, t.left) && sameTree(s.right, t.right); + } +} + + + + + /* -You have two every large binary trees: T1, with millions of nodes, and T2, with hundreds of nodes. Create an algorithm to decide if T2 is a subtree of T1. +You have two every large binary trees: T1, with millions of nodes, +and T2, with hundreds of nodes. + +Create an algorithm to decide if T2 is a subtree of T1. Example T2 is a subtree of T1 in the following case: @@ -17,17 +89,14 @@ / 4 Note -A tree T2 is a subtree of T1 if there exists a node n in T1 such that the subtree of n is identical to T2. That is, if you cut off the tree at node n, the two trees would be identical. +A tree T2 is a subtree of T1 if there exists a node n in T1 +such that the subtree of n is identical to T2. + +That is, if you cut off the tree at node n, the two trees would be identical. Tags Expand Recursion Binary Tree -Thoughts: -When T2 == null, reardless of T1 == null or NO, it can always return true; -WHen T2 != null, T1==null returns false; -1. recursively compare the two nodes: if both null, okay; if everything goes well, get deeper into the child nodes. -2. resursively check subtree: check root.left or root.right comparing with T2. - */ /** @@ -40,6 +109,41 @@ * this.left = this.right = null; * } * } + */ + +/* +Thoughts: similar to compare identical trees. +Except: only start compare if s.val == t.val, otherwise, keep dfs. +*/ +class Solution { + public boolean isSubtree(TreeNode s, TreeNode t) { + if (s == null || t == null) { + return s == null && t == null; + } + boolean checkSubTree = false; + if (s.val == t.val) { + checkSubTree = sameTree(s, t); + } + return checkSubTree || isSubtree(s.left, t) || isSubtree(s.right, t); + } + + private boolean sameTree(TreeNode s, TreeNode t) { + if (s == null || t == null) { + return s == null && t == null; + } + return s.val == t.val && sameTree(s.left, t.left) && sameTree(s.right, t.right); + } +} + +/** +Previous notes +Thoughts: +When T2 == null, reardless of T1 == null or NO, it can always return true; +WHen T2 != null, T1==null returns false; +1. recursively compare the two nodes: if both null, okay; if everything goes well, get deeper into the child nodes. +2. resursively check subtree: check root.left or root.right comparing with T2. + + */ public class Solution { /** @@ -47,25 +151,60 @@ public class Solution { * @return: True if T2 is a subtree of T1, or false. */ public boolean isSubtree(TreeNode T1, TreeNode T2) { - if (T2 == null) { - return true; - } else if (T1 == null) { - return false; - } else { - return compare(T1, T2) || isSubtree(T1.left, T2) || isSubtree(T1.right, T2); - } + if (T2 == null) { + return true; + } else if (T1 == null) { + return false; + } else { + return compare(T1, T2) || isSubtree(T1.left, T2) || isSubtree(T1.right, T2); + } } - //Recursive compare + //Recursive compare public boolean compare(TreeNode node1, TreeNode node2) { - if (node1 == null && node2 == null) { - return true; - } - if (node1 == null || node2 == null){ - return false; - } - if (node1.val != node2.val) { - return false; - } - return compare(node1.left, node2.left) && compare(node1.right, node2.right); + if (node1 == null && node2 == null) { + return true; + } + if (node1 == null || node2 == null){ + return false; + } + if (node1.val != node2.val) { + return false; + } + return compare(node1.left, node2.left) && compare(node1.right, node2.right); } } + + +// 2.22 recap: Find T2 first, then do a divide and conquer compare +public class Solution { + /** + * @param T1, T2: The roots of binary tree. + * @return: True if T2 is a subtree of T1, or false. + */ + public boolean isSubtree(TreeNode T1, TreeNode T2) { + if (T1 == null || T2 == null) { + return T2 == null; + } + + if (T1.val == T2.val) { + return compare(T1, T2) || isSubtree(T1.left, T2) || isSubtree(T1.right, T2); + } else { + return isSubtree(T1.left, T2) || isSubtree(T1.right, T2); + } + } + + public boolean compare(TreeNode T1, TreeNode T2) { + if (T1 == null && T2 == null) { + return true; + } else if (T1 == null || T2 == null) { + return false; + } + if (T1.val != T2.val) { + return false; + } + + return compare(T1.left, T2.left) && compare(T1.right, T2.right); + } +} + +``` \ No newline at end of file diff --git a/Java/Sum of Two Integers.java b/Java/Sum of Two Integers.java new file mode 100755 index 0000000..650de5e --- /dev/null +++ b/Java/Sum of Two Integers.java @@ -0,0 +1,92 @@ +E +1517979387 +tags: Bit Manipulation + +a^b 是: 不完全加法. +a&b 是: 所有可能的进位. a&b<<1是向左边进位的形态. + +Goal: 先a^b裸加, 算出进位; 再把结果和进位裸加, 再算出这一轮的进位; 再..裸价, 算进位....直到进位数==0. + +那么就,首先记录好进位的数字:carry. 然后 a^b 不完全加法一次。然后b用来放剩下的carry, 每次移动一位,继续加,知道b循环为0为止。 + +在第一回 a ^ b 之后, b 的本身意义就消失. 接下去应该给parameter重新命名. +sum = a ^ b; // sum without adding carries +nextCarry = (a & b) << 1; + +用其他variable name 取代 a, b 会更好理解一点. + +Bit Operation +Steps: + a & b: 每bit可能出现的进位数 + a ^ b: 每bit在此次操作可能留下的值,XOR 操作 + 每次左移余数1位,然后存到b, 再去跟a做第一步。loop until b == 0 + +(http://www.meetqun.com/thread-6580-1-1.html) + +``` +/* +Also on LeetCode: Sum of Two Integers. https://leetcode.com/problems/sum-of-two-integers/ +Write a function that add two numbers A and B. You should not use + or any arithmetic operators. + +Example +Given a=1 and b=2 return 3 + +Note +There is no need to read data from standard input stream. Both parameters are given in function aplusb, you job is to calculate the sum and return it. + +Challenge +Of course you can just return a + b to get accepted. But Can you challenge not do it like that? + +Clarification +Are a and b both 32-bit integers? + +Yes. +Can I use bit operation? + +Sure you can. +Tags Expand +Cracking The Coding Interview Bit Manipulation + + +*/ + +/* +Thoughts: +Wring down truth table for a and b: +a + b on each bit becomes OR operation, exception when they are both 1, where they becomes 0 and add forward to next bit. +- We can use a carryOver +- Use long to hold the result +*/ +class Solution { + public int getSum(int a, int b) { + int sum = a ^ b; // incomplete sum + int nextCarry = (a & b) << 1; + while (nextCarry != 0) { + int currentCarry = sum & nextCarry; + sum = sum ^ nextCarry; + nextCarry = currentCarry << 1; + } + return sum; + } +} + +/* +Thought: + Bit operation. Just to remmeber this problem, doing A+B using bit. +*/ +class Solution { + public int aplusb(int a, int b) { + int numA = a; + int numB = b; + int incompleteSum = 0; + while (numB != 0) { + incompleteSum = numA ^ numB; + numB = (numA & numB) << 1; // carry + numA = incompleteSum; + } + return incompleteSum; // sum completes when the carry in numB is finished. + } +}; + + +``` \ No newline at end of file diff --git a/Java/Summary Ranges.java b/Java/Summary Ranges.java new file mode 100755 index 0000000..89a9a06 --- /dev/null +++ b/Java/Summary Ranges.java @@ -0,0 +1,49 @@ +M +1528247314 +tags: Array + +给一串sorted list, 中间有缺数字, return 所有数字的range string (example 看题目) + +#### Basic implementation +- 用一个list as the buffer to store candidates +- when: 1. end of nums; 2. not continuous integer => convert list to result + +``` +/* +Given a sorted integer array without duplicates, return the summary of its ranges. + +For example, given [0,1,2,4,5,7], return ["0->2","4->5","7"]. + +Tags: Array +Similar Problems: (M) Missing Ranges + + +*/ +/* +Thoughts: basic implementation, use a arraylist to catch candidates. +Detect condition, and return results. +*/ +public class Solution { + public List summaryRanges(int[] nums) { + List rst = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return rst; + } + List list = new ArrayList<>(); + for (int i = 0; i < nums.length; i++) { + list.add(nums[i]); + if (i + 1 == nums.length || nums[i] + 1 != nums[i + 1]) { + if (list.size() == 1) { + rst.add(list.get(0) + ""); + } else { + rst.add(list.get(0) + "->" + list.get(list.size() - 1)); + } + list = new ArrayList(); + } + } + return rst; + } +} + +//O(n) +``` \ No newline at end of file diff --git a/Java/Surrounded Regions.java b/Java/Surrounded Regions.java new file mode 100755 index 0000000..7cdaeb6 --- /dev/null +++ b/Java/Surrounded Regions.java @@ -0,0 +1,191 @@ +M +1520479490 +tags: DFS, BFS, Union Find, Matrix DFS + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + +``` +/* +Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. + +A region is captured by flipping all 'O's into 'X's in that surrounded region. + +For example, +X X X X +X O O X +X X O X +X O X X +After running your function, the board should be: + +X X X X +X X X X +X X X X +X O X X +Hide Tags Breadth-first Search + +*/ + +// DFS +public class Solution { + private int[] dx = {1, -1, 0, 0}; + private int[] dy = {0, 0, 1, -1}; + + public void solve(char[][] board) { + if (board == null || board.length == 0) { + return; + } + int row = board.length; + int col = board[0].length; + //Check the board + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (i == 0 || i == row - 1 || j == 0 || j == col - 1) { + fill(board, i , j); + } + } + } + char mark = 'M', candidate = 'O', goal = 'X'; + //Replacement + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (board[i][j] == candidate) { + board[i][j] = goal; + } + if (board[i][j] == mark) { + board[i][j] = candidate; + } + } + } + } + + //DFS to traverse all border nodes and their connected nodes + public void fill(char[][] board, int x, int y) { + int row = board.length, col = board[0].length; + if (x < 0 || x >= row || y < 0 || y >= col || board[x][y] != 'O') { + return; + } + board[x][y] = 'M'; + for (int i = 0; i < dx.length; i++) { + fill(board, x + dx[i], y + dy[i]); + } + } +} + + +/* +Thoughts: +The only where it can't be surrounded is when the O is on edge; also, same to any connecting O with it. +1. Find all edging O and union all the neighbors, mark them differently +2. Assign all other positiions to X if not alreay is. +3. Flip marked positions back to O + +Use union the 4 directions of O to mark them +*/ +class Solution { + public void solve(char[][] board) { + if (board == null || board.length == 0 || board[0] == null || board[0].length == 0) { + return; + } + int m = board.length; + int n = board[0].length; + UnionFind unionFind = new UnionFind(m * n + 1); + int root = m * n; + int[] dx = {0, 0, 1, -1}; + int[] dy = {1, -1, 0, 0}; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == 'X') { + continue; + } + int curr = i * n + j; + // Merging the edge with the virtual root position + if (i == 0 || j == 0 || i == m - 1 || j == n - 1) { + unionFind.union(curr, root); + continue; + } + // If edge is 'O', union them. + for (int k = 0; k < dx.length; k++) { + int x = i + dx[k]; + int y = j + dy[k]; + + if (x >= 0 && x < m && y >= 0 && y < n && board[x][y] == 'O') { + unionFind.union(curr, x * n + y); + } + } + } + } + + // Assign 'X' + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (board[i][j] == 'O' && unionFind.find(i * n + j) != root) { + board[i][j] = 'X'; + } + } + } + } +} + +class UnionFind { + int[] father; + int[] rank; + public UnionFind(int x) { + father = new int[x]; + rank = new int[x]; + for (int i = 0; i < x; i++) { + father[i] = i; + } + rank[x - 1] = x; + } + + public void union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootX == rootY) { + return; + } + if (rank[rootX] < rank[rootY]) { + father[rootX] = rootY; + } else { + if (rank[rootX] == rank[rootY]) { + rank[rootX]++; + } + father[rootY] = rootX; + } + } + + public int find(int x) { + if (father[x] == x) { + return x; + } + return father[x] = find(father[x]); + } +} + + +``` \ No newline at end of file diff --git a/Java/Swap Bits.java b/Java/Swap Bits.java new file mode 100644 index 0000000..7fa5025 --- /dev/null +++ b/Java/Swap Bits.java @@ -0,0 +1,45 @@ +E +1517982439 +tags: Bit Manipulation + +简单, 但是很多知识点: +1. Hex 0xaaaaaaaa 是1010101....1010; 0x55555555 是01010101....0101 +2. 可以用这两个hex取单数和负数. 如果需要取其他的pattern, 也可以做. +3. x很可能是negative number, 所以right-shift 要用logic shift, >>> 避免leading负数补位. + +``` +/* +Write a program to swap odd and even bits in an integer with as few instructions as possible +(e.g., bit 0 and bit 1 are swapped, bit 2 and bit 3 are swapped, and so on). +*/ + +/* +Thoughts: +index '0' is even, so starting from even bits. +Goal: +Shift even bits << 1, shift odd bits >> 1. +We need to extract all of the odd bits and all of the even bits. + +Trick: +0xaaaaaaaa represents: 10101010101....1010 +Because 0xA: is number 10, and has binary of 1010. + +Similarly: +0x55555555 represents: 01010101010....0101 +Because 0x5: is number 5, and has binary of 0101 + +Therefore: +Get even bits: x & 0x55555555 +Get odd bits: x & 0xaaaaaaaa + +End: +Perform the shift and add up the two numbers together. + +Note: right-shift has to use the logic-shift >>> to resolve the negative number issue. +*/ +public class Solution { + public int swapOddEvenBits(int x) { + return ((x & 0xaaaaaaaa) >>> 1) + ((x & 0x55555555) << 1); + } +} +``` \ No newline at end of file diff --git a/Java/Swap Nodes in Pairs.java b/Java/Swap Nodes in Pairs.java old mode 100644 new mode 100755 index 2c3116b..cb18bd0 --- a/Java/Swap Nodes in Pairs.java +++ b/Java/Swap Nodes in Pairs.java @@ -1,5 +1,17 @@ -swap总是confuse. -画三个block, 1,2,3. 连线。 +M +1520834172 +tags: Linked List + +#### enumurate +基本原理, 写出来, 然后连线: +pre -> A -> B -> C -> ... +需要一个虚拟 preNode做起始node, 不然无法把后面的node换到开头. + +#### 注意 +用dummy = pre作为head前一格. +用 `pre.next == null && pre.next.next` 来check是否为NULL. +pre.next.next 保证了至少有一次swap. + ``` /* Swap Nodes in Pairs @@ -15,14 +27,6 @@ Tags Expand Linked List */ - -/* - Thoughts: - 1. swap - 2. move 2 steps, then swap again. - 3. becareful node.next == null, that's the end of list. no swapping. -*/ - /** * Definition for singly-linked list. * public class ListNode { @@ -31,30 +35,35 @@ * ListNode(int x) { val = x; } * } */ - -public class Solution { - /** - * @param head a ListNode - * @return a ListNode - */ +/* +Thoughts: +pre -> A -> B -> C -> ... +1. Link pre to B +2. Link A to C +3. Link B to A +4. move forward. +*/ +class Solution { public ListNode swapPairs(ListNode head) { - if (head == null) { - return head; - } - ListNode dummy = new ListNode(0); - dummy.next = head; - head = dummy; - while (head.next != null && head.next.next != null) { - ListNode n1 = head.next; - ListNode n2 = head.next.next; - - n1.next = n2.next; - n2.next = n1; - n1 = n2; - - head = head.next.next; - } - return dummy.next; + if (head == null || head.next == null) { + return head; + } + ListNode pre = new ListNode(-1); + pre.next = head; + ListNode dummy = pre; + + while (pre.next != null && pre.next.next != null) { + ListNode a = pre.next; + ListNode b = a.next; + + a.next = b.next; + b.next = a; + pre.next = b; + + // Move + pre = pre.next.next; + } + return dummy.next; } } diff --git a/Java/Symmetric Tree.java b/Java/Symmetric Tree.java new file mode 100755 index 0000000..5271933 --- /dev/null +++ b/Java/Symmetric Tree.java @@ -0,0 +1,123 @@ +E +1525669668 +tags: Tree, DFS, BFS + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + +``` +/* +Given a binary tree, check whether it is a mirror of itself (i.e., symmetric around its center). + +Example + 1 + / \ + 2 2 + / \ / \ +3 4 4 3 +is a symmetric binary tree. + + 1 + / \ + 2 2 + \ \ + 3 3 +is not a symmetric binary tree. + +Challenge +Can you solve it both recursively and iteratively? + +Tags Expand +Binary Tree +*/ + +/* + Thoughts: + verify that left and right tree are identical + A.val == B.val + check(A.left, B.left) + check(A.right, B.right) +*/ +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + +class Solution { + public boolean isSymmetric(TreeNode root) { + if (root == null) { + return true; + } + return dfs(root.left, root.right); + } + + public boolean dfs(TreeNode leftNode, TreeNode rightNode) { + if (leftNode == null || rightNode == null) { + return leftNode == null && rightNode == null; + } + return leftNode.val == rightNode.val && dfs(leftNode.left, rightNode.right) && dfs(leftNode.right, rightNode.left); + } +} + +//Non-recursive, iterative +/* + Thoughts: + Use 2 stack to hold the child that's needed to compare. + Have to use stack, otherwise, can't iterate through root node. +*/ + +public class Solution { + public boolean isSymmetric(TreeNode root) { + if (root == null) { + return true; + } + + Stack s1 = new Stack(); + Stack s2 = new Stack(); + s1.push(root.left); + s2.push(root.right); + while (!s1.isEmpty() && !s2.isEmpty()) { + TreeNode node1 = s1.pop(); + TreeNode node2 = s2.pop(); + if (node1 == null && node2 == null) { + continue; + } else if (node1 == null || node2 == null) { + return false; + } else if (node1.val != node2.val) { + return false; + } + s1.push(node1.left); + s2.push(node2.right); + s1.push(node1.right); + s2.push(node2.left); + } + + return true; + } +} + + + + + + + + +``` diff --git a/Java/Target Sum.java b/Java/Target Sum.java new file mode 100644 index 0000000..98e5bb2 --- /dev/null +++ b/Java/Target Sum.java @@ -0,0 +1,71 @@ +M +1531896045 +tags: DP, DFS + +// 如何想到从中间initialize + +``` +/* +You are given a list of non-negative integers, a1, a2, ..., an, and a target, S. +Now you have 2 symbols + and -. For each integer, you should choose one from + and - as its new symbol. + +Find out how many ways to assign symbols to make sum of integers equal to target S. + +Example 1: +Input: nums is [1, 1, 1, 1, 1], S is 3. +Output: 5 +Explanation: + +-1+1+1+1+1 = 3 ++1-1+1+1+1 = 3 ++1+1-1+1+1 = 3 ++1+1+1-1+1 = 3 ++1+1+1+1-1 = 3 + +There are 5 ways to assign symbols to make the sum of nums be target 3. +Note: +The length of the given array is positive and will not exceed 20. +The sum of elements in the given array will not exceed 1000. +Your output answer is guaranteed to be fitted in a 32-bit integer. +*/ + +/* +target S range [-1000, 1000] +dp[i][amount]: for the first i items, # of ways to sum up to certain amount +dp[i][j] = dp[i-1][j - nums[i-1]] + dp[i-1][j + nums[i-1]]; negative, or positive on item[i-1]. 加法原理. +int[][] dp = new int[n+1][S+1] +dp[0][anyamount] = 0; +dp[1][num[0]] = 1; + +// set up Math.abs(S); if negative, the # of ways will be the same for the absolute value. +goal: dp[n][S] +*/ +class Solution { + public int findTargetSumWays(int[] nums, int S) { + if (nums == null || nums.length == 0) return 0; + + int sum = 0; + for (int num : nums) sum += num; + if (sum < Math.abs(S)) return 0; + + // create dp, and init + int n = nums.length, m = (sum << 1) + 1; + int[][] dp = new int[n][m]; + dp[0][sum - nums[0]] += 1; + dp[0][sum + nums[0]] += 1; + + for (int i = 1; i < n; i++) { + for (int j = 0; j < m; j++) { + if (j - nums[i] >= 0) { + dp[i][j] += dp[i - 1][j - nums[i]]; + } + if (j + nums[i] < m) { + dp[i][j] += dp[i - 1][j + nums[i]]; + } + } + } + + return dp[n - 1][S + sum]; + } +} +``` \ No newline at end of file diff --git a/Java/Task Scheduler.java b/Java/Task Scheduler.java new file mode 100644 index 0000000..e19ce02 --- /dev/null +++ b/Java/Task Scheduler.java @@ -0,0 +1,109 @@ +M +1531202308 +tags: Array, Greedy, Enumeration, Queue, PriorityQueue + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + +``` +/* +Given a char array representing tasks CPU need to do. +It contains capital letters A to Z where different letters represent different tasks. +Tasks could be done without original order. Each task could be done in one interval. +For each interval, CPU could finish one task or just be idle. + +However, there is a non-negative cooling interval n that means between two same tasks, +there must be at least n intervals that CPU are doing different tasks or just be idle. + +You need to return the least number of intervals the CPU will take to finish all the given tasks. + +Example 1: +Input: tasks = ["A","A","A","B","B","B"], n = 2 +Output: 8 +Explanation: A -> B -> idle -> A -> B -> idle -> A -> B. +Note: +The number of tasks is in the range [1, 10000]. +The integer n is in the range [0, 100]. + +*/ + +class Solution { + public int leastInterval(char[] tasks, int n) { + if (tasks == null) return 0; + + int[] count = new int[26]; + for (char c : tasks) count[c - 'A']++; + Arrays.sort(count); // ascending + + int max = count[25], i = 25; + while (i >= 0 && count[i] == max) i--; + int countRepeatedTopTask = 25 - i; + + return Math.max(tasks.length, (max - 1) * (n + 1) + countRepeatedTopTask); + } +} + + +// Priority Queue +class Solution { + class Task { + public int count, c; + public Task(int c, int count) { + this.c = c; + this.count = count; + } + } + public int leastInterval(char[] tasks, int n) { + if (tasks == null) return 0; + + // Prepare priority queue of Task + PriorityQueue pq = new PriorityQueue<>((a, b) -> a.count != b.count ? b.count - a.count : a.c - b.c); + int[] taskCount = new int[26]; + for (char c : tasks) taskCount[c - 'A']++; + for (int i = 0; i < taskCount.length; i++) { + if (taskCount[i] > 0) pq.offer(new Task(i, taskCount[i])); + } + + // process queue and count for each section, where k = n + 1 slots + int count = 0; + while (!pq.isEmpty()) { + int k = n + 1; // slots in on section + Set tempSet = new HashSet<>(); + while (k > 0 && !pq.isEmpty()) { + Task task = pq.poll(); + if (task.count > 1) { + task.count = task.count - 1; + tempSet.add(task); + } + count++; + k--; + } + + pq.addAll(tempSet); // add valid tasks back to pq + if (pq.isEmpty()) break; + count += k; // if k > 0, the sections are all filled + } + return count; + } +} + +``` \ No newline at end of file diff --git a/Java/Text Justification.java b/Java/Text Justification.java new file mode 100644 index 0000000..4d63c92 --- /dev/null +++ b/Java/Text Justification.java @@ -0,0 +1,142 @@ +H +1533529849 +tags: String, Enumeration + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- Summing space = `width + (size-1)`. maintain: 1. list of candidates, 2. width of actual words +- calculate space in between: `remain/(size - 1)` +- overall for loop; clean up list: 1. over size; 2. last item +- 一点也不难, 但是要小心: deal with list of string的时候, 注意处理干净sum size of list, 就行了. +- `干净处理space`: 只处理 (n-1) items, 然后最后一个拿到for loop 外面, 特殊处理. + +#### Notes +- Clarification, observation: +- can start with greedy approach to stack as many words as possible +- once exceed the length, pop the top, and justify the added words (untouched words tracked by index) +- left justify: given list/stack of words with size t, overall remaining space length m, +- deal with last line with special care: just fill one space, and fill the rest of the row with space +- Does not seem very complicated, but need additional care of calculating the amount of space needed. +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + +``` +/* +Given an array of words and a width maxWidth, format the text such that each line has exactly maxWidth characters and is fully (left and right) justified. + +You should pack your words in a greedy approach; that is, pack as many words as you can in each line. Pad extra spaces ' ' when necessary so that each line has exactly maxWidth characters. + +Extra spaces between words should be distributed as evenly as possible. If the number of spaces on a line do not divide evenly between words, the empty slots on the left will be assigned more spaces than the slots on the right. + +For the last line of text, it should be left justified and no extra space is inserted between words. + +Note: + +A word is defined as a character sequence consisting of non-space characters only. +Each word's length is guaranteed to be greater than 0 and not exceed maxWidth. +The input array words contains at least one word. +Example 1: + +Input: +words = ["This", "is", "an", "example", "of", "text", "justification."] +maxWidth = 16 +Output: +[ + "This is an", + "example of text", + "justification. " +] +Example 2: + +Input: +words = ["What","must","be","acknowledgment","shall","be"] +maxWidth = 16 +Output: +[ + "What must be", + "acknowledgment ", + "shall be " +] +Explanation: Note that the last line is "shall be " instead of "shall be", + because the last line must be left-justified instead of fully-justified. + Note that the second line is also left-justified becase it contains only one word. +Example 3: + +Input: +words = ["Science","is","what","we","understand","well","enough","to","explain", + "to","a","computer.","Art","is","everything","else","we","do"] +maxWidth = 20 +Output: +[ + "Science is what we", + "understand well", + "enough to explain to", + "a computer. Art is", + "everything else we", + "do " +] +*/ + +class Solution { + public List fullJustify(String[] words, int maxWidth) { + List list = new ArrayList<>(); + List rst = new ArrayList<>(); + int width = 0; + for (String s : words) { + // when size reach maxWidth, comebine list[s] into s + if (getRowLength(list, width) + s.length() + 1 > maxWidth) { + rst.add(aggregate(list, width, maxWidth)); + list = new ArrayList<>(); + width = 0; + } + list.add(s); + width += s.length(); + } + + if (list.size() == 0) return rst; + + // at end, clean up last row + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < list.size() - 1; i++) { + sb.append(list.get(i) + " "); + } + sb.append(list.get(list.size() - 1)); + sb.append(generateSpace(maxWidth - sb.length())); + rst.add(sb.toString()); + return rst; + } + + private int getRowLength(List list, int width) { + return width + list.size() - 1; // overall width + (n - 1) spaces + } + + private String aggregate(List list, int width, int max) { + int slot = list.size() - 1, diff = max - width; + if (slot == 0) return (list.get(0) + generateSpace(diff)); + + int length = diff / (slot); + int remain = diff % slot; // less than list.size() - 1 + String space = generateSpace(length); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < slot; i++) { + sb.append(list.get(i) + space); + if (i < remain) sb.append(" "); + } + sb.append(list.get(slot)); + sb.append(generateSpace(max - sb.length())); + return sb.toString(); + } + + private String generateSpace(int x) { + if (x <= 0) return ""; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < x; i++) { + sb.append(" "); + } + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/The Maze II.java b/Java/The Maze II.java new file mode 100644 index 0000000..4b454c8 --- /dev/null +++ b/Java/The Maze II.java @@ -0,0 +1,166 @@ +M +1534299081 +tags: DFS, BFS, PriorityQueue + +#### BFS +- if already found a good/shorter route, skip +- `if (distMap[node.x][node.y] <= node.dist) continue;` +- This always terminates the possibility to go return to original route, because the dist will be double/higher + +``` +/* +There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling up, down, left or right, but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction. + +Given the ball's start position, the destination and the maze, find the shortest distance for the ball to stop at the destination. The distance is defined by the number of empty spaces traveled by the ball from the start position (excluded) to the destination (included). If the ball cannot stop at the destination, return -1. + +The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The start and destination coordinates are represented by row and column indexes. + +Example 1 + +Input 1: a maze represented by a 2D array + +0 0 1 0 0 +0 0 0 0 0 +0 0 0 1 0 +1 1 0 1 1 +0 0 0 0 0 + +Input 2: start coordinate (rowStart, colStart) = (0, 4) +Input 3: destination coordinate (rowDest, colDest) = (4, 4) + +Output: 12 +Explanation: One shortest way is : left -> down -> left -> down -> right -> down -> right. + The total distance is 1 + 1 + 3 + 1 + 2 + 2 + 2 = 12. + +Example 2 + +Input 1: a maze represented by a 2D array + +0 0 1 0 0 +0 0 0 0 0 +0 0 0 1 0 +1 1 0 1 1 +0 0 0 0 0 + +Input 2: start coordinate (rowStart, colStart) = (0, 4) +Input 3: destination coordinate (rowDest, colDest) = (3, 2) + +Output: -1 +Explanation: There is no way for the ball to stop at the destination. + +Note: +There is only one ball and one destination in the maze. +Both the ball and the destination exist on an empty space, and they will not be at the same position initially. +The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls. +The maze contains at least 2 empty spaces, and both the width and height of the maze won't exceed 100 +*/ +// BFS with int[][] distMap +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + public int shortestDistance(int[][] maze, int[] start, int[] dest) { + int m = maze.length, n = maze[0].length; + int[][] distMap = new int[m][n]; + for (int i = 0; i < n * m; i++) { + distMap[i/n][i%n] = Integer.MAX_VALUE; + } + //PriorityQueue pq = new PriorityQueue<>(Comparator.comparing(node -> node.dist)); // a bit slow + PriorityQueue pq = new PriorityQueue<>(new Comparator() { + public int compare(Node a, Node b) { + return a.dist - b.dist; + } + }); + pq.offer(new Node(start[0], start[1], 0)); + + while (!pq.isEmpty()) { + int size = pq.size(); + for (int i = 0; i < size; i++) { + Node node = pq.poll(); + if (distMap[node.x][node.y] <= node.dist) continue; // already found a good/shorter route + distMap[node.x][node.y] = node.dist; + for (int j = 0; j < 4; j++) { + int x = node.x, y = node.y; + while (x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == 0) { + x+=dx[j]; + y+=dy[j]; + } + x-=dx[j]; + y-=dy[j]; + int dist = Math.abs(node.x - x) + Math.abs(node.y - y); + pq.offer(new Node(x, y, node.dist + dist)); + } + } + } + + if (distMap[dest[0]][dest[1]]!= Integer.MAX_VALUE) return distMap[dest[0]][dest[1]]; + return -1; + } + + class Node { + int x, y, dist; + public Node(int x, int y, int dist) { + this.x = x; + this.y = y; + this.dist = dist; + } + } +} + +// BFS with Map<'x@y', Int> distMap +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + public int shortestDistance(int[][] maze, int[] start, int[] dest) { + int n = maze.length, m = maze[0].length; + Map distMap = new HashMap<>(); + for (int i = 0; i < n * m; i++) { + distMap.put(getKey(i/n, i%n), Integer.MAX_VALUE); + } + PriorityQueue pq = new PriorityQueue<>(Comparator.comparing(node -> node.dist)); + pq.offer(new Node(start[0], start[1], 0)); + + while (!pq.isEmpty()) { + int size = pq.size(); + for (int i = 0; i < size; i++) { + Node node = pq.poll(); + String key = getKey(node.x, node.y); + if (distMap.containsKey(key) && distMap.get(key) <= node.dist) continue; // already found a good/shorter route + distMap.put(key, node.dist); + for (int j = 0; j < 4; j++) { + int x = node.x, y = node.y, dist = node.dist; + while (x >= 0 && x < n && y >= 0 && y < m && maze[x][y] == 0) { + x+=dx[j]; + y+=dy[j]; + dist++; + } + x-=dx[j]; + y-=dy[j]; + dist--; + pq.offer(new Node(x, y, dist)); + } + } + } + + String key = getKey(dest[0], dest[1]); + if (distMap.containsKey(key) && distMap.get(key) != Integer.MAX_VALUE) return distMap.get(key); + return -1; + } + + private boolean verify(Node node, int[] dest) { + return node.x == dest[0] && node.y == dest[1]; + } + + private String getKey(int x, int y){ + return x + "@" + y; + } + + class Node { + int x, y, dist; + public Node(int x, int y, int dist) { + this.x = x; + this.y = y; + this.dist = dist; + } + } +} +``` \ No newline at end of file diff --git a/Java/The Maze III.java b/Java/The Maze III.java new file mode 100644 index 0000000..ef39529 --- /dev/null +++ b/Java/The Maze III.java @@ -0,0 +1,128 @@ +H +1534304210 +tags: DFS, BFS, PriorityQueue + +#### BFS +- 跟 Maze I, II 类似, 用一个 Node[][] 来存每一个(x,y)的state. +- Different from traditional BFS(shortest path): `it terminates BFS when good solution exists (distance), but will finish all possible routes` +- 1. `Termination condition`: if we already have a good/better solution on nodeMap[x][y], no need to add a new one +- 2. Always cache the node if passed the test in step1 +- 3. Always offer the moved position as a new node to queue (as long as it fits condition) +- 4. Finally the item at nodeMap[target.x][target.y] will have the best solution. + +``` +/* +There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling up (u), down (d), left (l) or right (r), but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction. There is also a hole in this maze. The ball will drop into the hole if it rolls on to the hole. + +Given the ball position, the hole position and the maze, find out how the ball could drop into the hole by moving the shortest distance. The distance is defined by the number of empty spaces traveled by the ball from the start position (excluded) to the hole (included). Output the moving directions by using 'u', 'd', 'l' and 'r'. Since there could be several different shortest ways, you should output the lexicographically smallest way. If the ball cannot reach the hole, output "impossible". + +The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The ball and the hole coordinates are represented by row and column indexes. + +Example 1 + +Input 1: a maze represented by a 2D array + +0 0 0 0 0 +1 1 0 0 1 +0 0 0 0 0 +0 1 0 0 1 +0 1 0 0 0 + +Input 2: ball coordinate (rowBall, colBall) = (4, 3) +Input 3: hole coordinate (rowHole, colHole) = (0, 1) + +Output: "lul" +Explanation: There are two shortest ways for the ball to drop into the hole. +The first way is left -> up -> left, represented by "lul". +The second way is up -> left, represented by 'ul'. +Both ways have shortest distance 6, but the first way is lexicographically smaller because 'l' < 'u'. So the output is "lul". + +Example 2 + +Input 1: a maze represented by a 2D array + +0 0 0 0 0 +1 1 0 0 1 +0 0 0 0 0 +0 1 0 0 1 +0 1 0 0 0 + +Input 2: ball coordinate (rowBall, colBall) = (4, 3) +Input 3: hole coordinate (rowHole, colHole) = (3, 0) +Output: "impossible" +Explanation: The ball cannot reach the hole. + +Note: +There is only one ball and one hole in the maze. +Both the ball and hole exist on an empty space, and they will not be at the same position initially. +The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls. +The maze contains at least 2 empty spaces, and the width and the height of the maze won't exceed 30. + +*/ + +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + String[] ds = {"d", "u", "r", "l"}; + public String findShortestWay(int[][] maze, int[] ball, int[] hole) { + int m = maze.length, n = maze[0].length; + // init + Node[][] nodeMap = new Node[m][n]; + for (int i = 0; i < n * m; i++) nodeMap[i/n][i%n] = new Node(i/n, i%n); + PriorityQueue pq = new PriorityQueue<>(); + pq.offer(new Node(ball[0], ball[1], 0, "")); + + // BFS + while (!pq.isEmpty()) { + int size = pq.size(); + for (int i = 0; i < size; i++) { + Node node = pq.poll(); + if (nodeMap[node.x][node.y].compareTo(node) <= 0) continue; // already found a better node + nodeMap[node.x][node.y] = node; // cache node in nodeMap since it's a good candidate + for (int j = 0; j < 4; j++) { + int x = node.x, y = node.y, dist = node.dist; + while (x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == 0 && !isEnd(hole, x, y)) { + x+=dx[j]; + y+=dy[j]; + dist++; + } + if (!isEnd(hole, x, y)) { + x-=dx[j]; + y-=dy[j]; + dist--; + } + // always offer to queue, as long as we can reach + pq.offer(new Node(x, y, dist, node.path + ds[j])); + } + } + } + + if (nodeMap[hole[0]][hole[1]].dist != Integer.MAX_VALUE) return nodeMap[hole[0]][hole[1]].path; + return "impossible"; + } + + private boolean isEnd(int[] hole, int x, int y) { + return x == hole[0] && y == hole[1]; + } + + class Node implements Comparable{ + int x, y, dist; + String path; + public Node(int x, int y) { + this.x = x; + this.y = y; + this.dist = Integer.MAX_VALUE; + } + public Node(int x, int y, int dist, String path) { + this.x = x; + this.y = y; + this.dist = dist; + this.path = path; + } + public int compareTo(Node node) { + return dist == node.dist ? path.compareTo(node.path): dist - node.dist; + } + } +} + +``` \ No newline at end of file diff --git a/Java/The Maze.java b/Java/The Maze.java new file mode 100644 index 0000000..dba59c2 --- /dev/null +++ b/Java/The Maze.java @@ -0,0 +1,173 @@ +M +1534262484 +tags: DFS, BFS + +#### BFS +- BFS on coordinates +- always attempt to move to end of border +- use boolean[][] visited to alingn with BFS solution in Maze II, III, where it uses Node[][] to store state on each item. + +``` +/* +There is a ball in a maze with empty spaces and walls. The ball can go through empty spaces by rolling up, down, left or right, but it won't stop rolling until hitting a wall. When the ball stops, it could choose the next direction. + +Given the ball's start position, the destination and the maze, determine whether the ball could stop at the destination. + +The maze is represented by a binary 2D array. 1 means the wall and 0 means the empty space. You may assume that the borders of the maze are all walls. The start and destination coordinates are represented by row and column indexes. + +Example 1 + +Input 1: a maze represented by a 2D array + +0 0 1 0 0 +0 0 0 0 0 +0 0 0 1 0 +1 1 0 1 1 +0 0 0 0 0 + +Input 2: start coordinate (rowStart, colStart) = (0, 4) +Input 3: destination coordinate (rowDest, colDest) = (4, 4) + +Output: true +Explanation: One possible way is : left -> down -> left -> down -> right -> down -> right. + +Example 2 + +Input 1: a maze represented by a 2D array + +0 0 1 0 0 +0 0 0 0 0 +0 0 0 1 0 +1 1 0 1 1 +0 0 0 0 0 + +Input 2: start coordinate (rowStart, colStart) = (0, 4) +Input 3: destination coordinate (rowDest, colDest) = (3, 2) + +Output: false +Explanation: There is no way for the ball to stop at the destination. + +Note: +There is only one ball and one destination in the maze. +Both the ball and the destination exist on an empty space, and they will not be at the same position initially. +The given maze does not contain border (like the red rectangle in the example pictures), but you could assume the border of the maze are all walls. +The maze contains at least 2 empty spaces, and both the width and height of the maze won't exceed 100. + +*/ +// BFS with node +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + public boolean hasPath(int[][] maze, int[] start, int[] dest) { + int m = maze.length, n = maze[0].length; + boolean[][] visited = new boolean[m][n]; + Queue queue = new LinkedList<>(); + queue.offer(new Node(start[0], start[1])); + visited[start[0]][start[1]] = true; + + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + Node node = queue.poll(); + for (int j = 0; j < 4; j++) { + int x = node.x, y = node.y; + while (x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == 0) { + x+=dx[j]; + y+=dy[j]; + } + x-=dx[j]; + y-=dy[j]; + if (dest[0] == x && dest[1] == y) return true; + if (visited[x][y]) continue; + visited[x][y] = true; + queue.offer(new Node(x, y)); + } + } + } + return false; + } + + class Node { + int x, y; + public Node(int x, int y) { + this.x = x; + this.y = y; + } + } +} + +// BFS with object[][] +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + public boolean hasPath(int[][] maze, int[] start, int[] dest) { + int m = maze.length, n = maze[0].length; + boolean[][] visited = new boolean[m][n]; + Queue queue = new LinkedList<>(); + queue.offer(start); + visited[start[0]][start[1]] = true; + + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + int[] pos = queue.poll(); + for (int j = 0; j < 4; j++) { + int x = pos[0], y = pos[1]; + while (x >= 0 && x < m && y >= 0 && y < n && maze[x][y] == 0) { + x+=dx[j]; + y+=dy[j]; + } + x-=dx[j]; + y-=dy[j]; + if (dest[0] == x && dest[1] == y) return true; + if (visited[x][y]) continue; + visited[x][y] = true; + queue.offer(new int[] {x, y}); + } + } + } + return false; + } +} + +// BFS +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + public boolean hasPath(int[][] maze, int[] start, int[] dest) { + int n = maze.length; + int m = maze[0].length; + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + queue.offer(start); + visited.add(start[0] + "@" + start[1]); + + while (!queue.isEmpty()) { + int size = queue.size(); + for (int i = 0; i < size; i++) { + int[] pos = queue.poll(); + if (verify(pos, dest)) return true; + for (int j = 0; j < 4; j++) { + int x = pos[0], y = pos[1]; + while (x >= 0 && x < n && y >= 0 && y < m && maze[x][y] == 0) { + x+=dx[j]; + y+=dy[j]; + } + x-=dx[j]; + y-=dy[j]; + String key = x + "@" + y; + if (!visited.contains(key)) { + visited.add(key); + queue.offer(new int[] {x, y}); + } + } + } + } + return false; + } + + private boolean verify(int[] pos, int[] dest) { + return pos[0] == dest[0] && pos[1] == dest[1]; + } +} +``` \ No newline at end of file diff --git a/Java/The Skyline Problem.java b/Java/The Skyline Problem.java new file mode 100755 index 0000000..74d7d07 --- /dev/null +++ b/Java/The Skyline Problem.java @@ -0,0 +1,439 @@ +R +1521176156 +tags: Divide and Conquer, Heap, PriorityQueue, Binary Indexed Tree, Segment Tree, Sweep Line + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + +``` +/* +LeetCode: +A city's skyline is the outer contour of the silhouette formed by all the buildings in that city +when viewed from a distance. Now suppose you are given the locations and height of all the buildings +as shown on a cityscape photo (Figure A), write a program to output the skyline formed by these buildings +collectively (Figure B). + +The geometric information of each building is represented by a triplet of integers [Li, Ri, Hi], +where Li and Ri are the x coordinates of the left and right edge of the ith building, respectively, +and Hi is its height. It is guaranteed that 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAX, and Ri - Li > 0. +You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0. + +For instance, the dimensions of all buildings in Figure A are recorded as: +[ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] . + +The output is a list of "key points" (red dots in Figure B) in the format of +[ [x1,y1], [x2, y2], [x3, y3], ... ] that uniquely defines a skyline. +A key point is the left endpoint of a horizontal line segment. Note that the last key point, +where the rightmost building ends, is merely used to mark the termination of the skyline, +and always has zero height. Also, the ground in between any two adjacent buildings +should be considered part of the skyline contour. + +For instance, the skyline in Figure B should be represented as: +[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ]. + +Notes: + +The number of buildings in any input list is guaranteed to be in the range [0, 10000]. +The input list is already sorted in ascending order by the left x position Li. +The output list must be sorted by the x position. +There must be no consecutive horizontal lines of equal height in the output skyline. For instance, [...[2 3], [4 5], [7 5], [11 5], [12 7]...] is not acceptable; the three lines of height 5 should be merged into one in the final output as such: [...[2 3], [4 5], [12 7], ...] + +*/ + +/* +Thoughts: +Sweep Line. +1. Store the building start/end points into a Point object. +2. PriorityQueue on the Point: height low -> high && position lowX -> highX +3. Loop over PointQueue, and store the heights in a separate maxQueue +4. Based on the currPeak and prevPeak to detect diff: when there is a diff, there is a recording point. + Keeping add start height into the maxHeap, and remove end points whenever facing one. + +*/ +class Solution { + class Point { + int pos, height; + public Point(int pos, int height) { + this.pos = pos; + this.height = height; + } + } + public List getSkyline(int[][] buildings) { + List rst = new ArrayList<>(); + if (isInvalid(buildings)) return rst; + + int m = buildings.length; + // init: sort points(xPos, height, isStart:boolean) by height && xPos with queue + PriorityQueue queue = new PriorityQueue<>((a, b) -> a.pos == b.pos ? a.height - b.height : a.pos - b.pos); + for (int i = 0; i < m; i++) { + queue.offer(new Point(buildings[i][0], -buildings[i][2])); + queue.offer(new Point(buildings[i][1], buildings[i][2])); + } + + // Mark height and calcualte the outline point. + PriorityQueue maxHeightQueue = new PriorityQueue<>(Collections.reverseOrder()); + maxHeightQueue.offer(0); + int prevPeak = maxHeightQueue.peek(); + + // process + while (!queue.isEmpty()) { + Point point = queue.poll(); + if (point.height < 0) { + maxHeightQueue.offer(-point.height); + } else { // is end point of a building + maxHeightQueue.remove(point.height); + } + + int currPeak = maxHeightQueue.peek(); + if (currPeak != prevPeak) { + rst.add(new int[]{point.pos, currPeak}); + prevPeak = currPeak; + } + } + return rst; + } + + private boolean isInvalid(int[][] buildings) { + return buildings == null || buildings.length == 0 || buildings[0] == null || buildings[0].length == 0; + } +} + + +/* +LintCode description: +Given N buildings in a x-axis,each building is a rectangle and can be represented by a triple (start, end, height), +where start is the start position on x-axis, end is the end position on x-axis and height is the height of the building. +Buildings may overlap if you see them from far away,find the outline of them。 +An outline can be represented by a triple, (start, end, height), +where start is the start position on x-axis of the outline, +end is the end position on x-axis and height is the height of the outline. + +Example +Given 3 buildings: +[ + [1, 3, 3], + [2, 4, 4], + [5, 6, 1] +] +The outlines are: +[ + [1, 2, 3], + [2, 4, 4], + [5, 6, 1] +] +Note +Please merge the adjacent outlines if they have the same height and make sure different outlines cant overlap on x-axis. +Tags Expand +LintCode Copyright Heap +*/ + +/*LeetCode description +A city's skyline is the outer contour of the silhouette formed by all the buildings in that city when viewed from a distance. Now suppose you are given the locations and height of all the buildings as shown on a cityscape photo (Figure A), write a program to output the skyline formed by these buildings collectively (Figure B). + + Buildings Skyline Contour +The geometric information of each building is represented by a triplet of integers [Li, Ri, Hi], where Li and Ri are the x coordinates of the left and right edge of the ith building, respectively, and Hi is its height. It is guaranteed that 0 ≤ Li, Ri ≤ INT_MAX, 0 < Hi ≤ INT_MAX, and Ri - Li > 0. You may assume all buildings are perfect rectangles grounded on an absolutely flat surface at height 0. + +For instance, the dimensions of all buildings in Figure A are recorded as: [ [2 9 10], [3 7 15], [5 12 12], [15 20 10], [19 24 8] ] . + +The output is a list of "key points" (red dots in Figure B) in the format of [ [x1,y1], [x2, y2], [x3, y3], ... ] that uniquely defines a skyline. A key point is the left endpoint of a horizontal line segment. Note that the last key point, where the rightmost building ends, is merely used to mark the termination of the skyline, and always has zero height. Also, the ground in between any two adjacent buildings should be considered part of the skyline contour. + +For instance, the skyline in Figure B should be represented as:[ [2 10], [3 15], [7 12], [12 0], [15 10], [20 8], [24, 0] ]. + +Notes: + +The number of buildings in any input list is guaranteed to be in the range [0, 10000]. +The input list is already sorted in ascending order by the left x position Li. +The output list must be sorted by the x position. +There must be no consecutive horizontal lines of equal height in the output skyline. For instance, [...[2 3], [4 5], [7 5], [11 5], [12 7]...] is not acceptable; the three lines of height 5 should be merged into one in the final output as such: [...[2 3], [4 5], [12 7], ...] + +Hide Tags Divide and Conquer Binary Indexed Tree Heap Segment Tree + +*/ + +/* + Thoughts + Based idea here:http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt + Here is the thinking process, 3.15.2016 + + The class HeightPoint{int x, int height} is very similar to Sweep Line Point{int x, int flag}. However the usage is a bit different. + Sort all of the heightPoints by index && by height. + Use nagtive height for start point to make sure start appears before end point, tho they share same height + We use an extra priorityQueue to store the height being processed (note, this queue, decending order, max in front) + When having a negative height(start point), add into queue + At each point, find heightest point (common sense!) and mark it on map: the overlapping point at this index will be skipped because the rest are not high enough. + How to process the rest redundant point? + Here introduce a 'prev' variable that stores last processed value. If same height appears right before curr, don't add to result; it's added during this continuous line. + How to maintain the queue? + Once a height > 0 appears, remove that height from queue. + OKay, let's break it down: + because we sort HeightPoint object by index and height, start height will appear before end height of same building, for sure. + So whenever positive height appears, the same bulding must have been marked, so can safely remove the height instance from queue. + Well, in HeightPoint object, start height is negative for sorting purpose. When adding into queue, use it's absolute value : ) +*/ +public class Solution { + public class HeightPoint{ + int x, height; + public HeightPoint(int x, int height) { + this.x = x; + this.height = height; + } + } + public List getSkyline(int[][] buildings) { + List rst = new ArrayList(); + if (buildings == null || buildings.length == 0 || buildings[0].length == 0) { + return rst; + } + + //Init the list sorted by index && height + List heightPoints = new ArrayList(); + for (int i = 0; i < buildings.length; i++) { + heightPoints.add(new HeightPoint(buildings[i][0], -buildings[i][2])); + heightPoints.add(new HeightPoint(buildings[i][1], buildings[i][2])); + } + Collections.sort(heightPoints, new Comparator() { + public int compare(HeightPoint p1, HeightPoint p2) { + if (p1.x == p2.x) { + return p1.height - p2.height; + } else { + return p1.x - p2.x; + } + } + }); + + //Process height points + //reversed priorityqueue, becase for same pos x, we always want the highest point + PriorityQueue queue = new PriorityQueue(1000, Collections.reverseOrder()); + queue.offer(0); + int prev = queue.peek(); + + for (HeightPoint p : heightPoints) { + if (p.height < 0) { + queue.offer(-p.height); + } else { + queue.remove(p.height); + } + + int currPeak = queue.peek(); + if (currPeak != prev) { + rst.add(new int[] {p.x, currPeak}); + prev = currPeak; + } + } + + return rst; + } +} + + + +/* +Thoughts: +Well, based on JiuZhang, http://www.jiuzhang.com/solutions/building-outline/, implement a HashHeap. +**HashHeap. Super long implementation: http://www.jiuzhang.com/solutions/hash-heap/ + +What is HashHeap Exactly? Document below: + +*/ +class HashHeap { + //Heap is a arraylist, which stores the actaul Integer values. It stores the real data + ArrayList heap; + //HashMap stores the actual value, and the corresponding node. + HashMap hash; + String mode; + int size_t; + + /* + Used in HashMap, + id: id in the Heap tree + num: The frequency of the appearance of this id. + */ + class Node { + public Integer id; + public Integer num; + + Node(Node now) { + id = now.id; + num = now.num; + } + + Node(Integer first, Integer second) { + + this.id = first; + this.num = second; + } + } + + public HashHeap(String mod) { // 传入min 表示最小堆,max 表示最大堆 + // TODO Auto-generated constructor stub + heap = new ArrayList(); + mode = mod; + hash = new HashMap(); + size_t = 0; + } + /*Regular peak, size, empty functions*/ + int peak() { + return heap.get(0); + } + + int size() { + return size_t; + } + + Boolean empty() { + return (heap.size() == 0); + } + // Basis of heap + int parent(int id) { + if (id == 0) { + return -1; + } + return (id - 1) / 2; + } + // Basis of heap. Our heap is base indxe = 0 + int lson(int id) { + return id * 2 + 1; + } + // Basis of heap. Our heap is base indxe = 0 + int rson(int id) { + return id * 2 + 2; + } + // Basis of heap. + //If min heap, parent < left/right child + //If max heap, parent > left/right child + boolean comparesmall(int a, int b) { + if (a <= b) { + if (mode == "min") + return true; + else + return false; + } else { + if (mode == "min") + return false; + else + return true; + } + + } + //swap value in heap based the 2 ids + //based on value, create new node in hashmap. + void swap(int idA, int idB) { + int valA = heap.get(idA); + int valB = heap.get(idB); + + int numA = hash.get(valA).num; + int numB = hash.get(valB).num; + hash.put(valB, new Node(idA, numB)); + hash.put(valA, new Node(idB, numA)); + heap.set(idA, valB); + heap.set(idB, valA); + } + + //Similar to delete, but only delete element at index==0, and return the value + Integer poll() { + size_t--; + Integer now = heap.get(0); + Node hashnow = hash.get(now); + if (hashnow.num == 1) { + swap(0, heap.size() - 1); + hash.remove(now); + heap.remove(heap.size() - 1); + if (heap.size() > 0) { + siftdown(0); + } + } else { + hash.put(now, new Node(0, hashnow.num - 1)); + } + return now; + } + //Add + //If exist, count++ in hashmap + //If not exited, add to tail, then sfitup + void add(int now) { + size_t++; + if (hash.containsKey(now)) { + Node hashnow = hash.get(now); + hash.put(now, new Node(hashnow.id, hashnow.num + 1)); + + } else { + heap.add(now); + hash.put(now, new Node(heap.size() - 1, 1)); + } + + siftup(heap.size() - 1); + } + //Remove node + //If not last one, count-- from the hashMap + //If last one, move it to tail of the structure, and delete it. + //When the id is not tail (note, the id is already attached with new values after swap), then siftup and siftdown to sort all ids + void delete(int now) { + size_t--; + ; + Node hashnow = hash.get(now); + int id = hashnow.id; + int num = hashnow.num; + if (hashnow.num == 1) { + + swap(id, heap.size() - 1); + hash.remove(now); + heap.remove(heap.size() - 1); + if (heap.size() > id) { + siftup(id); + siftdown(id); + } + } else { + hash.put(now, new Node(id, num - 1)); + } + } + //If the target id and its parent do not comply the heap structure, siftup. + void siftup(int id) { + while (parent(id) > -1) { + int parentId = parent(id); + if (comparesmall(heap.get(parentId), heap.get(id)) == true) { + break; + } else { + swap(id, parentId); + } + id = parentId; + } + } + //If the target id and its children do not comply with the heap structure, siftdown + void siftdown(int id) { + while (lson(id) < heap.size()) { + int leftId = lson(id); + int rightId = rson(id); + int son; + if (rightId >= heap.size() || (comparesmall(heap.get(leftId), heap.get(rightId)) == true)) { + son = leftId; + } else { + son = rightId; + } + if (comparesmall(heap.get(id), heap.get(son)) == true) { + break; + } else { + swap(id, son); + } + id = son; + } + } +} + +``` \ No newline at end of file diff --git a/Java/The Smallest Difference.java b/Java/The Smallest Difference.java old mode 100644 new mode 100755 index fd9c4e9..5e1dd4b --- a/Java/The Smallest Difference.java +++ b/Java/The Smallest Difference.java @@ -1,3 +1,7 @@ +M +tags: Sort, Two Pointers, Array + +``` /* Given two array of integers(the first array is array A, the second array is array B), now we are going to find a element in array A which is A[i], and another element in array B which is B[j], so that the difference between A[i] and B[j] (|A[i] - B[j]|) is as small as possible, return their smallest difference. @@ -82,3 +86,5 @@ public int smallestDifference(int[] A, int[] B) { + +``` \ No newline at end of file diff --git a/Java/The Spiral Matrix II.java b/Java/The Spiral Matrix II.java new file mode 100644 index 0000000..fa3fa29 --- /dev/null +++ b/Java/The Spiral Matrix II.java @@ -0,0 +1,51 @@ +M +1534346957 +tags: Array + +#### Move forward till end +- Similar concept as `The Maze`: keep walking until hit wall, turn back +- fix direction `dx[direction % 4]` + +``` +/* +Given a positive integer n, generate a square matrix filled with elements from 1 to n2 in spiral order. + +Example: + +Input: 3 +Output: +[ + [ 1, 2, 3 ], + [ 8, 9, 4 ], + [ 7, 6, 5 ] +] +*/ + +/* +Use while loop, reach end and stop +*/ +class Solution { + int[] dx = {0, 1, 0, -1}; + int[] dy = {1, 0, -1, 0}; + public int[][] generateMatrix(int n) { + int[][] grid = new int[n][n]; + int step = 1, i = 0, j = 0, direction = 0; + grid[i][j] = step++; + while (step <= n * n) { + int x = dx[direction % 4]; + int y = dy[direction % 4]; + i += x; + j += y; + while (i >= 0 && i < n && j >= 0 && j < n && grid[i][j] == 0) { + grid[i][j] = step++; + i += x; + j += y; + } + i -= x; + j -= y; + direction++; + } + return grid; + } +} +``` \ No newline at end of file diff --git a/Java/Toeplitz Matrix.java b/Java/Toeplitz Matrix.java new file mode 100644 index 0000000..13241f7 --- /dev/null +++ b/Java/Toeplitz Matrix.java @@ -0,0 +1,77 @@ +E +1517561820 +tags: Array + +似乎没什么算法特点, 就是array基本运算, 然后分割成一个helper function去做重复计算, 剪短代码. +注意check MxN 的分界线. + +``` +/* +A matrix is Toeplitz if every diagonal from top-left to bottom-right has the same element. + +Now given an M x N matrix, return True if and only if the matrix is Toeplitz. + + +Example 1: + +Input: matrix = [[1,2,3,4],[5,1,2,3],[9,5,1,2]] +Output: True +Explanation: +1234 +5123 +9512 + +In the above grid, the diagonals are "[9]", "[5, 5]", "[1, 1, 1]", "[2, 2, 2]", "[3, 3]", "[4]", +and in each diagonal all elements are the same, so the answer is True. +Example 2: + +Input: matrix = [[1,2],[2,2]] +Output: False +Explanation: +The diagonal "[1, 2]" has different elements. +Note: + +matrix will be a 2D array of integers. +matrix will have a number of rows and columns in range [1, 20]. +matrix[i][j] will be integers in range [0, 99]. + +*/ + +/* +Thoughts: +Basic implementation, 3 sections: +1. x,y=0: center diagonal +2. x=1~n, y=0: left-bottom section +3. x=0, y=1~n: right-top section +*/ +class Solution { + public boolean isToeplitzMatrix(int[][] matrix) { + int n = matrix.length; + int m = matrix[0].length; + for (int i = 0; i < n; i++) { + if(!validation(i, 0, matrix)) { + return false; + } + } + + for (int j = 0; j < m; j++) { + if(!validation(0, j, matrix)) { + return false; + } + } + + return true; + } + + private boolean validation(int x, int y, int[][] matrix) { + int n = matrix.length; + int m = matrix[0].length; + for (int i = x, j = y; i < n && j < m; i++, j++) { + if (matrix[i][j] != matrix[x][y]) { + return false; + } + } + return true; + } +} +``` \ No newline at end of file diff --git a/Java/Top K Frequent Elements.java b/Java/Top K Frequent Elements.java new file mode 100644 index 0000000..f6041e7 --- /dev/null +++ b/Java/Top K Frequent Elements.java @@ -0,0 +1,161 @@ +M +1533166756 +tags: Hash Table, Heap, PriorityQueue, MinHeap, MaxHeap +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + +``` +/** +Given a non-empty array of integers, return the k most frequent elements. + +For example, +Given [1,1,1,2,2,3] and k = 2, return [1,2]. + +Note: +You may assume k is always valid, 1 ≤ k ≤ number of unique elements. +Your algorithm's time complexity must be better than O(n log n), where n is the array's size. + */ + +// Hashmap with count List[] +class Solution { + public List topKFrequent(int[] nums, int k) { + List rst = new ArrayList<>(); + if (nums == null || nums.length == 0) return rst; + + int n = nums.length; + Map map = new HashMap<>(); + for (int num : nums) { + map.put(num, map.getOrDefault(num, 0) + 1); + } + + List[] bucket = new List[n + 1]; // [0 ~ n] + for (Map.Entry entry : map.entrySet()){ + int count = entry.getValue(); + if (bucket[count] == null) { + bucket[count] = new ArrayList<>(); + } + bucket[count].add(entry.getKey()); + } + + for(int i = n; i >= 0 && rst.size() < k; i--) { + if(bucket[i] != null) { + rst.addAll(bucket[i]); + } + } + return rst; + } +} +// Min Heap. O(n) space, O(nlogk) time. +class Solution { + class Record { + public int value; + public int freq = 0; + public Record(int value) { + this.value = value; + } + } + public List topKFrequent(int[] nums, int k) { + List result = new ArrayList<>(); + if (nums == null || nums.length == 0) return result; + + // init hashmap. O(n) + Map map = new HashMap<>(); + for (int num : nums) { + map.putIfAbsent(num, new Record(num)); + map.get(num).freq += 1; + } + + // Prepare PriorityQueue + PriorityQueue queue = new PriorityQueue<>(k, new Comparator() { + public int compare(Record a, Record b) { + return a.freq - b.freq; + } + }); + // Lambda comparator is slow + //PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(a -> a.freq)); + for (Record record: map.values()) { + queue.offer(record); + if (queue.size() > k) { + queue.poll(); + } + } + + while (!queue.isEmpty()) { + result.add(0, queue.poll().value); + } + return result; + } +} + +/* + +MaxHeap +O(nLogn) means we can not run priorityQueue.offer within the loop, because the queue.offer operation includes sorting time O(logn). +We can: +1. Use a map to track the occurrence of integer in a Record object +2. Use priority queue to sort the record by occurrence +*/ +class Solution { + class Record { + public int value; + public int freq = 0; + public Record(int value) { + this.value = value; + } + public int getFreq() { + return this.freq; + } + } + public List topKFrequent(int[] nums, int k) { + List result = new ArrayList<>(); + if (nums == null || nums.length == 0) return result; + + // init hashmap. O(n) + Map map = new HashMap<>(); + for (int num : nums) { + map.putIfAbsent(num, new Record(num)); + map.get(num).freq += 1; + } + + // Prepare PriorityQueue, using new lambda comparator interface + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing((Record a) -> a.getFreq()).reversed()); + for (Record record: map.values()) { + queue.offer(record); + } + + for (int i = 0; i < k; i++) { + result.add(queue.poll().value); + } + return result; + } +} + + + +``` \ No newline at end of file diff --git a/Java/Top K Frequent Words.java b/Java/Top K Frequent Words.java new file mode 100755 index 0000000..513c4e7 --- /dev/null +++ b/Java/Top K Frequent Words.java @@ -0,0 +1,212 @@ +M +1527835239 +tags: Hash Table, Heap, Trie, PriorityQueue, MaxHeap, MinHeap +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + +``` +/* +Given a list of words and an integer k, return the top k frequent words in the list. + +Example +Given + +[ + "yes", "lint", "code", + "yes", "code", "baby", + "you", "baby", "chrome", + "safari", "lint", "code", + "body", "lint", "code" +] +for k = 3, return ["code", "lint", "baby"]. + +for k = 4, return ["code", "lint", "baby", "yes"], + +Note +You should order the words by the frequency of them in the return list, +the most frequent one comes first. If two words has the same frequency, +the one with lower alphabetical order come first. + +Challenge +Do it in O(nlogk) time and O(n) extra space. + +Extra points if you can do it in O(n) time with O(k) extra space. + +Tags Expand +Hash Table Heap Priority Queue +*/ +// MaxHeap +class Solution { + class Node { + int freq; + String str; + public Node(String str, int freq){ + this.str = str; + this.freq = freq; + } + } + public List topKFrequent(String[] words, int k) { + List rst = new ArrayList<>(); + if (words == null || words.length == 0 || k <= 0) { + return rst; + } + //queue + PriorityQueue queue = new PriorityQueue<>(k, new Comparator(){ + public int compare(Node a, Node b) { + if (a.freq == b.freq) { + return a.str.compareTo(b.str); + } else { + return b.freq - a.freq; + } + } + }); + //map + HashMap map = new HashMap<>(); + for (String word: words) { + if (!map.containsKey(word)) { + map.put(word, new Node(word, 0)); + } + map.get(word).freq = map.get(word).freq + 1; + } + + for (Map.Entry entry : map.entrySet()) { + queue.offer(entry.getValue()); + } + //output + for (int i = 0; i < k; i++) { + rst.add(queue.poll().str); + } + + return rst; + } +} + +// MinHeap: sort by ascending frequency; by reverse alphabetical order +class Solution { + class Node { + int freq = 0; + String str; + public Node(String str){ + this.str = str; + } + } + public List topKFrequent(String[] words, int k) { + List rst = new ArrayList<>(); + if (isInvalid(words, k)) return rst; + //queue + PriorityQueue queue = new PriorityQueue<>(k, new Comparator(){ + public int compare(Node a, Node b) { + if (a.freq == b.freq) { + return b.str.compareTo(a.str); + } else { + return a.freq - b.freq; + } + } + }); + // Lambda notation, slower: + //PriorityQueue queue = new PriorityQueue<>((a, b) -> a.freq == b.freq ? b.str.compareTo(a.str) : a.freq - b.freq); + //map + HashMap map = new HashMap<>(); + for (String word: words) { + map.putIfAbsent(word, new Node(word)); + map.get(word).freq += 1; + } + + for (Map.Entry entry : map.entrySet()) { + Node node = entry.getValue(); + if (queue.size() < k || node.freq >= queue.peek().freq) { + queue.offer(node); + } + + if (queue.size() > k) { + queue.poll(); + } + } + //output + while (!queue.isEmpty()) { + rst.add(0, queue.poll().str); + } + + return rst; + } + + private boolean isInvalid(String[] words, int k) { + return words == null || words.length == 0 || k <= 0; + } +} + +/* + Attempt1, Thoughts: + Brutle force + HashMap, size n. O(n) + ArrayList> lists: each entry has a list of strings with index as frequency. O(n) + Adding to result String[], do a Collections.sort(), which cause O(nlogk) with a O(n) for loop on top; It becomes O(nLogk)on average. + Return lists' top k. +*/ +public class Solution { + public String[] topKFrequentWords(String[] words, int k) { + String[] rst = new String[k]; + if (words == null || words.length == 0 || k <= 0) { + return rst; + } + + HashMap map = new HashMap(); + ArrayList> lists = new ArrayList>(); + for (int i = 0; i <= words.length; i++) { + lists.add(new ArrayList()); + } + //Fill map + for (int i = 0; i < words.length; i++) { + if (!map.containsKey(words[i])) { + map.put(words[i], 0); + } + map.put(words[i], map.get(words[i]) + 1); + } + //Fill ArrayList + for (Map.Entry entry : map.entrySet()) { + int freqIndex = entry.getValue(); + lists.get(freqIndex).add(entry.getKey()); + } + + int count = 0; + for (int i = lists.size() - 1; i >= 0; i--) { + ArrayList list = lists.get(i); + Collections.sort(list); + for (int j = 0; j < list.size(); j++) { + if (count < k) { + rst[count] = list.get(j); + count++; + } + if (count >= k) { + return rst; + } + } + } + + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/Topological Sorting.java b/Java/Topological Sorting.java old mode 100644 new mode 100755 index 3b4aa87..5e47a10 --- a/Java/Topological Sorting.java +++ b/Java/Topological Sorting.java @@ -1,3 +1,25 @@ +M +1528248097 +tags: Topological Sort, BFS, DFS + +#### Topological Sort BFS +- indegree tracking: Track all neighbors/childrens. 把所有的children都存在 inDegree里面 +- Process with a queue: 先把所有的root加一遍(indegree == 0),可能多个root。并且全部加到queue里面。 +- BFS with Queue: +- Only when map.get(label) == 0, add into queue && rst. (indegree剪完了, 就是root啦) +- inDegree在这里就 count down indegree, 确保在后面出现的node, 一定最后process. + + +#### Basics about graph +- 几个graph的condition: +- 1. 可能有多个root +- 2. directed node, 可以direct backwards. + +TODO: +- build`Map inDegree = new HashMap<>();` and include the root itself +- that is more traditional indegree building + +``` /* Given an directed graph, a topological order of the graph nodes is defined as follow: @@ -24,17 +46,21 @@ Tags Expand LintCode Copyright Geeks for Geeks Depth First Search Breadth First Search +*/ + +/** + Thoughts: First idea is Breadth First Search. -1. Find the node which has no parent node: this will be the beginning node. Use a HashMap to map all nodes with children, and whatever not in that map, is a root option. +1. Find the node which has no parent node: this will be the beginning node. + Use a HashMap to map all nodes with children, and whatever not in that map, is a root option. 2. Starting from this node, put all nodes in the queue (breadth-first) 3. process each node in the queue: add to array list Note: All all possible root node (whatever not added into the map) because there could be multiple heads : (. Really need to ask about this if not sure. -*/ - + */ /** * Definition for Directed graph. * class DirectedGraphNode { @@ -51,40 +77,42 @@ public class Solution { public ArrayList topSort(ArrayList graph) { ArrayList rst = new ArrayList(); if (graph == null || graph.size() == 0) { - return graph; + return graph; } - //Keep track of all neighbors in HashMap - HashMap map = new HashMap(); + //Keep track of all neighbors in HashMap + Map inDegree = new HashMap<>(); for (DirectedGraphNode node : graph) { - for (DirectedGraphNode neighbor : node.neighbors) { - int keyN = neighbor.label; - if (map.containsKey(keyN)) { - map.put(keyN, map.get(keyN) + 1); - } else { - map.put(keyN, 1); - } - } + for (DirectedGraphNode neighbor : node.neighbors) { + int keyN = neighbor.label; + if (!inDegree.containsKey(keyN)) { + inDegree.put(keyN, 0); + } + inDegree.put(keyN, inDegree.get(keyN) + 1); + } } - //BFS: Add root node. Note: - Queue queue = new LinkedList(); + + //BFS: Add root node. + Queue queue = new LinkedList<>(); for (DirectedGraphNode node : graph) { - if (!map.containsKey(node.label)) { - queue.offer(node); - rst.add(node); - } + if (!inDegree.containsKey(node.label)) { + queue.offer(node); + rst.add(node); + } } //BFS: go through all children while (!queue.isEmpty()) { - DirectedGraphNode node = queue.poll(); - for (DirectedGraphNode n : node.neighbors) { - int label = n.label; - map.put(label, map.get(label) - 1); - if (map.get(label) == 0) { - rst.add(n); - queue.offer(n); - } - } + DirectedGraphNode node = queue.poll(); + for (DirectedGraphNode n : node.neighbors) { + int label = n.label; + inDegree.put(label, inDegree.get(label) - 1); + if (inDegree.get(label) == 0) { + rst.add(n); + queue.offer(n); + } + } } return rst; } } + +``` \ No newline at end of file diff --git a/Java/Total Hamming Distance.java b/Java/Total Hamming Distance.java new file mode 100644 index 0000000..33a927a --- /dev/null +++ b/Java/Total Hamming Distance.java @@ -0,0 +1,65 @@ +M +1531598157 +tags: Bit Manipulation +time: O(n) +space: O(1), 32-bit array + +给出Hamming Distance定义(bit format时候有多少binary diff), 求一串数字的hamming distance总和. + +#### Bit Manipulation +- Bit题: 考验 bit >>, mask & 1, 还有对题目的理解能力 +- Put integers in binary, and compare each column: +- for each `1`, ask: how many are different from me? all the `0` +- `# of diffs at each bit-column = #ofZero * #ofOne ` +- 1. countZero[], countOne[]; 2. loop over nums and populate the two array + +##### 注意雷点 +- 问清楚: 10^9 < 2^31, we are okay with 32 bits +- `最终的hamming distance 要从 [1 ~ 32] 哪个bit开始算起`? 取决于 `最长`的那个binary format: 但不用先去找bit length +- 在做countZero, countOne时候, 都做32-bit; 最终做乘积的时候, 如果 `1` 或者 `0` 个数为零, 乘积自然为0. + + +``` +/* +The Hamming distance between two integers is the number of positions at which the corresponding bits are different. + +Now your job is to find the total Hamming distance between all pairs of the given numbers. + +Example: +Input: 4, 14, 2 + +Output: 6 + +Explanation: In binary representation, the 4 is 0100, 14 is 1110, and 2 is 0010 (just +showing the four bits relevant in this case). So the answer will be: +HammingDistance(4, 14) + HammingDistance(4, 2) + HammingDistance(14, 2) = 2 + 2 + 2 = 6. +Note: +Elements of the given array are in the range of 0 to 10^9 +Length of the array will not exceed 10^4. + */ + +// bit manipulation +class Solution { + public int totalHammingDistance(int[] nums) { + int rst = 0; + if (nums == null || nums.length == 0) return rst; // check input + + // populate over all nums + int[] countZero = new int[32], countOne = new int[32]; + for (int num : nums) populateBinaryCount(countZero, countOne, num); + + // calc final result + for (int i = 0; i < 32; i++) rst += countZero[i] * countOne[i]; + + return rst; + } + + private void populateBinaryCount(int[] countZero, int[] countOne, int num) { + for (int i = 0; i < 32; i++){ + if ((num & 1) == 1) countOne[i]++; + else countZero[i]++; + num = (num >> 1); + } + } +} +``` \ No newline at end of file diff --git a/Java/Total Occurrence of Target.java b/Java/Total Occurrence of Target.java old mode 100644 new mode 100755 index c48c726..dfd9828 --- a/Java/Total Occurrence of Target.java +++ b/Java/Total Occurrence of Target.java @@ -1,5 +1,8 @@ +M + 想法很简单。写起来有点长。 找total number of occurance. 首先找first occurance, 再找last occurance. + ``` /* Total Occurrence of Target diff --git a/Java/Trailing Zeros.java b/Java/Trailing Zeros.java old mode 100644 new mode 100755 index c28f157..4dfa7b1 --- a/Java/Trailing Zeros.java +++ b/Java/Trailing Zeros.java @@ -1,4 +1,9 @@ +E +tags: Math + +``` /* +LintCode Write an algorithm which computes the number of trailing zeros in n factorial. Example @@ -91,4 +96,5 @@ public long countExistance(long n, long m, HashMap map) { } }; -*/ \ No newline at end of file +*/ +``` \ No newline at end of file diff --git a/Java/Trapping Rain Water II.java b/Java/Trapping Rain Water II.java old mode 100644 new mode 100755 index bf459ec..2b2f56a --- a/Java/Trapping Rain Water II.java +++ b/Java/Trapping Rain Water II.java @@ -1,6 +1,140 @@ +H +1533110743 +tags: Heap, BFS, PriorityQueue, MinHeap + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + +``` +/** +LeetCode: https://leetcode.com/problems/trapping-rain-water-ii/description/ +Given an m x n matrix of positive integers representing the height of each unit cell +in a 2D elevation map, compute the volume of water it is able to trap after raining. + +Note: +Both m and n are less than 110. The height of each unit cell is greater than 0 and is less than 20,000. + +Example: + +Given the following 3x6 height map: +[ + [1,4,3,1,3,2], + [3,2,1,3,2,4], + [2,3,3,2,3,1] +] + +Return 4. +*/ + +/* +Thoughts: +1. Each spot needs to know the wall height from 4 directions +2. The water height of current spot is determined by the lowest of the 4 walls +=> Use Priority queue to store position sorted by height. + +Go layer by layer: outside layer first, then process queue => BFS +剥洋葱皮 + +Time: O(mn), queue with check through all items +Space: O(mn), queue size +*/ +class Solution { + class Cell { + int x, y, h; + public Cell(int x, int y, int h) { + this.x = x; + this.y = y; + this.h = h; + } + } + // main code block + public int trapRainWater(int[][] heightMap) { + if(isInvalid(heightMap)) return 0; + + int m = heightMap.length, n = heightMap[0].length; + int[] dx = {0, 0, 1, -1}; + int[] dy = {1, -1, 0, 0}; + boolean[][] visited = new boolean[m][n]; + // Prepare queue + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(cell -> cell.h)); + init(visited, queue, heightMap); + + // Calculate total + int total = 0; + while (!queue.isEmpty()) { + Cell cell = queue.poll(); + for (int i = 0; i < dx.length; i++) { + int mX = cell.x + dx[i]; + int mY = cell.y + dy[i]; + if (isValidCoordinate(mX, mY, visited)) { + visited[mX][mY] = true; + total += cell.h > heightMap[mX][mY] ? cell.h - heightMap[mX][mY] : 0; // cell is lowest, so any lower should contain water + queue.offer(new Cell(mX, mY, Math.max(cell.h, heightMap[mX][mY]))); + } + } + } + return total; + } + + // helper functions: + private void init(boolean[][] visited, PriorityQueue queue, int[][] heightMap) { + int m = heightMap.length, n = heightMap[0].length; + // LEFT/RIGHT + for (int i = 0; i < m; i++) { + visited[i][0] = true; + visited[i][n - 1] = true; + queue.offer(new Cell(i, 0, heightMap[i][0])); + queue.offer(new Cell(i, n - 1, heightMap[i][n - 1])); + } + // TOP/BOTTOM + for (int j = 0; j < n; j++) { + visited[0][j] = true; + visited[m - 1][j] = true; + queue.offer(new Cell(0, j, heightMap[0][j])); + queue.offer(new Cell(m - 1, j, heightMap[m - 1][j])); + } + } + + private boolean isValidCoordinate(int x, int y, boolean[][] visited) { + int m = visited.length, n = visited[0].length; + return x >= 0 && x < m && y >= 0 && y < n && !visited[x][y]; + } + + private boolean isInvalid(int[][] heightMap) { + return heightMap == null || heightMap.length == 0 || heightMap[0] == null || heightMap[0].length == 0; + } +} + + /* Trapping Rain Water II -Given n x m non-negative integers representing an elevation map 2d where the area of each cell is 1 x 1, compute how much water it is able to trap after raining. +Given n x m non-negative integers representing an elevation map 2d +where the area of each cell is 1 x 1, compute how much water it is able to trap after raining. Example @@ -41,79 +175,124 @@ */ public class Solution { - class Cell { - int x; - int y; - int h; - public Cell(int x, int y, int height) { - this.x = x; - this.y = y; - this.h = height; - } - } + class Cell { + int x; + int y; + int h; + public Cell(int x, int y, int height) { + this.x = x; + this.y = y; + this.h = height; + } + } /** * @param heights: a matrix of integers * @return: an integer */ public int trapRainWater(int[][] heights) { - if (heights == null || heights.length == 0 || heights[0].length == 0) { - return 0; - } - - PriorityQueue queue = new PriorityQueue(1, new Comparator(){ - public int compare(Cell A, Cell B) { - return A.h - B.h; - } - }); - int n = heights.length; - int m = heights[0].length; - boolean[][] visited = new boolean[n][m]; - - //LEFT-RIGHT - for (int i = 0; i < n; i++) { - visited[i][0] = true; - visited[i][m - 1] = true; - queue.offer(new Cell(i, 0, heights[i][0])); - queue.offer(new Cell(i, m - 1, heights[i][m - 1])); - } - //TOP-BOTTOM - for (int i = 0; i < m; i++) { - visited[0][i] = true; - visited[n - 1][i] = true; - queue.offer(new Cell(0, i, heights[0][i])); - queue.offer(new Cell(n - 1, i, heights[n - 1][i])); - } - - int[] xs = {0, 0, 1, -1}; - int[] ys = {1, -1, 0, 0}; - int sum = 0; - while (!queue.isEmpty()) { - Cell cell = queue.poll(); - for (int i = 0; i < 4; i++) { - int nx = cell.x + xs[i]; - int ny = cell.y + ys[i]; - if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny]) { - visited[nx][ny] = true; - sum += Math.max(0, cell.h - heights[nx][ny]); - queue.offer(new Cell(nx, ny, Math.max(heights[nx][ny], cell.h))); - } - } - }//end while - return sum; + if (heights == null || heights.length == 0 || heights[0].length == 0) { + return 0; + } + + PriorityQueue queue = new PriorityQueue(1, new Comparator(){ + public int compare(Cell A, Cell B) { + return A.h - B.h; + } + }); + int n = heights.length; + int m = heights[0].length; + boolean[][] visited = new boolean[n][m]; + + //LEFT-RIGHT + for (int i = 0; i < n; i++) { + visited[i][0] = true; + visited[i][m - 1] = true; + queue.offer(new Cell(i, 0, heights[i][0])); + queue.offer(new Cell(i, m - 1, heights[i][m - 1])); + } + //TOP-BOTTOM + for (int i = 0; i < m; i++) { + visited[0][i] = true; + visited[n - 1][i] = true; + queue.offer(new Cell(0, i, heights[0][i])); + queue.offer(new Cell(n - 1, i, heights[n - 1][i])); + } + + int[] xs = {0, 0, 1, -1}; + int[] ys = {1, -1, 0, 0}; + int sum = 0; + while (!queue.isEmpty()) { + Cell cell = queue.poll(); + for (int i = 0; i < 4; i++) { + int nx = cell.x + xs[i]; + int ny = cell.y + ys[i]; + if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny]) { + visited[nx][ny] = true; + sum += Math.max(0, cell.h - heights[nx][ny]); + queue.offer(new Cell(nx, ny, Math.max(heights[nx][ny], cell.h))); + } + } + }//end while + return sum; } }; +/* +*** Bellow solution is incorrect *** +Not sure why it's not correct. +Similar to the 2D version: record the highest from 4 different directions in array: +maxUp[], maxDown[], maxLeft[], maxRight[]. +then calculate each index. +time: O(n^2) +space: O(n^2) +*/ +class Solution { + public int trapRainWater(int[][] heightMap) { + if (heightMap == null || heightMap.length == 0 || heightMap[0] == null || heightMap[0].length == 0) { + return 0; + } + int m = heightMap.length; + int n = heightMap[0].length; + int[][] maxLeft = new int[m][n]; + int[][] maxRight = new int[m][n]; + int[][] maxUp = new int[m][n]; + int[][] maxDown = new int[m][n]; + + // Prepare the highest matrixes from 4 sides + for (int i = 0; i < m; i++) { + maxLeft[i][0] = heightMap[i][0]; + maxRight[i][n - 1] = heightMap[i][n - 1]; + } + for (int j = 0; j < n; j++) { + maxUp[0][j] = heightMap[0][j]; + maxDown[m - 1][j] = heightMap[m - 1][j]; + } + for (int i = 0; i < m; i++) { + for (int j = 1; j < n; j++) { + maxLeft[i][j] = Math.max(maxLeft[i][j - 1], heightMap[i][j]); + maxRight[i][n - j - 1] = Math.max(maxRight[i][n - j], heightMap[i][n - j - 1]); + } + } + for (int j = 0; j < n; j++) { + for (int i = 1; i < m; i++) { + maxUp[i][j] = Math.max(maxUp[i - 1][j], heightMap[i][j]); + maxDown[m - i - 1][j] = Math.max(maxDown[m - i][j], heightMap[m - i - 1][j]); + } + } + + // Calculate total + int total = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + int lowestHeight = Math.min(Math.min(maxLeft[i][j], maxRight[i][j]), Math.min(maxUp[i][j], maxDown[i][j])); + total += lowestHeight > heightMap[i][j] ? lowestHeight - heightMap[i][j] : 0; + } + } + return total; + } +} - - - - - - - - - - +``` \ No newline at end of file diff --git a/Java/Trapping Rain Water.java b/Java/Trapping Rain Water.java old mode 100644 new mode 100755 index cf83d97..1f43947 --- a/Java/Trapping Rain Water.java +++ b/Java/Trapping Rain Water.java @@ -1,9 +1,34 @@ -2 Pointers, -双面夹击:找中间最大bar,两面往中心扫 +H +1520734907 +tags: Array, Two Pointers, Stack + +这道题目的方法比较多. +#### 方法1 +Array, 维持一个左手最高墙array, 右手最高强array. +对于每个index而言, vertically 能存放的最大水柱, 就是靠左右最高墙决定的: +min(leftHighestWall, rightHighestWall) - currHeight. + +#### 方法2 +方法1上面的优化, two pointer, 还是找左边最高和右边最高. O(1) space. +利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +左边按照maxLeft来计算, 右边按照maxRight来计算. + +#### 方法3 +2 Pointers, 双面夹击: +1. 找中间最高bar的index +2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条。 +3. 每次还要减去block自身的height + +#### 方法4 +主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. + ``` /* -Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining. +Given n non-negative integers representing an elevation map where the width of each bar is 1, +compute how much water it is able to trap after raining. Example Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6. @@ -25,6 +50,100 @@ */ +/* +Thoughts: +At each position, if want to know how much water can trap, need to know height of leftWall, and rightWall. +Use the short wall and water height. +Don't forget to remove current wall height. + +Record highest left wall in one array and highest right wall in anothe array for every position. + +Time: O(n) +Space: O(n) +*/ +class Solution { + public int trap(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + int n = height.length; + int[] maxLeft = new int[n]; + int[] maxRight = new int[n]; + maxLeft[0] = height[0]; + maxRight[n - 1] = height[n - 1]; + // Prepare left/right highest for each index + for (int i = 1; i < n; i++) { + maxLeft[i] = Math.max(maxLeft[i - 1], height[i]); + } + for (int i = n - 2; i >= 0; i--) { + maxRight[i] = Math.max(maxRight[i + 1], height[i]); + } + + /* + Prepare left/right highest for each index. Simplified Version: + for (int i = 1; i < n; i++) { + maxLeft[i] = Math.max(maxLeft[i - 1], height[i]); + maxRight[n - i - 1] = Math.max(maxRight[n - i], height[n - i - 1]); + } + */ + + // Add water and remove wall height + int total = 0; + for (int i = 0; i < n; i++) { + int bottom = Math.min(maxLeft[i], maxRight[i]); + total += height[i] < bottom ? bottom - height[i] : 0; + } + return total; + } +} + + +/* +Thoughts: +Two Pointer +The two pointer approach respects the central heightest point: +The entire structure is divided by the central highest point, where maxLeft == maxRight. +Before left and right meet on this point, keep left++, right--. + +Time: O(n) +Space: O(1) +*/ +class Solution { + public int trap(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + int n = height.length; + int left = 0; + int right = n - 1; + int maxLeft = height[0]; + int maxRight = height[n - 1]; + + // process left/right pointer together + int total = 0; + while (left < right) { + if (maxLeft < maxRight) { + left++; + if (maxLeft > height[left]) { + total += maxLeft - height[left]; + } else { + maxLeft = height[left]; + } + } else { + right--; + if (maxRight > height[right]) { + total += maxRight - height[right]; + } else { + maxRight = height[right]; + } + } + } + + return total; + } +} + + /* Thoughts: @@ -85,20 +204,54 @@ public int trapRainWater(int[] heights) { } - - - - - - - - - - - - - - +/* +Thoughts: +Wather only can aggregate at bottom of the hill, +which means we need tack downhill indexes untill it hit bottom (before acending). +We can use Stack as decendingStack: +1. push to stack when current height[i] < stack.top() +2. If current height[i] > stack.top(), then it's ascending, + and try to calculate water accumulated from all stack elements that's <= height[i] + +Extreme case: +When it's always ascending and only have a downhill at last index, then it'll be a O(n) for loop, +and at the end + another O(n) while loop for once. + +Overall, time should be O(n), space O(n) +The 'while' iterate over stack once, which should be distributed across for interations. +*/ +class Solution { + public int trap(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + + // deceding stack that stores decending indexes + final Stack decendingStack = new Stack<>(); + int result = 0; + decendingStack.push(0); + + for (int i = 1; i < height.length; i++) { + if (height[i] >= height[decendingStack.peek()]) {// if ascending + int bottom = height[decendingStack.pop()]; + // Calculate volumn based on height diff from stack.peek() index to bottom index + // Note, stack.peek() and bottom are moving leftwards, and each time a row of water is added to result + // Of course, left bound has to be lower than current height[i] + while (!decendingStack.isEmpty() && height[i] >= height[decendingStack.peek()]) { + int leftBound = decendingStack.pop(); // pop(): shift left bound + result += (height[leftBound] - bottom) * (i - leftBound - 1); // a water row + bottom = height[leftBound]; // shift bottom to left + } + // When current height[i] < leftBound from stack, try use existing bottom to add more rows of water. + if (!decendingStack.isEmpty() && height[i] < height[decendingStack.peek()]) { + result += (height[i] - bottom) * (i - decendingStack.peek() - 1); + } + } + decendingStack.push(i); + } + return result; + } +} ``` \ No newline at end of file diff --git a/Java/Triangle Count.java b/Java/Triangle Count.java old mode 100644 new mode 100755 index 4a9f57a..12f3799 --- a/Java/Triangle Count.java +++ b/Java/Triangle Count.java @@ -1,5 +1,64 @@ +M +1516683660 +tags: Array + +其实也就是3sum的变形, 或者而说2sum的变形. 主要用2 pointers来做. +注意, 在选index时候每次定好一个 [0 ~ i], 在这里面找点start, end, 然后i 来组成triangle. +Note巧妙点: +在此之中, 如果一种start/end/i 符合, 那么从这个[start~end]中, 大于start的都可以, 所以我们count+= end-start. +反而言之, nums[i] + +``` +/* +Valid Triangle Number +Given an array consists of non-negative integers, your task is to count the number of triplets chosen from the array that can make triangles if we take them as side lengths of a triangle. +Example 1: +Input: [2,2,3,4] +Output: 3 +Explanation: +Valid combinations are: +2,3,4 (using the first 2) +2,3,4 (using the second 2) +2,2,3 +Note: +The length of the given array won't exceed 1000. +The integers in the given array are in the range of [0, 1000]. + + */ + /* -Given an array of integers, how many three numbers can be found in the array, so that we can build an triangle whose three edges length is the three numbers that we find? +Thoughts: +Follow the triangle rules, we'll sort, and use two pointers to count all possible triangles. +When sorted, if at one point moving out of range, any point going beyound will be out of range. +*/ + +class Solution { + public int triangleNumber(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int count = 0; + Arrays.sort(nums); + for (int i = 2; i < nums.length; i++) { + int start = 0; + int end = i - 1; + while (start < end) { + if (nums[start] + nums[end] > nums[i]) { + count += end - start; // any num[index] where start < index < end, can replace start and fit. + end--; + } else { + start++; + } + } + } + return count; + } +} + + +/* +Given an array of integers, how many three numbers can be found in the array, +so that we can build an triangle whose three edges length is the three numbers that we find? Example Given array S = [3,4,6,7], return 3. They are: @@ -16,7 +75,6 @@ Tags Expand Two Pointers LintCode Copyright */ - /* Thoughts: Pick 3 integers that fits the condition: @@ -60,3 +118,5 @@ public int triangleCount(int S[]) { } } + +``` \ No newline at end of file diff --git a/Java/Triangles.java b/Java/Triangles.java new file mode 100755 index 0000000..749b242 --- /dev/null +++ b/Java/Triangles.java @@ -0,0 +1,163 @@ +M +1530152837 +tags: Array, DP, DFS, Memoization, Coordinate DP + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + +``` + +/* +Given a triangle, find the minimum path sum from top to bottom. +Each step you may move to adjacent numbers on the row below. + +For example, given the following triangle + +[ + [2], + [3,4], + [6,5,7], + [4,1,8,3] +] +The minimum path sum from top to bottom is 11 (i.e., 2 + 3 + 5 + 1 = 11). + +Note: + +Bonus point if you are able to do this using only O(n) extra space, where n is the total number of rows in the triangle. + + +Thinking process: +1. Bottom-up + - Start from the bottom row, get all values of this row. Note: in triangle, height = cols at each row. So row X has X numbers. + - Start from (n - 1)th row and run up: calculate min from lower level + current node value. + - Depending what is wanted, here we use a 2D int arraya and return the min sum. +*/ + +public class Solution { + //Bottom - up + public int minimumTotal(List> triangle) { + if (triangle == null || triangle.size() == 0) { + return 0; + } + int n = triangle.size(); + int[][] dp = new int[n][n]; + // Init bottom row + for (int j = 0; j < n; j++) { + dp[n - 1][j] = triangle.get(n - 1).get(j); + } + for (int i = n - 2; i >= 0; i--) { + for (int j = 0; j <= i; j++) { + dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); + } + } + return dp[0][0]; + } +} + +// Rolling arary +public class Solution { + //Bottom - up + public int minimumTotal(List> triangle) { + if (triangle == null || triangle.size() == 0) { + return 0; + } + int n = triangle.size(); + int[][] dp = new int[2][n]; + // Init bottom row + for (int j = 0; j < n; j++) { + dp[(n - 1) % 2][j] = triangle.get(n - 1).get(j); + } + for (int i = n - 2; i >= 0; i--) { + for (int j = 0; j <= i; j++) { + dp[i % 2][j] = Math.min(dp[(i + 1)%2][j], dp[(i + 1)%2][j + 1]) + triangle.get(i).get(j); + } + } + return dp[0][0]; + } +} +/** +From above solution, row number does not seem to matter during calculation + */ +public class Solution { + //Bottom - up + public int minimumTotal(List> triangle) { + if (triangle == null || triangle.size() == 0) { + return 0; + } + int n = triangle.size(); + int[] dp = new int[n]; + for (int j = 0; j < n; j++) { + dp[j] = triangle.get(n - 1).get(j); + } + + for (int i = n - 2; i >= 0; i--) { + for (int j = 0; j <= i; j++) { + dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j); + } + } + return dp[0]; + } +} + +/* +2. dfs, memoization +- Go through all nodes and initialize with Integer.MAX_VALUE; +- Search from top: pathSum[i][j] = Math.min(dfs(i+1,j), dfs(i+1, j+1)) + currNode +- In dfs: if a node has been set previously, just return this value because this min value has been pre-calculated. +- If row is >= triangle.size(), return 0. +- This method can actually calculate the min sum from bottom to any point in the triangle. +// 94% +*/ +public class Solution { + int[][] pathSum; + public int minimumTotal(List> triangle) { + if (triangle == null || triangle.size() == 0) { + return 0; + } + int n = triangle.size(); + pathSum = new int[n][n]; + for (int i = 0; i < n; i++) { + for (int j = 0; j <= i; j++) { + pathSum[i][j] = Integer.MAX_VALUE; + } + } + return dfs(0, 0, triangle); + } + + public int dfs(int i, int j, List> triangle) { + if (i >= triangle.size()) { + // each row i should only have i items by triangle definition + return 0; + } + if (pathSum[i][j] != Integer.MAX_VALUE) return pathSum[i][j]; // memoization + + pathSum[i][j] = Math.min(dfs(i + 1, j, triangle), dfs(i + 1, j + 1, triangle)) + triangle.get(i).get(j); + return pathSum[i][j]; + } +} + + + + +``` \ No newline at end of file diff --git a/Java/Trim a Binary Search Tree.java b/Java/Trim a Binary Search Tree.java new file mode 100644 index 0000000..21d69f4 --- /dev/null +++ b/Java/Trim a Binary Search Tree.java @@ -0,0 +1,86 @@ +E +1516868090 +tags: Tree, BST + +方法1: +适合复习BST. 用DFS对待每个node. 注意BST的特征: 所有left nodes都小于当下node, 所有right nodes都大于当下node. + +根据题意用[L,R]切割.如果node.val= L). You might need to change the root of the tree, so the result should return the new root of the trimmed binary search tree. + +Example 1: +Input: + 1 + / \ + 0 2 + + L = 1 + R = 2 + +Output: + 1 + \ + 2 +Example 2: +Input: + 3 + / \ + 0 4 + \ + 2 + / + 1 + + L = 1 + R = 3 + +Output: + 3 + / + 2 + / + 1 + +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Thoughts: +0. Have node = root, and return node. +1. Consider root. If < L, node = node.right; if >R, node = node.left; +2. DFS into left, right. +3. At leaf, if node.left, node.right all null, return node. If not in [L, R], return null. +*/ +class Solution { + public TreeNode trimBST(TreeNode root, int L, int R) { + if (root == null) { + return null; + } + TreeNode node = root; // 3, 0 + if (node.val < L) { // 0 < 1 + return trimBST(node.right, L, R); // 2 + } else if (node.val > R) { + return trimBST(node.left, L, R); + } + node.left = trimBST(node.left, L, R); // 0, 1 + node.right = trimBST(node.right, L, R); // 4 + return node; + } +} + + +``` \ No newline at end of file diff --git a/Java/Tweaked Identical Binary Tree.java b/Java/Tweaked Identical Binary Tree.java old mode 100644 new mode 100755 index cf00367..cb6c98c --- a/Java/Tweaked Identical Binary Tree.java +++ b/Java/Tweaked Identical Binary Tree.java @@ -1,7 +1,20 @@ +E +1525670127 +tags: DFS, Tree + +检查binary tree是否 identical. + +特点: subtree如果是有旋转的, 只要tree node value相等, 就可以算是identical + +#### DFS +- 在DFS的基础上, 比对左左,左右,右左,右右 + +``` /* -Check two given binary trees are identical or not. Assuming any number of tweaks are allowed. A tweak is defined as a swap of the children of one node in the tree. +Check two given binary trees are identical or not. +Assuming any number of tweaks are allowed. +A tweak is defined as a swap of the children of one node in the tree. -Have you met this question in a real interview? Yes Example 1 1 / \ / \ @@ -28,10 +41,10 @@ */ /* - check isTweakedIdentical(a.left, b.right); + check isTweakedIdentical(a.left, b.right); - corner case: if both null, true; - if one null, false + corner case: if both null, true; + if one null, false */ /** * Definition of TreeNode: @@ -50,19 +63,18 @@ public class Solution { * @return true if they are tweaked identical, or false. */ public boolean isTweakedIdentical(TreeNode a, TreeNode b) { - if (a == null || b == null) { - return a == null && b == null; - } - if (a.val != b.val) { - return false; - } - return (isTweakedIdentical(a.left, b.left) && isTweakedIdentical(a.right, b.right)) - || (isTweakedIdentical(a.left, b.right) && isTweakedIdentical(a.right, b.left)); + if (a == null || b == null) { + return a == null && b == null; + } + if (a.val != b.val) { + return false; + } + return (isTweakedIdentical(a.left, b.left) && isTweakedIdentical(a.right, b.right)) + || (isTweakedIdentical(a.left, b.right) && isTweakedIdentical(a.right, b.left)); } } - - +``` \ No newline at end of file diff --git a/Java/Two Lists Sum.java b/Java/Two Lists Sum.java old mode 100644 new mode 100755 index 62f071a..239a6b8 --- a/Java/Two Lists Sum.java +++ b/Java/Two Lists Sum.java @@ -1,5 +1,14 @@ +M +tags: Linked List + +给两个Linked list, sum up and 合成新的list + +``` + /* -You have two numbers represented by a linked list, where each node contains a single digit.The digits are stored in reverse order, such that the 1’s digit is at the head of the list.Write a function that adds the two numbers and returns the sum as a linked list. +You have two numbers represented by a linked list, where each node contains a single digit. +The digits are stored in reverse order, such that the 1’s digit is at the head of the list. +Write a function that adds the two numbers and returns the sum as a linked list. Example Given two lists, 3->1->5->null and 5->9->2->null, return 8->0->8->null @@ -56,3 +65,5 @@ public ListNode addLists(ListNode l1, ListNode l2) { } } + +``` \ No newline at end of file diff --git a/Java/Two Strings Are Anagrams.java b/Java/Two Strings Are Anagrams.java old mode 100644 new mode 100755 index 7ee5907..88b09b4 --- a/Java/Two Strings Are Anagrams.java +++ b/Java/Two Strings Are Anagrams.java @@ -1,5 +1,11 @@ -Anagrams。 用count[256] +E + +方法1:char ascii 用count[256] 坑:不要想象这个是个26letter lowercase. may not be true. + +方法2: 若是其他字符encoding, 而不只是utf16-encoding (java char)? +那么就继续用string去做 + ``` /* Write a method anagram(s,t) to decide if two strings are anagrams or not. @@ -55,49 +61,43 @@ public boolean anagram(String s, String t) { }; - - - /* -Older version: -Thoughts: -1. s.charAt(i) is in t. -2. remove that char in t and s. -3. if at then all both are empty, return true. -NOTE: cannot use chararray to sort, because that takes O(n) space -BUG solved: when editing string, be careful with index. Index builds on original string length, -but string length changes over time. Solution: extra care on index, extra care on loop length change. +What if it's not just ascii code, maybe uni-code? +Then the character (utf16-encoding) may not be enough. So we use String here. */ + +//check length. compare public class Solution { - public boolean anagram(String s, String t) { - if (s == null || t == null || s.length() == 0 || t.length() == 0 || s.length() != t.length()) { + public boolean isAnagram(String s, String t) { + if (s == null || t == null || s.length() != t.length()) { return false; } if (s.equals(t)) { return true; } - int length = s.length(); - for (int i = 0; i < length; i++) { - int j = t.indexOf(s.charAt(0)); - if (j == -1) { - return false; + HashMap map = new HashMap(); + for (int i = 0; i < s.length(); i++) { + String ss = s.substring(i, i + 1); + String tt = t.substring(i, i + 1); + if (!map.containsKey(ss)) { + map.put(ss, 0); + } + map.put(ss, map.get(ss) + 1); + if (!map.containsKey(tt)) { + map.put(tt, 0); } - if (s.charAt(0) == t.charAt(j)) { - s = s.substring(1); - if (j == t.length() - 1) { - t = t.substring(0, j); - } else { - t = t.substring(0, j) + t.substring(j + 1); - } - } else { + map.put(tt, map.get(tt) - 1); + } + + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() != 0) { return false; } } - if (s.length() == 0 && t.length() == 0) { - return true; - } - return false; + + return true; } -}; +} + ``` \ No newline at end of file diff --git a/Java/Two Sum II - Input array is sorted.java b/Java/Two Sum II - Input array is sorted.java new file mode 100755 index 0000000..9820026 --- /dev/null +++ b/Java/Two Sum II - Input array is sorted.java @@ -0,0 +1,67 @@ +M +1516439332 +tags: Array, Two Pointers, Binary Search + +升序array, 找2SUM. + +#### Two pointers +- 排序好的array. Two pointer移动start和end,核查sum. +- 注意sum用long. +- O(n) time + +#### Binary Search, 因为已经排好序了啊 +- 定住一个valueA, 然后在剩下的里面 binary serach 找 (target - valueB) +- for loop O(n), binary search O(logn) +- overall time: O(nLogN), 就不写了 + +``` +/* +Given an array of integers that is already sorted in ascending order, +find two numbers such that they add up to a specific target number. + +The function twoSum should return indices of the two numbers such that they add up to the target, +where index1 must be less than index2. +Please note that your returned answers (both index1 and index2) are not zero-based. + +You may assume that each input would have exactly one solution. + +Input: numbers={2, 7, 11, 15}, target=9 +Output: index1=1, index2=2 + +Tags: Array Two Pointers, Binary Search +Similar Problems: (M) Two Sum + +*/ + + +/* +Thoughts: +Two pointer sweeping. +Start, end. Check if nums[start] + nums[end] == target. +*/ + +class Solution { + public int[] twoSum(int[] numbers, int target) { + if (numbers == null || numbers.length == 0) { + return numbers; + } + final int[] result = new int[2]; + int start = 0; + int end = numbers.length - 1; + while (start < end) { + long sum = (long)(numbers[start] + numbers[end]); + if (sum == target) { + result[0] = start + 1; + result[1] = end + 1; + break; + } else if (sum < target) { + start++; + } else { + end--; + } + }//end while + return result; + } +} + +``` \ No newline at end of file diff --git a/Java/Two Sum IV - Input is a BST.java b/Java/Two Sum IV - Input is a BST.java new file mode 100644 index 0000000..69168a4 --- /dev/null +++ b/Java/Two Sum IV - Input is a BST.java @@ -0,0 +1,50 @@ +E +1533407180 +tags: Tree + +HashSet to store visited items. Same old 2 sum trick. + +``` +/* +Given a Binary Search Tree and a target number, +return true if there exist two elements in the BST such that their sum is equal to the given target. + +Example 1: +Input: + 5 + / \ + 3 6 + / \ \ +2 4 7 + +Target = 9 + +Output: True +Example 2: +Input: + 5 + / \ + 3 6 + / \ \ +2 4 7 + +Target = 28 + +Output: False +*/ + +// hashmap, in-order traverse for smaller items, recursively +class Solution { + Set set = new HashSet<>(); + + public boolean findTarget(TreeNode root, int k) { + if (root == null) return false; + if (findTarget(root.left, k)) return true; + if (set.contains(k - root.val)) return true; + set.add(root.val); + if (findTarget(root.right, k)) return true; + + return false; + } +} +``` \ No newline at end of file diff --git a/Java/Two Sum.java b/Java/Two Sum.java new file mode 100755 index 0000000..edf9815 --- /dev/null +++ b/Java/Two Sum.java @@ -0,0 +1,140 @@ +E +1528356671 +tags: Array, Hash Table + +#### HashMap +- 相对暴力简洁: 找到一个value, 存一个index +- 若在HashMap里面 match 到结果, 就return HashMap里存的index. +- O(n) space && time. + +#### Sort array, two pointer +- 前后++, --搜索. Sort 用时O(nlogn). +- 1. 第一步 two pointer 找 value. +- 2. 注意,要利用额外的空间保留original array, 用来时候找index. (此处不能用HashMap,因为以value 为key,但value可能重复) +- O(n) space, O(nlogn) time. + + +``` +/** +LeetCode: 0-based answer +Given an array of integers, return indices of the two numbers such that they add up to a specific target. + +You may assume that each input would have exactly one solution, and you may not use the same element twice. + +Example: + +Given nums = [2, 7, 11, 15], target = 9, + +Because nums[0] + nums[1] = 2 + 7 = 9, +return [0, 1]. +*/ +/* +Thoughts: + Using a HashMap, O(n) space and O(n) time. + Thinking process: + Push everything into a HashMap. + Check if one element exist in the HashMap, if so save it. Meanwhile, save the other one. + Trick: after adding into the HashMap, we are looking for the 2nd index first. + Always check (target - current) from the HashMap. + If exist, that means index0 has already been pushed into the HashMap and current value is at index1. + (key, value) = (numbers[i], i) + Note: return index+1 because this is not 0-based. +*/ +class Solution { + public int[] twoSum(int[] nums, int target) { + int[] rst = new int[2]; + if (nums == null || nums.length <= 1) return rst; + + Map map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(target - nums[i])) { + rst[0] = map.get(target - nums[i]); + rst[1] = i; + break; + } + map.put(nums[i], i); + } + return rst; + } +} + +/* +Given an array of integers, find two numbers such that they add up to a specific target number. +The function twoSum should return indices of the two numbers such that they add up to the target, +where index1 must be less than index2. +Please note that your returned answers (both index1 and index2) are NOT zero-based. +Example +numbers=[2, 7, 11, 15], target=9 +return [1, 2] +Note +You may assume that each input would have exactly one solution +Challenge +Either of the following solutions are acceptable: +O(n) Space, O(nlogn) Time +O(n) Space, O(n) Time +Tags Expand +Two Pointers Sort Hash Table Array Airbnb Facebook +*/ + +//2. O(n) Space O(nlogn) time +/* + Feels like binary search when looking at O(nlogn) + 1. sort + 2. loop all number + 3. binary search on rest +*/ +public class Solution { + public int[] twoSum(int[] numbers, int target) { + if (numbers == null || numbers.length == 0) { + return null; + } + int[] original = new int[numbers.length]; + for (int i = 0; i < numbers.length; i++) { + original[i] = numbers[i]; + } + + Arrays.sort(numbers); + int start = 0; + int end = numbers.length - 1; + int num1 = -1; + int num2 = -1; + while (start != end) { + int sum = numbers[start] + numbers[end]; + if (sum == target) { + num1 = numbers[start]; + num2 = numbers[end]; + break; + }else if (sum < target) { + start++; + } else { + end--; + } + } + + //Find the num1,num2 in original array and record the index + int[] rst = new int[2]; + rst[0] = -1; + rst[1] = -1; + for (int i = 0; i < original.length; i++) { + if (original[i] == num1 || original[i] == num2) { + if (rst[0] == -1) { + rst[0] = i + 1; + } else { + rst[1] = i + 1; + break; + } + } + } + return rst; + } +} + + + + + + + + + +``` \ No newline at end of file diff --git a/Java/Ugly Number II.java b/Java/Ugly Number II.java new file mode 100755 index 0000000..52f344f --- /dev/null +++ b/Java/Ugly Number II.java @@ -0,0 +1,138 @@ +M +1533183153 +tags: Math, DP, Heap, Enumeration, PriorityQueue +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + +``` +/* +Write a program to find the n-th ugly number. + +Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. +For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers. + +Note that 1 is typically treated as an ugly number. +*/ + +/* + Thought: + Could do a infinite while loop, check each number see if that's an ugly number; till we find the nth. + time = m * O(avg # of factors for each integer) [m is probably a lot greater than n]~= O(m^2) + + So we definitely need to calculate it: + if we know a number x is ugly, we can *2, *3, *5 to make furture ugly numbers. + State: + dp[i]: ith ugly number. + fn: + candidate: Priority queue to hold candiates. + A HashSet() that makes sure dp[i-1]*2/3/5 is not duplicate. + candidate.add(dp[i - 1] * 2) + candidate.add(dp[i - 1] * 3) + candidate.add(dp[i - 1] * 5) + dp[i] = candidate.poll(); + init: + dp[0] = 0; + dp[1] = 1; + + return dp[n] + +Note: some number * 5 could be long. Just make sure it's long, then convert to int at the end. +*/ + +// DP. +public class Solution { + public int nthUglyNumber(int n) { + if (n <= 1) return n; + int[] dp = new int[n]; + dp[0] = 1; + int index2 = 0, index3 = 0, index5 = 0; + int factor2 = 2, factor3 = 3, factor5 = 5; + for (int i = 1; i < n; i++){ + int min = Math.min(Math.min(factor2, factor3), factor5); + dp[i] = min; + if (factor2 == min) factor2 = dp[++index2] * 2; + if (factor3 == min) factor3 = dp[++index3] * 3; + if (factor5 == min) factor5 = dp[++index5] * 5; + } + return dp[n-1]; + } +} + + +// PriorityQueue, O(nlong) +public class Solution { + public int nthUglyNumber(int n) { + if (n <= 1) return n; + + long[] dp = new long[n + 1]; + dp[0] = 0; + dp[1] = 1; + + PriorityQueue candidate = new PriorityQueue<>(); + HashSet set = new HashSet<>(); + set.add(dp[1]); + for (int i = 2; i <= n; i++) { + populate(candidate, set, dp, dp[i - 1], 2); + populate(candidate, set, dp, dp[i - 1], 3); + populate(candidate, set, dp, dp[i - 1], 5); + dp[i] = candidate.poll(); + } + + return (int)dp[n]; + } + + private void populate(PriorityQueue candidate, HashSet set, long[] dp, long last, int factor) { + long nextCandidate = last * factor; + if (!set.contains(nextCandidate)) { + candidate.add(nextCandidate); + set.add(nextCandidate); + } + } +} + +public class Solution { + public int nthUglyNumber(int n) { + if (n <= 0) { + return 0; + } else if (n == 1) { + return 1; + } + long[] dp = new long[n + 1]; + dp[0] = 0; + dp[1] = 1; + + PriorityQueue candidate = new PriorityQueue(); + HashSet set = new HashSet(); + set.add(dp[1]); + for (int i = 2; i <= n; i++) { + if (!set.contains(dp[i - 1] * 2)) { + candidate.add(dp[i - 1] * 2); + set.add(dp[i - 1] * 2); + } + if (!set.contains(dp[i - 1] * 3)) { + candidate.add(dp[i - 1] * 3); + set.add(dp[i - 1] * 3); + } + if (!set.contains(dp[i - 1] * 5)) { + candidate.add(dp[i - 1] * 5); + set.add(dp[i - 1] * 5); + } + dp[i] = candidate.poll(); + } + + return (int)dp[n]; + } +} +``` \ No newline at end of file diff --git a/Java/Ugly Number.java b/Java/Ugly Number.java old mode 100644 new mode 100755 index 026c1d7..d7da1c8 --- a/Java/Ugly Number.java +++ b/Java/Ugly Number.java @@ -1,4 +1,64 @@ +M +1527833857 +tags: Math + +LeetCode: 判断数字是否是ugly number. (definition: factor only have 2, 3, 5) + +#### Math +- 看是否可以整除. +- 看整除最终结果是否== 1 + +LintCode: 找kth ugly number, 应该与 Ugly Number II是一样的 + +- 方法1: PriorityQueue排序。用ArrayList check 新的ugly Number是否出现过。 +- 方法1-1:(解释不通,不可取)用PriorityQueue排序。神奇的3,5,7走位:按照题目答案的出发,定了3,5,7以什么规律出现。但是题目并没有特殊表明。 +- 方法2: DP . Not Done yet. + + +``` +/* +LeetCode +Write a program to check whether a given number is an ugly number. + +Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. + +Example 1: + +Input: 6 +Output: true +Explanation: 6 = 2 × 3 +Example 2: + +Input: 8 +Output: true +Explanation: 8 = 2 × 2 × 2 +Example 3: + +Input: 14 +Output: false +Explanation: 14 is not ugly since it includes another prime factor 7. +Note: + +1 is typically treated as an ugly number. +Input is within the 32-bit signed integer range: [−2^31, 2^31 − 1]. + +*/ + +class Solution { + public boolean isUgly(int num) { + if (num == 0) { + return false; + } + while (num % 2 == 0) num /= 2; + while (num % 3 == 0) num /= 3; + while (num % 5 == 0) num /= 5; + return num == 1; + } +} + + /* +LintCode Ugly number is a number that only have factors 3, 5 and 7. Design an algorithm to find the Kth ugly number. The first 5 ugly numbers are 3, 5, 7, 9, 15 ... @@ -12,6 +72,11 @@ Tags Expand LintCode Copyright Priority Queue + +*/ + +/* + Thoughts: Every level it's like: 3 5 7 @@ -25,7 +90,6 @@ Therefore, leave 3,5,7 cases till 7's . */ - class Solution { /** * @param k: The number k. @@ -57,7 +121,46 @@ public long kthPrimeNumber(int k) { } }; +//Ignore the sequence of 3, 5, 7. Use arraylist to check for duplicates +class Solution { + /** + * @param k: The number k. + * @return: The kth prime number as description. + */ + public long kthPrimeNumber(int k) { + if (k == 0) { + return 0; + } + ArrayList set = new ArrayList(); + PriorityQueue queue = new PriorityQueue(); + queue.offer((long)3); + queue.offer((long)5); + queue.offer((long)7); + set.add((long)3); + set.add((long)5); + set.add((long)7); + long num = 0; + for (int i = 0; i < k; i++) { + num = queue.poll(); + + if (!set.contains(num * 3)) { + queue.offer(num * 3); + set.add(num * 3); + } + if (!set.contains(num * 5)) { + queue.offer(num * 5); + set.add(num * 5); + } + if (!set.contains(num * 7)) { + queue.offer(num * 7); + set.add(num * 7); + } + } + return num; + } +}; /* Can use DP as well:http://blog.welkinlan.com/2015/07/28/ugly-number-lintcode-java/ -*/ \ No newline at end of file +*/ +``` \ No newline at end of file diff --git a/Java/Unique Binary Search Tree II.java b/Java/Unique Binary Search Tree II.java old mode 100644 new mode 100755 index b7ec9a6..9ad68fc --- a/Java/Unique Binary Search Tree II.java +++ b/Java/Unique Binary Search Tree II.java @@ -1,3 +1,17 @@ +M +1527832674 +tags: BST, DP, Tree, Divide and Conquer + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + +``` /* Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. @@ -30,27 +44,28 @@ * } */ public class Solution { - /** - * @paramn n: An integer - * @return: A list of root - */ public List generateTrees(int n) { + List rst = new ArrayList<>(); + if (n <= 0) { + return rst; + } return generate(1, n); } - public ArrayList generate(int start, int end) { - ArrayList rst = new ArrayList(); + + public List generate(int start, int end) { + List rst = new ArrayList<>(); if (start > end) { rst.add(null); return rst; } for (int i = start; i <= end; i++){ - ArrayList left = generate(start, i - 1); - ArrayList right = generate(i + 1, end); - for (TreeNode l : left) { - for (TreeNode r : right) { + List leftChildren = generate(start, i - 1); + List rightChildren = generate(i + 1, end); + for (TreeNode left : leftChildren) { + for (TreeNode right : rightChildren) { TreeNode root = new TreeNode(i); - root.left = l; - root.right = r; + root.left = left; + root.right = right; rst.add(root); } } @@ -59,3 +74,6 @@ public ArrayList generate(int start, int end) { } } + + +``` \ No newline at end of file diff --git a/Java/Unique Binary Search Tree.java b/Java/Unique Binary Search Tree.java old mode 100644 new mode 100755 index 5d373b4..6d46968 --- a/Java/Unique Binary Search Tree.java +++ b/Java/Unique Binary Search Tree.java @@ -1,3 +1,15 @@ +M +1516865694 +tags: DP, Tree, BST + +Not quite clear. +根据左右分割而总结出了原理, 每次分割, 左右两边都会有一定数量的permutation, 总体上的情况数量当然是相乘. +然后每一个不同的分割点都加一遍: +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +然后把数学公式转换成DP的方程, 有点玄学的意思啊! 不好想. + +``` /* Given n, how many structurally unique BST's (binary search trees) that store values 1...n? @@ -20,6 +32,37 @@ OR: C(n) = SUM(C(i)*C(n-i-1)). */ +/* +Thoughts: +If pick 1, left: 0, right: 2, 3. f(0) = 1, f(2) with value 2,3, there are two ways of orientation, which makes f(2) = 2. + Therefore, f(0) * f(2) = 2; +If pick 2, left: 1, right:3, there is only f(1)*f(1) = 1 +If pick 3, left: 1, 2, right: 0, f(2) * f(0) = 2 +So add all possible conditions together: 2 + 1 + 2 = 5 +f(0) = 1 +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +This is more like a math problem: Catalan number +C(n+1) = SUM(C(i)*C(n-i)) +OR: C(n) = SUM(C(i)*C(n-i-1)). +*/ +class Solution { + public int numTrees(int n) { + if ( n <= 1) { + return 1; + } + final int[] numTree = new int[n + 1]; + numTree[0] = 1; + for (int i = 1; i <= n; i++) { + for (int j = 0; j < i; j++) { + numTree[i] += numTree[j] * numTree[i - j - 1]; + } + } + return numTree[n]; + } +} + + public class Solution { /** * @paramn n: An integer @@ -40,3 +83,5 @@ public int numTrees(int n) { return count[n]; } } + +``` \ No newline at end of file diff --git a/Java/Unique Characters.java b/Java/Unique Characters.java old mode 100644 new mode 100755 index 6e4c03c..923a679 --- a/Java/Unique Characters.java +++ b/Java/Unique Characters.java @@ -1,7 +1,22 @@ -不用额外data structure, O(n^2), double for loop. -用hashSet, space O(n), time O(n) +E +1531457659 +tags: String, Array + +determine if characters are unique in string + +#### HashSet +- space O(n), time O(n) + +#### char[] +- space O(n), time O(nlogn) + +#### no additional data structure +- double for loop: O(n^2) + + ``` /* +LintCode Implement an algorithm to determine if a string has all unique characters. Example @@ -16,44 +31,24 @@ String Cracking The Coding Interview Array */ -/* - Thought: - do it without hash set. - Can do a double-for loop, check from i~j, if str[i] exist later in the string. - O(n^2) -*/ - public class Solution { - /** - * @param str: a string - * @return: a boolean - */ public boolean isUnique(String str) { - if (str == null || str.length() == 0) { - return true; - } - for (int i = 0; i < str.length(); i++) { - for (int j = i + 1; j < str.length(); j++) { - if (str.charAt(i) == str.charAt(j)) { - return false; - } - } + if (str == null || str.length() == 0) return true; + + char[] arr = str.toCharArray(); // nlogn + Arrays.sort(arr); + for (int i = 1; i < arr.length; i++) { + if (arr[i] == arr[i - 1]) return false; } return true; } } - - /* Thought: 1st, write hasset, there you go. */ public class Solution { - /** - * @param str: a string - * @return: a boolean - */ public boolean isUnique(String str) { if (str == null || str.length() == 0) { return true; @@ -71,4 +66,28 @@ public boolean isUnique(String str) { } } +/* + Thought: + do it without hash set. + Can do a double-for loop, check from i~j, if str[i] exist later in the string. + O(n^2) +*/ + +public class Solution { + public boolean isUnique(String str) { + if (str == null || str.length() == 0) { + return true; + } + for (int i = 0; i < str.length(); i++) { + for (int j = i + 1; j < str.length(); j++) { + if (str.charAt(i) == str.charAt(j)) { + return false; + } + } + } + return true; + } +} + + ``` \ No newline at end of file diff --git a/Java/Unique Path.java b/Java/Unique Path.java old mode 100644 new mode 100755 index 90e5be2..c34fedb --- a/Java/Unique Path.java +++ b/Java/Unique Path.java @@ -1,7 +1,27 @@ +M +1521312479 +tags: Array, DP, Coordinate DP + +2D array, 算走到最右下角,有多少种方式. + +##### DP +- 计数DP.注意方程式前两位置加在一起: 前两种情况没有overlap, 也不会缺情况. +- 注意initialization, 归1. +- 需要initialize的原因是,也是一个reminder: 在方程中会出现-1index +- Of course, row i = 0, or col j = 0, there is only 1 way to access +- time O(mn), space O(mn) + +##### 滚动数组 +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + + +``` /* A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). -The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below). +The robot can only move either down or right at any point in time. +The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below). How many possible unique paths are there? @@ -9,15 +29,97 @@ m and n will be at most 100. Example -1,1 1,2 1,3 1,4 1,5 1,6 1,7 +1,1 1,2 1,3 1,4 1,5 1,6 1,7 2,1 -3,1 3,7 +3,1 3,7 Above is a 3 x 7 grid. How many possible unique paths are there? Tags Expand Array Dynamic Programming +*/ + + + +/* +Thoughts: +Count # of ways to reach bottom-right: it depends on top/left +dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +Init: +For all i == 0, there is only 1 way to reach each spot: can only move down or right. + +Rolling Array: +1st dimension [i] only deals with [i - 1]. +Build rolling array to save space: O(n) +*/ +class Solution { + public int uniquePaths(int m, int n) { + if (m <= 0 || n <= 0) { + return 0; + } + int[][] dp = new int[2][n]; + int curr = 0; + int prev = 0; + // Init j = 0 col + for (int i = 0; i < 2; i++) { + dp[i][0] = 1; + } + // Init i = 0 row + for (int j = 0; j < n; j++) { + dp[curr][j] = 1; + } + + // Calcualte the dp[i][j] + for (int i = 1; i < m; i++) { + prev = curr; + curr = 1 - prev; + for (int j = 1; j < n; j++) { + dp[curr][j] = dp[prev][j] + dp[curr][j - 1]; + } + } + + return dp[curr][n - 1]; + } +} + + +/* +Thoughts: +'how many ways' -> Could do DFS, but try DP +Robot moves: (0, 1) or (1, 0) +gird[x][y]: #paths to reach x,y. +There are only 2 ways for getting to (x, y): from (x-1, y) or (x, y-1) +Then, the sub problem is grid[x-1,y], and grid[x, y-1]. +grid[x][y] = Math.min(grid[x-1,y], grid[x, y-1]) + 1; + +Boundary: when x = 0, grid[0, 0~y] = 0~y; same for y=0, grid[0~x, 0] = 0~x; +Path: should go from y++ and y=0, because when we advance +1 row, we'd use previous x/y, which should be calculated already. +*/ +class Solution { + public int uniquePaths(int m, int n) { + if (m == 0 || n == 0) { + return 0; + } + // Initialization + final int[][] grid = new int[m][n]; + for (int i = 0; i < n; i++) { + grid[0][i] = 1; + } + for (int i = 0; i < m; i++) { + grid[i][0] = 1; + } + // Calculate based on equation + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + grid[i][j] = grid[i - 1][j] + grid[i][j - 1]; + } + } + return grid[m-1][n-1]; + } +} + +/* Thinking process: f[x][y]: want to find out all possible path To get to f[m][m] from f[m-1][n-1] has 2 way: f[m-1][n] or f[m][n-1]. @@ -27,7 +129,6 @@ Recursively add up to (0,0), will find out the total path. 1. Own solution: user HashMap to memorize */ - public class Solution { /** * @param n, m: positive integer (1 <= n ,m <= 100) @@ -81,7 +182,7 @@ public class Solution { * @param n, m: positive integer (1 <= n ,m <= 100) * @return an integer */ - //Traverse + //Traverse public int uniquePaths(int m, int n) { if (m <= 1 || n <= 1) { return 1; @@ -104,3 +205,5 @@ public int uniquePaths(int m, int n) { } } + +``` \ No newline at end of file diff --git a/Java/Unique Paths II.java b/Java/Unique Paths II.java old mode 100644 new mode 100755 index 51d7ef3..3500c63 --- a/Java/Unique Paths II.java +++ b/Java/Unique Paths II.java @@ -1,3 +1,16 @@ +M +1517284913 +tags: Array, DP, Coordinate DP + +跟unique path的grid一样, 目标走到右下角, 但是grid里面可能有obstacle, 不能跨越. 求unique path 的count. + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- 考虑最终结尾需要的状态:如何组成,写出公式. +- 公式中注意处理能跳掉的block, marked as 1. '到不了', 即为 0 path in dp[i][j]. + +``` /* Follow up for "Unique Paths": @@ -29,42 +42,48 @@ 4. Note: At evaluating double-for loop, we cannot use break, because we still need to evaluate using upper/left block. Hence we set the obstacle = 0. */ -public class Solution { - /** - * @param obstacleGrid: A list of lists of integers - * @return: An integer - */ +/* +Thoughts: +Last right-bottom corner is always filled by left + up: dp[i][j] = dp[i - 1][j] + dp[i][j - 1]. +Whenever there is 1, mark the position with 0 ways, because it can get pass through. + +init: if row has block, all the rest of the row remains 0. If column has a block, the rest of the column remains 0. +*/ + +class Solution { public int uniquePathsWithObstacles(int[][] obstacleGrid) { - if (obstacleGrid == null || obstacleGrid.length == 0 || obstacleGrid[0].length == 0) { + if (obstacleGrid == null || obstacleGrid.length == 0) { return 0; } - int row = obstacleGrid.length; - int col = obstacleGrid[0].length; - int[][] matrix = new int[row][col]; - for (int i = 0; i < row; i++) { - if (obstacleGrid[i][0] == 1) { + int m = obstacleGrid.length; + int n = obstacleGrid[0].length; + + int[][] dp = new int[m][n]; + // init: + for (int i = 0, j = 0; j < n; j++) { + if (obstacleGrid[i][j] == 1) { break; - } else { - matrix[i][0] = 1; } + dp[i][j] = 1; } - for (int j = 0; j < col; j++) { - if (obstacleGrid[0][j] == 1) { + for (int i = 0, j = 0; i < m; i++) { + if (obstacleGrid[i][j] == 1) { break; - } else { - matrix[0][j] = 1; } + dp[i][j] = 1; } - for (int i = 1; i < row; i++) { - for (int j = 1; j < col; j++) { + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { if (obstacleGrid[i][j] == 1) { - matrix[i][j] = 0; - } else { - matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1]; + continue; } + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; } } - return matrix[row - 1][col - 1]; + return dp[m - 1][n - 1]; } } + +``` \ No newline at end of file diff --git a/Java/Unique Word Abbreviation.java b/Java/Unique Word Abbreviation.java new file mode 100755 index 0000000..8460f95 --- /dev/null +++ b/Java/Unique Word Abbreviation.java @@ -0,0 +1,155 @@ +M +1527830038 +tags: Hash Table, Design + + +给一个string[] dict, 和一个word. + +每个word都可以缩写成固定的abbreviation ``(详细看原题) + +检查input word是否满足unique + +#### HashMap +- 简单算出abbreviatioin +- 检查abbr是否存在; 如果存在, 是不是input word本身. + +``` +/* +An abbreviation of a word follows the form . +Below are some examples of word abbreviations: + +a) it --> it (no abbreviation) + + 1 + ↓ +b) d|o|g --> d1g + + 1 1 1 + 1---5----0----5--8 + ↓ ↓ ↓ ↓ ↓ +c) i|nternationalizatio|n --> i18n + + 1 + 1---5----0 + ↓ ↓ ↓ +d) l|ocalizatio|n --> l10n +Assume you have a dictionary and given a word, find whether its abbreviation is unique in the dictionary. A word's abbreviation is unique if no other word from the dictionary has the same abbreviation. + +Example: + +Given dictionary = [ "deer", "door", "cake", "card" ] + +isUnique("dear") -> false +isUnique("cart") -> true +isUnique("cane") -> false +isUnique("make") -> true + +*/ + +/* +Given dict is String[], can't do contains(), so we'd convert the entire dict to abbreviation format. +Convert word into abbreviation as well, then perform check +*/ + +class ValidWordAbbr { + HashMap> map = new HashMap<>(); + public ValidWordAbbr(String[] dict) { + if (dict == null || dict.length == 0) { + return; + } + for (String word : dict) { + String abbr = convertToAbbr(word); + if (!map.containsKey(abbr)){ + map.put(abbr, new HashSet<>()); + } + map.get(abbr).add(word); + } + } + + public boolean isUnique(String word) { + if (word == null) { + return false; + } + String abbr = convertToAbbr(word); + if (!map.containsKey(abbr)) { + return true; + } else { + Set words = map.get(abbr); + if (words.size() == 1 && words.contains(word)) { + return true; + } + } + return false; + } + + private String convertToAbbr(String word) { + if (word == null || word.length() <= 2) { + return word; + } + return word.charAt(0) + String.valueOf(word.length() - 2) + word.charAt(word.length() - 1); + } +} + + + +/* +Thought: +Originally, used a hashset to store all existing pattern. If checked word exist in dict hashset, then return false. +However, there is a case that: the word existed in the dict only for once, which is by accident the same as the checked work, then return true. +Therefore, we need to keep track of what word has been catagrize into pattern. SO, use a HashMap instead. + +Note: Dealing with char, integer, string. Be careful if char are turnning int integers. +*/ +public class ValidWordAbbr { + HashMap> map; + public ValidWordAbbr(String[] dict) { + if (dict == null || dict.length == 0) { + return; + } + map = new HashMap>(); + for (String s : dict) { + String str = ""; + if (s.length() <= 2) { + str = s; + } else { + str += s.charAt(0) + (s.length() - 2 + "") + s.charAt(s.length() - 1); + } + if (!map.containsKey(str)) { + ArrayList list = new ArrayList(); + list.add(s); + map.put(str, list); + } else { + if (!map.get(str).contains(s)) { + map.get(str).add(s); + } + + } + } + } + + public boolean isUnique(String word) { + if (map == null || map.size() == 0) { + return true; + } + String str = ""; + if (word.length() <= 2) { + str = word; + } else { + str += word.charAt(0) + (word.length() - 2 + "") + word.charAt(word.length() - 1); + } + if (map.containsKey(str) && map.get(str).size() == 1 && map.get(str).get(0).equals(word)) { + return true; + } + return !map.containsKey(str); + } +} + + + + + +// Your ValidWordAbbr object will be instantiated and called as such: +// ValidWordAbbr vwa = new ValidWordAbbr(dictionary); +// vwa.isUnique("Word"); +// vwa.isUnique("anotherWord"); +``` \ No newline at end of file diff --git a/Java/Update Bits.java b/Java/Update Bits.java old mode 100644 new mode 100755 index cdd2b5b..0be3acc --- a/Java/Update Bits.java +++ b/Java/Update Bits.java @@ -1,6 +1,18 @@ -/* -Given two 32-bit numbers, N and M, and two bit positions, i and j. Write a method to set all bits between i and j in N equal to M (e g , M becomes a substring of N located at i and starting at j) +M +1517984778 +tags: Bit Manipulation + +熟悉bits的一些trick: +- ~0 = -1 = 111111...11111111 (32-bit) +- Create mask by shifting right >>>, and shifting left +- Reverse to get 0000...11110000 format mask +- & 0000 = clean up; | ABC = assign ABC +``` +/* +Given two 32-bit numbers, N and M, and two bit positions, i and j. +Write a method to set all bits between i and j in N equal to M +(e g , M becomes a substring of N located at i and starting at j) Example @@ -18,7 +30,29 @@ Create a mask: xxxx000000xxxx. Trick part: when it encounters negative number or dealing with index at edge index = 31, it starts having issue. Interesting fix: use long for masks. */ +/* +Thoughts: +Need a mask of 1111110000001111... where the '0's are representing range [i, j]. +Use the mask to n & mask, so this block will be 0 in n. +shift m << i, then n | m will do. + +Problem is: +how to create that mask? +Trick: +We can create 00000001111110000, and reverse it. + +32-bit 111111...111 is actually -1 in decimal. Or, we can get it by ~0. +1. We want a block of size [j - i], so right-logic-shift -1 >>> (j - i + 1). (be careful with negative leading 1) +2. Left-shift << i so the block is at correct position. +3. Reverse to complete the mask + +End: +1. n & mask +2. left-shift m by i +3. n | m + +*/ class Solution { /** *@param n, m: Two integer @@ -26,16 +60,15 @@ class Solution { *return: An integer */ public int updateBits(int n, int m, int i, int j) { - //Create mask: xxx00000xxx - long rightMask = ~0 >> i; - rightMask = ~(rightMask << i);// 00000xxx - long leftMask = ~0 >> (j + 1); - leftMask = leftMask << (j + 1);//xxxxx00000000 - long mask = leftMask | rightMask;//xxx00000xxx - n = (int) (n & mask); - n = (int) (n | (m << i)); - return n; + // Prepare mask + int mask = -1; // 11111111...1111 + mask = -1 >>> (32 - (j - i + 1)); // 000000...00111111, shift and leave only [j, i] block + mask = mask << i; // 000000..11111000 + mask = ~mask; // reverse: 111111...00000111 + + // Apply mask + n = n & mask; // Remove existing + return n | (m << i); // apply m } } - - +``` \ No newline at end of file diff --git a/Java/Valid Anagram.java b/Java/Valid Anagram.java new file mode 100755 index 0000000..bf9746f --- /dev/null +++ b/Java/Valid Anagram.java @@ -0,0 +1,92 @@ +E +1519713982 +tags: Hash Table, Sort + +HashMap + +``` +/* +Given two strings s and t, write a function to determine if t is an anagram of s. + +For example, +s = "anagram", t = "nagaram", return true. +s = "rat", t = "car", return false. + +Note: +You may assume the string contains only lowercase alphabets. + +Follow up: +What if the inputs contain unicode characters? How would you adapt your solution to such case? + +Tags: Hash Table, Sort +Similar Problems: (M) Group Anagrams, (E) Palindrome Permutation + + +*/ +/* +Thoughts: +Anagram: reorder of letters. +Use HashMap to store the frequency of chars of 1st string, and check aginst 2nd string. +s character: +1; +t character: -1; +check count of each index in the map; they should all be 0 +*/ + + +class Solution { + public boolean isAnagram(String s, String t) { + if (s == null || t == null || s.length() != t.length()) { + return false; + } + if (s.equals(t)) { + return true; + } + final Map charMap = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + char charS = s.charAt(i); + char charT = t.charAt(i); + if (!charMap.containsKey(charS)) { + charMap.put(charS, 0); + } + if (!charMap.containsKey(charT)) { + charMap.put(charT, 0); + } + charMap.put(charS, charMap.get(charS) + 1); + charMap.put(charT, charMap.get(charT) - 1); + } + for (Map.Entry entry: charMap.entrySet()) { + if (entry.getValue() != 0) { + return false; + } + } + return true; + } +} + + +/* +Thoughts: if only lower case letters, use int[26] for simplicity +*/ +class Solution { + public boolean isAnagram(String s, String t) { + if (s == null || t == null || s.length() != t.length()) { + return false; + } + if (s.equals(t)) { + return true; + } + int[] chars = new int[26]; + for (int i = 0; i < s.length(); i++) { + chars[s.charAt(i) - 'a'] += 1; + chars[t.charAt(i) - 'a'] -= 1; + } + + for (int i = 0; i < chars.length; i++) { + if (chars[i] != 0) { + return false; + } + } + return true; + } +} +``` \ No newline at end of file diff --git a/Java/Valid Number.java b/Java/Valid Number.java new file mode 100644 index 0000000..88cd799 --- /dev/null +++ b/Java/Valid Number.java @@ -0,0 +1,63 @@ +H +1532500367 +tags: Math, String, Enumeration +time: O(n) + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + +``` +/* +Validate if a given string is numeric. + +Some examples: +"0" => true +" 0.1 " => true +"abc" => false +"1 a" => false +"2e10" => true + +Note: It is intended for the problem statement to be ambiguous. +You should gather all requirements up front before implementing one. + +Update (2015-02-10): +The signature of the C++ function had been updated. +If you still see your function signature accepts a const char * argument, +please click the reload button to reset your code definition. +*/ + +class Solution { + public boolean isNumber(String s) { + if (s == null) return false; + s = s.trim(); + if (s.length() == 0) return false; + int i = 0; + char[] ss = s.toCharArray(); + if (ss[i] == '+' || ss[i] == '-') i++; + boolean isNum = false, isDot = false, isExp = false; + for (; i < s.length(); i++) { + char c = ss[i]; + if (Character.isDigit(c)) { + isNum = true; + } else if (c == '.') { + if (isDot || isExp) return false; // . or e exists already, return false + isDot = true; + } else if (c == 'e') { + if (isExp || !isNum) return false; // e exists, or if no integer ahead of e, return false + isExp = true; + isNum = false; // can start re-count integer + } else if (c == '+' || c == '-') { + if (i >= 1 && s.charAt(i - 1) != 'e') return false; + } else { + return false; + } + } + + return isNum; + } +} +``` \ No newline at end of file diff --git a/Java/Valid Palindrome II.java b/Java/Valid Palindrome II.java new file mode 100644 index 0000000..fb1c4cc --- /dev/null +++ b/Java/Valid Palindrome II.java @@ -0,0 +1,71 @@ +E +1533454056 +tags: String + +#### Palindrome String +- delete an index = jump over the index +- 注意 boolean chance 可以用一个helper function + +``` +/* +Given a non-empty string s, you may delete at most one character. +Judge whether you can make it a palindrome. + +Example 1: +Input: "aba" +Output: True +Example 2: +Input: "abca" +Output: True +Explanation: You could delete the character 'c'. +Note: +The string will only contain lowercase characters a-z. The maximum length of the string is 50000. + +*/ + +class Solution { + public boolean validPalindrome(String s) { + if (s == null || s.length() <= 1) return true; + + return validate(s, 0, s.length() - 1, true); + } + + public boolean validate(String s, int start, int end, boolean chance) { + if (start > end) return false; + while (start < end) { + if (s.charAt(start) == s.charAt(end)) { + start++; + end--; + continue; + } else if (chance) { + return validate(s, start + 1, end, false) || validate(s, start, end - 1, false); + } + return false; + } + return true; + } +} + +// Simplify start++, end-- +class Solution { + public boolean validPalindrome(String s) { + if (s == null || s.length() <= 1) return true; + + return validate(s, 0, s.length() - 1, true); + } + + public boolean validate(String s, int start, int end, boolean chance) { + if (start > end) return false; + while (start < end) { + if (s.charAt(start++) == s.charAt(end--)) { + continue; + } else if (chance) { + return validate(s, start - 1, end, false) || validate(s, start, end + 1, false); + } + return false; + } + return true; + } +} + +``` \ No newline at end of file diff --git a/Java/Valid Palindrome.java b/Java/Valid Palindrome.java old mode 100644 new mode 100755 index 88a81a9..648e156 --- a/Java/Valid Palindrome.java +++ b/Java/Valid Palindrome.java @@ -1,7 +1,21 @@ -注意如何滤过: alphanumeric +E +1525666944 +tags: Two Pointers, String + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Check Palindrome +- 前后两个指针, 往中间移动, 查看是否字母重合 + +#### 过滤 alphanumeric +- 可以用 ASCII code 来手动过滤, 只要 '0' ~ '9', 'a' ~ 'z', 'A' - 'Z' 之间的 +- 也可以用 regular expression: match 所有这些字母, 是 [a-zA-Z0-9] +- 那凡是不是这些字母的 match, 就是取反: "[^a-zA-Z0-9]". 测试: https://regex101.com/ + ``` /* -Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. +Given a string, determine if it is a palindrome, +considering only alphanumeric characters and ignoring cases. Example "A man, a plan, a canal: Panama" is a palindrome. @@ -9,16 +23,77 @@ "race a car" is not a palindrome. Note -Have you consider that the string might be empty? This is a good question to ask during an interview. +Have you consider that the string might be empty? +This is a good question to ask during an interview. For the purpose of this problem, we define empty string as valid palindrome. Challenge O(n) time without extra memory. -Tags Expand -String Two Pointers +Company: Microsoft Uber Facebook Zenefits +Hide Tags Two Pointers String +Hide Similar Problems (E) Palindrome Linked List + + */ +/* +recap: +Use regular expression [^a-zA-Z0-9] to replace all non-alphanumeric chars with "" +*/ +public class Solution { + public boolean isPalindrome(String s) { + if (s == null || s.length() <= 1) { + return true; + } + final String str = s.replaceAll("[^a-zA-Z0-9]", "").toLowerCase(); + int start = 0; + int end = str.length() - 1; + while (start < end) { + if (str.charAt(start) != str.charAt(end)) { + return false; + } + start++; + end--; + } + return true; + } +} + +/* +3.4.2016 recap +Filter the String first, then check Palindrome +But uses extra space to filter the string. Can also just manipulate string on itself. no big deal +*/ + +public class Solution { + public boolean isPalindrome(String s) { + if (s == null || s.length() == 0) { + return true; + } + + StringBuffer sb = new StringBuffer(); + s = s.toLowerCase(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if ((c <= 'z' && c >= 'a') || (c >= '0' && c <= '9')) { + sb.append(s.charAt(i)); + } + } + s = sb.toString(); + int start = 0; + int end = s.length() - 1; + while (start < end) { + if (s.charAt(start) != s.charAt(end)) { + return false; + } + start++; + end--; + } + return true; + } +} + /* Thoughts: Pointer from front to end. Front char has to equal end char. diff --git a/Java/Valid Parentheses.java b/Java/Valid Parentheses.java old mode 100644 new mode 100755 index ef8e128..7ce2e5f --- a/Java/Valid Parentheses.java +++ b/Java/Valid Parentheses.java @@ -1,6 +1,11 @@ -剥皮形态。 -左边的外皮在stack底部。 -右边的外皮应该和最顶上的左外皮一一对应。 +E +1533542429 +tags: String, Stack + +剥皮过程。解铃还须系铃人 +左边的外皮'{['在stack底部 +右边的外皮应该和stack顶上的左外皮一一对应 + ``` /* Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. @@ -12,6 +17,35 @@ Stack */ +//lock will be unlocked by the same key +//put in stack. when '),],}' appears, check stack.top() to make sure they are good match +// Use stack to hold the parentheses (head/tail) and remove them accordingly. +class Solution { + public boolean isValid(String s) { + if (s == null || s.length() == 0) return true; + final Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + if (c == '(' || c == '{' || c == '[') { + stack.add(c); + } else if (c == ')' || c == '}' || c == ']') { + if (stack.isEmpty()) { + return false; + } + if (c == ')' && stack.peek() == '(') { + stack.pop(); + } else if (c == '}' && stack.peek() == '{') { + stack.pop(); + } else if (c == ']' && stack.peek() == '[') { + stack.pop(); + } else { + return false; + } + } + } + return stack.isEmpty(); + } +} + /* Thoughts: Did this on Leetcode. Think about how do we naturally check it? diff --git a/Java/Valid Perfect Square.java b/Java/Valid Perfect Square.java new file mode 100644 index 0000000..447f2f4 --- /dev/null +++ b/Java/Valid Perfect Square.java @@ -0,0 +1,50 @@ +R +1518426971 +tags: Math, Binary Search + +Binary找sqrt. 基本 mid+1, mid-1 template. +注意: define index as long. + +``` +/* +Given a positive integer num, write a function which returns True if num is a perfect square else False. + +Note: Do not use any built-in library function such as sqrt. + +Example 1: + +Input: 16 +Returns: True +Example 2: + +Input: 14 +Returns: False +*/ + +/* +Thoughts: +Find a number x that x * x == num. x = [0 ~ num] +Binary search [0 ~ num] +*/ +class Solution { + public boolean isPerfectSquare(int num) { + if (num <= 1) { + return true; + } + long start = 0; + long end = num; + while (start <= end) { + long mid = start + (end - start) / 2; + long product = mid * mid; + if (product == num) { + return true; + } else if (product < num) { + start = mid + 1; + } else { + end = mid - 1; + } + } + return false; + } +} +``` \ No newline at end of file diff --git a/Java/Valid Sudoku.java b/Java/Valid Sudoku.java old mode 100644 new mode 100755 index 4988597..f8b05ad --- a/Java/Valid Sudoku.java +++ b/Java/Valid Sudoku.java @@ -1,5 +1,18 @@ -好像没啥技术含量. 做就行了。 -validate block的时候虽然看到了4层for.其实也就是n^2. +E +tags: Hash Table, Enumeration + +#### Hash Set +- 用HashSet存visited value. +- 在nest for loop里面validate row,col,and block. +- validate block要利用i 和 j 增长的规律。 +- 说白了,i && j是按照0~n增长的index, 具体怎么用是可以flexible的。这个方法在同一个nest for loop解决所有运算。 +- `int c = 3 * (i % 3) + j % 3;` //make use of how i and j increases +- `int r = 3 * (i / 3) + j / 3;` + +#### A bit Slower approach +- 单独做block validation: validate block的时候虽然看到了4层for. 其实也就是n^2 +- 可能代码稍微复杂一点 + ``` /* Determine whether a Sudoku is valid. @@ -25,6 +38,80 @@ A valid Sudoku board (partially filled) is not necessarily solvable. Only the fi Tags Expand Matrix */ +class Solution { + public boolean isValidSudoku(char[][] board) { + if (board == null || board.length == 0 || board[0].length == 0 || board.length != board[0].length) { + return false; + } + int n = board.length; + + for (int i = 0; i < n; i++) { + HashSet row = new HashSet<>(), col = new HashSet<>(), block = new HashSet<>(); + for (int j = 0; j < n; j++) { + //Check row + if (!helper(board[i][j], row)) return false; + //Check col, revert use of i,j + if (!helper(board[j][i], col)) return false; + //check block + int c = 3 * (i % 3) + j % 3;//make use of how i and j increases + int r = 3 * (i / 3) + j / 3; + if (!helper(board[r][c], block)) return false; + } + } + return true; + } + + private boolean helper(char c, Set set) { + if (!set.contains(c)) { + set.add(c); + } else if (c != '.') { + return false; + } + return true; + } +}; + +/*Recap 3.7.2016 */ +class Solution { + public boolean isValidSudoku(char[][] board) { + if (board == null || board.length == 0 || board[0].length == 0 || board.length != board[0].length) { + return false; + } + HashSet row,col,block; + int n = board.length; + + for (int i = 0; i < n; i++) { + row = new HashSet(); + col = new HashSet(); + block = new HashSet(); + for (int j = 0; j < n; j++) { + //Check row + if (!row.contains(board[i][j])) { + row.add(board[i][j]); + } else if (board[i][j] != '.'){ + return false; + } + //Check col + if (!col.contains(board[j][i])) { + col.add(board[j][i]); + } else if (board[j][i] != '.'){ + return false; + } + //check block + int c = j % 3 + 3 * (i % 3);//make use of how i and j increases + int r = j / 3 + 3 * (i / 3); + if (!block.contains(board[r][c])) { + block.add(board[r][c]); + } else if (board[r][c] != '.') { + return false; + } + } + } + + return true; + } +}; + /* Thoughts: @@ -35,10 +122,6 @@ A valid Sudoku board (partially filled) is not necessarily solvable. Only the fi */ class Solution { - /** - * @param board: the board - @return: wether the Sudoku is valid - */ public boolean isValidSudoku(char[][] board) { if (board == null || board.length == 0 || board[0].length == 0 || board.length != board[0].length) { return false; diff --git a/Java/Validate Binary Search Tree.java b/Java/Validate Binary Search Tree.java old mode 100644 new mode 100755 index 21cfec4..6036b86 --- a/Java/Validate Binary Search Tree.java +++ b/Java/Validate Binary Search Tree.java @@ -1,5 +1,23 @@ +M +1519720374 +tags: Tree, DFS, BST, Divide and Conquer + +如题, 验证是否是BST. + +#### DFS +- 查看每个parent-child关系: leftchild < root < rightChild; +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest +- imagine we know the two extreme border: Integer.MIN_VALUE, Integer.MAX_VALUE; pass node around and compare node vs. node.parent. +- 方法: 把root.val 传下来作为 max 或者 min, 然后检查children +- + +##### Note: +- min/max需要时long type. +- 如果题目真的给node.val = Integer.MAX_VALUE, 我们需要能够与之比较, long就可以. + +``` + /* -29% Accepted Given a binary tree, determine if it is a valid binary search tree (BST). Assume a BST is defined as follows: @@ -7,58 +25,77 @@ Given a binary tree, determine if it is a valid binary search tree (BST). The left subtree of a node contains only nodes with keys less than the node's key. The right subtree of a node contains only nodes with keys greater than the node's key. Both the left and right subtrees must also be binary search trees. -Example -An example: - - 1 - / \ - 2 3 - / - 4 - \ - 5 -The above binary tree is serialized as "{1,2,3,#,#,4,#,#,5}". +A single node tree is a BST + +Example: + + 2 + / \ +1 4 + / \ + 3 5 +The above binary tree is serialized as {2,1,4,#,#,3,5} (in level order). Tags Expand -Tree Binary Tree Binary Search Tree +Divide and Conquer Recursion Binary Search Tree Binary Tree -Thinking process: -Need to go through every node. -Need to determine parent node’s relationship with left/right child. -Then reminds me of Divide and Conquer. + +*/ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Thoughts: +Pass along the min/max value as border for sub trees */ +class Solution { + public boolean isValidBST(TreeNode root) { + return dfs(root, Long.MIN_VALUE, Long.MAX_VALUE) ; + } + public boolean dfs(TreeNode node, long min, long max) { + if (node == null) { + return true; + } + return node.val > min && node.val < max + && dfs(node.left, min, node.val) && dfs(node.right, node.val, max); + } +} + + +//recursively check if tree are BST, && them all /** - * Definition of TreeNode: + * Definition for a binary tree node. * public class TreeNode { - * public int val; - * public TreeNode left, right; - * public TreeNode(int val) { - * this.val = val; - * this.left = this.right = null; - * } + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } * } */ public class Solution { - /** - * @param root: The root of binary tree. - * @return: True if the binary tree is BST, or false - */ public boolean isValidBST(TreeNode root) { + return helper(root, Long.MIN_VALUE, Long.MAX_VALUE); + } + + public boolean helper(TreeNode root, long min, long max) { if (root == null) { return true; } - if (root.left != null && root.left.val >= root.val) { - return false; - } - if (root.right != null && root.right.val <= root.val) { - return false; - } - - boolean left = isValidBST(root.left); - boolean right = isValidBST(root.right); - - return left && right; + if (root.val < max && root.val > min && + helper(root.left, min, root.val) && + helper(root.right, root.val, max)) { + return true; + } + return false; } } + +``` \ No newline at end of file diff --git a/Java/Walls and Gates.java b/Java/Walls and Gates.java new file mode 100644 index 0000000..2766766 --- /dev/null +++ b/Java/Walls and Gates.java @@ -0,0 +1,191 @@ +M +1533438636 +tags: BFS, DFS + +给一个room 2D grid. 里面有墙-1, 门0, 还有empty space INF(Math.MAX_VALUE). + +对每个empty space而言, fill it with dist to nearest gate. + +#### DFS +- Form empty room: it can reach different gate, but each shortest length will be determined by the 4 directions. +- Option1(NOT applicable). DFS on INF, mark visited, summerize results of 4 directions. +- hard to resue: we do not know the direction in cached result dist[i][j] +- Option2. DFS on gate, and each step taken to each direction will +1 on the spot: distance from one '0'; +- Through dfs from all zeros, update each spot with shorter dist +- Worst time: O(mn), where entre rooms[][] are gates. It takes O(mn) to complete the iteration. Other gates be skipped by `if (rooms[x][y] <= dist) return;` + +#### BFS +- Exact same concept. Init with `Queue queue = new LinkedList()` + +``` +/* +You are given a m x n 2D grid initialized with these three possible values. + +-1 - A wall or an obstacle. +0 - A gate. +INF - Infinity means an empty room. We use the value 231 - 1 = 2147483647 +to represent INF as you may assume that the distance to a gate is less than 2147483647. +Fill each empty room with the distance to its nearest gate. +If it is impossible to reach a gate, it should be filled with INF. + +Example: + +Given the 2D grid: + +INF -1 0 INF +INF INF INF -1 +INF -1 INF -1 + 0 -1 INF INF +After running your function, the 2D grid should be: + + 3 -1 0 1 + 2 2 1 -1 + 1 -1 2 -1 + 0 -1 3 4 + + */ + +// No need of the visited. Simplified code: +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + + public void wallsAndGates(int[][] rooms) { + if (validate(rooms)) return; + + int m = rooms.length, n = rooms[0].length; + boolean[][] visited = new boolean[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (rooms[i][j] == 0) {// test all 0's with for loop + dfsHelper(rooms, i, j, 1); + } + } + } + } + + // mark grid with dist (compare if existed) + public void dfs(int[][] rooms, int x, int y, int dist) { + if (validateCoordinate(rooms, x, y)) return; + if (rooms[x][y] <= dist) return; + + rooms[x][y] = dist; // dist < room[x][y], so update rooms[x][y] with smaller dist + dfsHelper(rooms, x, y, dist + 1); // dfs if applicable + } + + private void dfsHelper(int[][] rooms, int x, int y, int dist) { + for (int i = 0; i < dx.length; i++) { + dfs(rooms, x + dx[i], y + dy[i], dist); + } + } + + private boolean validateCoordinate(int[][] rooms, int x, int y) { + return x < 0 || x >= rooms.length || y < 0 || y >= rooms[0].length || rooms[x][y] == -1 || rooms[x][y] == 0; + } + + private boolean validate(int[][] rooms) { + return rooms == null || rooms.length == 0 || rooms[0] == null || rooms[0].length == 0; + } + +} + + +// BFS: + +class Solution { + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + + public void wallsAndGates(int[][] rooms) { + if (validate(rooms)) return; + int m = rooms.length, n = rooms[0].length; + Queue queue = new LinkedList<>(); + + // Initi with 0 + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (rooms[i][j] == 0) {// test all 0's with for loop + queue.offer(new int[] {i, j}); + } + } + } + + // Process queue + while (!queue.isEmpty()) { + int[] point = queue.poll(); + bfsHelper(rooms, queue, point[0], point[1]); + } + } + + // Process with queue, and skip of value depth > existing depth. + private void bfsHelper(int[][] rooms, Queue queue, int x, int y) { + for (int i = 0; i < dx.length; i++) { + int mX = x + dx[i], mY = y + dy[i]; + if (validateCoordinate(rooms, mX, mY) || rooms[x][y] + 1 > rooms[mX][mY]) continue; + rooms[mX][mY] = rooms[x][y] + 1; + queue.offer(new int[] {mX, mY}); + } + } + + private boolean validateCoordinate(int[][] rooms, int x, int y) { + return x < 0 || x >= rooms.length || y < 0 || y >= rooms[0].length || rooms[x][y] == -1 || rooms[x][y] == 0; + } + + private boolean validate(int[][] rooms) { + return rooms == null || rooms.length == 0 || rooms[0] == null || rooms[0].length == 0; + } + +} +/* +Form empty room: it can reach different gate, but each shortest length will be determined by the 4 directions. +Option1. DFS on INF, mark visited, summerize results of 4 directions. it's hard to resue: we do not know the direction in cached result dist[i][j] +Option2. DFS on gate, and each step taken to each direction will +1 on the spot: distance from one '0'; through dfs from all zeros, update each spot with shorter dist +*/ +class Solution { + public void wallsAndGates(int[][] rooms) { + // check input + if (validate(rooms)) return; + + // find all 0's with for loop + int m = rooms.length, n = rooms[0].length; + boolean[][] visited = new boolean[m][n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (rooms[i][j] == 0 && !visited[i][j]) { + runDfs(rooms, visited, i, j, 0); + } + } + } + } + + // mark visited with dist (compare if existed) + public void dfs(int[][] rooms, boolean[][] visited, int x, int y, int dist) { + if (validateCoordinate(rooms, x, y)) return; + if (rooms[x][y] <= dist) return; + + // update rooms[x][y] + visited[x][y] = true; + rooms[x][y] = dist; //Math.min(rooms[x][y], dist); + + // dfs if applicable + runDfs(rooms, visited, x, y, dist); + visited[x][y] = false; + } + + private void runDfs(int[][] rooms, boolean[][] visited, int x, int y, int dist) { + dfs(rooms, visited, x-1, y, dist + 1); + dfs(rooms, visited, x+1, y, dist + 1); + dfs(rooms, visited, x, y-1, dist + 1); + dfs(rooms, visited, x, y+1, dist + 1); + } + + private boolean validateCoordinate(int[][] rooms, int x, int y) { + return x < 0 || x >= rooms.length || y < 0 || y >= rooms[0].length || rooms[x][y] == -1 || rooms[x][y] == 0; + } + + private boolean validate(int[][] rooms) { + return rooms == null || rooms.length == 0 || rooms[0] == null || rooms[0].length == 0; + } + +} +``` diff --git a/Java/Wiggle Sort.java b/Java/Wiggle Sort.java new file mode 100755 index 0000000..95f7b90 --- /dev/null +++ b/Java/Wiggle Sort.java @@ -0,0 +1,172 @@ +M +1516434841 +tags: Array, Sort + +方法1: +排序, nLog(n). 然后把直线上坡变成层叠山峰, 需要每隔几个(题目中是每隔2位)就做个swap 造成高低不平. +Note: 每隔山峰之间是相互没有关系的, 所以每次只要操心 [i], [i-1]两个位置就好了. + +方法2: +O(n) +看好奇数偶数位的规律, 然后根据题目给出的规律, 跑一遍, 每次只关注两个位置: 把不合适的[i], [i-1]调换位置就好了. + +方法3: +跟法2一样, 只是更巧妙一点罢了: +第一遍想太多. 其实做一个fall-through就能把问题解决,原因是因为: +这样的fall-through每次在乎两个element,可以一口气搞定,无关乎再之前的elements。 +特别的一点:flag来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + +``` +/* +Given an unsorted array nums, reorder it in-place such that nums[0] <= nums[1] >= nums[2] <= nums[3].... + +For example, given nums = [3, 5, 2, 1, 6, 4], one possible answer is [1, 6, 2, 5, 3, 4]. + +Tags: Array Sort +Similar Problems: (M) Sort Colors + +*/ + +/* +Thoughts: +Sort the orignal array. +Every 2 spots there is a peek, switch [i], [i-1] every 2 spots. +nLog(n). +draw a ascending line with dots at positions: 0, 1, 2, 3, 4 ... etc. +To make the dots into mountain, some dots needs to swap. According to the question, we can swap every 2 dots. +*/ +class Solution { + public void wiggleSort(int[] nums) { + if (nums == null || nums.length <= 1) { + return; + } + Arrays.sort(nums); + for (int i = 2; i < nums.length; i+=2) { + swap(nums, i, i - 1); + } + } + + private void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + +/* +Thoughts: +O(n) +Odd indexs are taking the peek, and even indexes are taking the bottom. +However, values that are two indexes apart does not need to follow any sequence rules. +That is, at every index, we only have to make sure [i] and [i-1] follows the rules provided. +Once we make one pass through the array, it should be swapped completely. + +same as the 'flag' solution below. +*/ +class Solution { + public void wiggleSort(int[] nums) { + if (nums == null || nums.length <= 1) { + return; + } + + for (int i = 1; i < nums.length; i++) { + if (i % 2 == 0 && nums[i] > nums[i - 1]) { + swap(nums, i, i - 1); + } + if (i % 2 == 1 && nums[i] < nums[i - 1]) { + swap(nums, i, i - 1); + } + /* + // Of course, in one line: + if ((i % 2 == 1 && nums[i] < nums[i - 1]) || (i % 2 == 0 && nums[i] > nums[i - 1])) { + swap(nums, i, i - 1); + } + */ + } + } + + private void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + +/* +O(n) +Attemp2, Thoughts: (http://www.cnblogs.com/easonliu/p/4798814.html) +Draw wiggle on original array. Whenever any postion i is not working with i-1, swap them. +Cases: +1. when nums[i] is supposed to >= nums[i - 1], however it's nums[i] < nums[i - 1], swap them. For example (nums[1] vs. nums[0]) +2. when nums[i] is supposed to <= nums[i - 1], however it's nums[i] > nums[i - 1], swap them. For example (nums[2] vs. nums[1]) +Specially, for case 2: can times a -1 on both side, to make nums[i] * -1 < nums[i - 1] * -1. Therefore we only need 1 if statement. + +Concept: whenver something does not work, fix it. (especailly now we are only taking caer of 2 elements at a time, so we can do it as a fall-through) +*/ +public class Solution { + public void wiggleSort(int[] nums) { + if (nums == null || nums.length <= 1) { + return; + } + int flag = 1; + for (int i = 1; i < nums.length; i++) { + if (flag * nums[i] < flag * nums[i - 1]) { + swap(nums, i, i - 1); + } + flag = -1 * flag; + } + } + + public void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + + +/* +Attempt1: +Wiggle sort, ofcourse can't do Arrays.sort()! Should sort in one pass. +Also, we don't need to make each wiggle elements in ascending order (1,(4),2,(5),3,(6) ... etc). I thought it over. +---- +List out the example of 1,2,3,4,5,6,7,8, want to move (5,6,7,8) and insert between (1,2,3,4). It follows the follows patter: +Assume total length = n, and we are moving index i = (1 ~ n) +Step1: swap (n + i)/2, and i, (where initially i == 1) +Step2: swap (n + i)/2, and i+1 +Step2: i+=2; + + +public class Solution { + public void wiggleSort(int[] nums) { + if (nums == null || nums.length == 0) { + return; + } + Arrays.sort(nums); + if (nums.length <= 2) { + return; + } + int leng = nums.length; + int ind1 = 0; + int ind2 = 0 + for (int i = 1; i < leng; i++) { + //Step1 + ind1 = (leng + i) / 2; + ind2 = i; + swap(ind1, ind2); + //Step2: + ind2 = i + 1; + swap(ind1, ind2); + } + } + + public void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} +*/ + +``` \ No newline at end of file diff --git a/Java/Wildcard Matching.java b/Java/Wildcard Matching.java new file mode 100644 index 0000000..9e4b5d1 --- /dev/null +++ b/Java/Wildcard Matching.java @@ -0,0 +1,140 @@ +H +1534348528 +tags: String, DP, Sequence DP, Double Sequence DP, Backtracking, Greedy + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + +``` +/* +Implement wildcard pattern matching with support for '?' and '*'. + +'?' Matches any single character. +'*' Matches any sequence of characters (including the empty sequence). + +The matching should cover the entire input string (not partial). + +The function prototype should be: +bool isMatch(const char *s, const char *p) + +Some examples: +isMatch("aa","a") → false +isMatch("aa","aa") → true +isMatch("aaa","aa") → false +isMatch("aa", "*") → true +isMatch("aa", "a*") → true +isMatch("ab", "?*") → true +isMatch("aab", "c*a*b") → false +*/ + +/* +Thoughts: +? -> any single character +* -> any empty, one or more of any characters. +dp[i][j]: can we match s[0 ~ i -1] and p[0 ~ j - 1]? true/false +There can be different conditions: +A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + +init: +when p == empty and s not empty, we defined it's not a match. +However, if s == empty, p can still be *, which will be a match, so no need to hard define dp[0][j] to be true/false. + +Time,Space O(MN) +*/ +class Solution { + public boolean isMatch(String s, String p) { + if (s == null || p == null) return false; + int m = s.length(), n = p.length(); + boolean[][] dp = new boolean[m + 1][n + 1]; + char[] ss = s.toCharArray(); + char[] pp = p.toCharArray(); + + for (int i = 0; i <= m; i++) { + for (int j = 0; j <= n; j++) { + if (i == 0 && j == 0) { + dp[i][j] = true; + continue; + } + if (j == 0) { // When p is empty but s is not empty, should not match + dp[i][j] = false; + continue; + } + dp[i][j] = false; + if (pp[j - 1] != '*') { + if (i >= 1 && (ss[i - 1] == pp[j - 1] || pp[j - 1] == '?')) { + dp[i][j] |= dp[i - 1][j - 1]; + } + } else { + dp[i][j] |= dp[i][j - 1];// '*' -> empty + if (i >= 1) { // '*' matches one or more of any character + dp[i][j] |= dp[i - 1][j]; + } + } + } + } + return dp[m][n]; + } +} + + +// Optimize, rolling array: Space O(N) +class Solution { + public boolean isMatch(String s, String p) { + if (s == null || p == null) { + return false; + } + int m = s.length(); + int n = p.length(); + boolean[][] dp = new boolean[2][n + 1]; + char[] ss = s.toCharArray(); + char[] pp = p.toCharArray(); + int curr = 0; + int prev = 1; + + // dp[0][j] = false; dp[i][0] = false; + + for (int i = 0; i <= m; i++) { + prev = curr; + curr = 1 - prev; + for (int j = 0; j <= n; j++) { + if (i == 0 && j == 0) { + dp[curr][j] = true; + continue; + } + if (j == 0) { // When p is empty but s is not empty, should not match + dp[curr][j] = false; + continue; + } + dp[curr][j] = false; + if (pp[j - 1] != '*') { + if (i >= 1 && (ss[i - 1] == pp[j - 1] || pp[j - 1] == '?')) { + dp[curr][j] = dp[prev][j - 1]; + } + } else { + dp[curr][j] |= dp[curr][j - 1];// '*' -> empty + if (i >= 1) { // '*' matches one or more of any character + dp[curr][j] |= dp[prev][j]; + } + } + } + } + return dp[curr][n]; + } +} +``` \ No newline at end of file diff --git a/Java/Wood Cut.java b/Java/Wood Cut.java old mode 100644 new mode 100755 index 0d862d3..2691560 --- a/Java/Wood Cut.java +++ b/Java/Wood Cut.java @@ -1,3 +1,14 @@ +M +1520921982 +tags: Binary Search + +二分的思想: 判断的是一个 validate() function, 而不是简单的'==' + +不需要sort! 为什么呢? 因为我们不是在given array上面二分, 我们是根据最大值在[0, max]上二分. + +Overall time: O(nLogM), where M = largest wood length + +``` /* Given n pieces of wood with length L[i] (integer array). Cut them into small pieces to guarantee you could have equal or more than k pieces with the same length. What is the longest length you can get from the n pieces of wood? Given L & k, return the maximum length of the small pieces. @@ -13,59 +24,62 @@ Tags Expand Binary Search -Thinking process: -Take the largest item. -Priorities: -1. Have to get calculatedK >= givenK -2. Meanwhile, want to maximize the smal piece. - -One thing not clear: do we have to use the given small piece? If we have to, we need to concern about the shortest wood piece. See commentted-out part -In this problem, however, we can abandon the small pieces, as long as the max_small_pieces can allow calculatedK >= givenK. - -Use binary search on the largest item: -1. if calculatedK < givenK: end = mid; -2. If calculated >= givenK, move start = mid as much as possible, which gives maximized small piece. - */ +/* +Thoughts: +Find a number, that can split all L's into K parts. The number should range: +[0, L(largest)] +1. binary search [0, largest value] +2. condition check: count pieces +3. Need find the mid: valid(mid) && !valid(mid + 1) - +Time: O(n) find max +Time: O(n * logM) +Overall: O(nLogM), where M = largest wood length +*/ public class Solution { - /** - *@param L: Given n pieces of wood with length L[i] - *@param k: An integer - *return: The maximum length of the small pieces. + /** + * @param L: Given n pieces of wood with length L[i] + * @param k: An integer + * @return: The maximum length of the small pieces */ public int woodCut(int[] L, int k) { if (L == null || L.length == 0 || k < 0) { return 0; - } - if (L.length == 1) { - return L[0] / (L[0] / k); } - Arrays.sort(L); - int start = 0; - int end = L[L.length - 1]; - int mid = 0; + + int m = L.length; int max = 0; - // int min = L[0]; + for (int i = 0; i < m; i++) { + max = Math.max(max, L[i]); + } + + int start = 0; + int end = max; while (start + 1 < end) { - mid = start + (end - start) / 2; - //if (mid > min) { - // end = mid; - // } else { - int count = 0; - for (int i : L) { - count += i / mid; - } - if (count < k) { - end = mid; - } else { - start = mid; - max = mid; - } - //} - }//end while - return max; + int mid = start + (end - start) / 2; + if (validate(mid, k, L) && mid + 1 < max && !validate(mid + 1, k , L)) { + return mid; + } else if (validate(mid, k, L)) { + start = mid; + } else { + end = mid; + } + } + + if (validate(end, k , L)) { + return end; + } else { + return start; + } + } + + private boolean validate(int size, int k, int[] L) { + int count = 0; + for (int length : L) { + count += length / size; + } + return count >= k; } } - +``` \ No newline at end of file diff --git a/Java/Word Break II.java b/Java/Word Break II.java new file mode 100755 index 0000000..91271ff --- /dev/null +++ b/Java/Word Break II.java @@ -0,0 +1,257 @@ +H +1519546502 +tags: DP, Backtracking, DFS, Memoization, Hash Table + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + +``` +/* +Given a string s and a dictionary of words dict, add spaces in s to construct a sentence +where each word is a valid dictionary word. + +Return all such possible sentences. + +For example, given +s = "catsanddog", +dict = ["cat", "cats", "and", "sand", "dog"]. + +A solution is ["cats and dog", "cat sand dog"]. + +Hide Company Tags Google Uber +Hide Tags Dynamic Programming Backtracking + +*/ +// Simplier solution, memoization +class Solution { + Map> memo; + public List wordBreak(String s, List dict) { + List rst = new ArrayList<>(); + if (s == null || s.length() == 0 || dict == null || dict.size() == 0) return rst; + + // dfs + memo = new HashMap<>(); + return dfs(new HashSet<>(dict), s); + } + + private List dfs(Set dict, String s) { + if (memo.containsKey(s)) return memo.get(s); // calculated, just return + List rst = new ArrayList<>(); + if (s.length() == 0) return rst; + + if (dict.contains(s)) rst.add(s); // total match word + + // loop over form index -> n, find candidates, validate, dfs + StringBuffer sb = new StringBuffer(); + for (int i = 1; i < s.length(); i++) { + sb.append(s.charAt(i - 1)); + if (!dict.contains(sb.toString())) { + continue; + } + String suffix = s.substring(i); + List segments = dfs(dict, suffix); + for (String segment : segments) { + rst.add(sb.toString() + " " + segment); + } + } + memo.put(s, rst); + return rst; + } +} + +// Just use memo, with void dfs. +class Solution { + Map> memo = new HashMap<>(); + public List wordBreak(String s, List dict) { + List rst = new ArrayList<>(); + if (s == null || s.length() == 0 || dict == null || dict.size() == 0) return rst; + + dfs(new HashSet<>(dict), s); + return memo.get(s); + } + + private void dfs(Set dict, String s) { + List rst = new ArrayList<>(); + if (dict.contains(s)) rst.add(s); // match word, populate suffix variation list + + // loop over form index -> n: set prefix and corss-match with all possible suffix variations + for (int i = 1; i < s.length(); i++) { + String prefix = s.substring(0, i); + if (!dict.contains(prefix)) continue; // validation with dict + + String suffix = s.substring(i); + if (suffix.length() > 0 && !memo.containsKey(suffix)) { // if calculated, skip dfs + dfs(dict, suffix); + } + List segments = memo.get(suffix); + for (String segment : segments) { + rst.add(prefix + " " + segment); + } + } + memo.put(s, rst); // save result + } +} + +/* +Thoughts: +Dict is a look up table. We need to backtrack on s && result list. +When iterating over s && worst case of each remaining letter is a word, time: n * (n - 1) * (n - 2) * (n - 3) ... + 1 => O(n!) + +return result when s string is traversed completely. + +Questions to ask: +1. all chars in s need to be used? +2. can we jump over chars in s? NO +3. can we resume item in dict? YES + +Problem: +dict.contains() is O(logN), so overall time went up to O(N! * LogN). Should improve. + +DP1: +Is (i,j) a valid word form dictionary? DP[i][j] represents true/false valid word. + +DP2: +for s[i,j] to be a valid entry in the result, s[0, i] has to be validated as well. +dp[i] represents: up to ith index, all substring ahead are valid +*/ +class Solution { + public List wordBreak(String s, List wordDict) { + List result = new ArrayList<>(); + if(s == null || s.length() == 0 || wordDict == null || wordDict.size() == 0) { + return result; + } + + boolean[][] isWord = isWord(wordDict, s); + boolean[] isValid = validatePossibility(wordDict, isWord, s); + helper(result, new ArrayList(), isWord, isValid, s, 0); + return result; + } + + public void helper(List result, List list, boolean[][] isWord, boolean[] isValid, String s, int start) { + if (!isValid[start]) { + return; + } + if (start >= s.length()) { + StringBuffer sb = new StringBuffer(); + for (String word : list) { + sb.append(word + " "); + } + result.add(sb.toString().trim()); + return; + } + for (int i = start; i < s.length(); i++) { + if (!isWord[start][i]) {//O(1) + continue; + } + list.add(s.substring(start, i + 1)); + helper(result, list, isWord, isValid, s, i + 1); + list.remove(list.size() - 1); + } + } + + // isWord[i][j]: is subString s[i, j] a word from dictionary? + public boolean[][] isWord(List wordDict, String s) { + int n = s.length(); + boolean[][] isWord = new boolean[n][n]; + + for (int i = 0; i < n; i++) { + for (int j = i; j < n; j++) { + isWord[i][j] = wordDict.contains(s.substring(i, j + 1)); + } + } + return isWord; + } + + /* + Verify: up to i letters, is it possible to satisfy the 'word break' rules? + Need to consider ith index: sequence DP, create dp[n + 1]; + dp[i] = substring(i, j) valid && dp[after substring, j to end] + Calculating DP from right-side of the string: from the right side, we need to know the substring is valid, then move to left and check further + */ + public boolean[] validatePossibility(List wordDict, boolean[][] isWord, String s) { + //optimize, find maxLength in wordDict to restrict string growth + int maxLen = 0; + for (String word : wordDict) { + maxLen = Math.max(maxLen, word.length()); + } + + int n = s.length(); + boolean[] isValid = new boolean[n + 1]; + isValid[n] = true; + for (int i = n - 1; i >= 0; i--) { + for (int j = i; j < n; j++) { + if (isWord[i][j] && isValid[j + 1]) { + isValid[i] = true; + break; + } + } + } + return isValid; + } +} + +// Valid, but timeout solution +class Solution { + public List wordBreak(String s, List dict) { + // check s, dict, define + List rst = new ArrayList<>(); + if (s == null || s.length() == 0 || dict == null || dict.size() == 0) { + return rst; + } + + // dfs + dfs(rst, new ArrayList<>(), new HashSet<>(dict), s, 0); + + return rst; + } + + private void dfs(List rst, List list, Set dict, String s, int index) { + if (index == s.length()) { + StringBuffer sb = new StringBuffer(); + for (String str : list) { + sb.append(str + " "); + } + rst.add(sb.toString().trim()); + return; + } + + // loop over form index -> n, find candidates, validate, dfs + StringBuffer sb = new StringBuffer(); + for (int i = index; i < s.length(); i++) { // O(n - index) + sb.append(s.charAt(i)); + + if (!dict.contains(sb.toString())) { + continue; + } + + list.add(sb.toString()); + dfs(rst, list, dict, s, i + 1); + list.remove(list.size() - 1); + } + } +} + +``` diff --git a/Java/Word Break.java b/Java/Word Break.java old mode 100644 new mode 100755 index 1ff9ff8..7c77b81 --- a/Java/Word Break.java +++ b/Java/Word Break.java @@ -1,18 +1,133 @@ +M +1531713769 +tags: DP, Sequence DP, Hash Table +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- true/false problem, think about dp +- 子问题: 前i个字母, 是否可以有valid break +- 检查dp[j] && `if substring(j, i) valid`, for all j = [0 ~ i] +- dp = new boolean[n + 1]; dp[0] = true; +- goal: if there is a j, `dp[j] == true && word[j, n] in dict`. Need iterate over i = [0 ~ n], also j = [0, i] +- 注意, 用set代替list, 因为要用 contains(). + +#### Previous notes +##### 方法2(attempt4 code) +- 与Word BreakII用同样的DP。 +- valid[i]: 记录从i到valid array末尾是否valid. + +##### 方法1:(attempt3 code) +- state,rst[i]: 从[0~i] inclusive的string是否可以在dict中break开来找到? +- function: rst[i] = true if (rst[i - j] && set.contains(s.substring(i - j, i))); j in[0~i] +- 1. rst[i - j] 记录的是[0, i-j]这一段是否可以break后在dict找到。 +- 2. 若true,再加上剩下所有[i-j, i]都能在dict找到,那么rst[i] = rst[0, i - j] && rst[i-j, i] == true +- 优化:找dict里面最长string, 限制j的增大。 + + +``` /* -Given a string s and a dictionary of words dict, determine if s can be break into a space-separated sequence of one or more dictionary words. +Given a non-empty string s and a dictionary wordDict containing a list of non-empty words, +determine if s can be segmented into a space-separated sequence of one or more dictionary words. + +Note: + +The same word in the dictionary may be reused multiple times in the segmentation. +You may assume the dictionary does not contain duplicate words. +Example 1: + +Input: s = "leetcode", wordDict = ["leet", "code"] +Output: true +Explanation: Return true because "leetcode" can be segmented as "leet code". +Example 2: + +Input: s = "applepenapple", wordDict = ["apple", "pen"] +Output: true +Explanation: Return true because "applepenapple" can be segmented as "apple pen apple". + Note that you are allowed to reuse a dictionary word. +Example 3: -Example -Given s = "lintcode", dict = ["lint", "code"]. +Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] +Output: false -Return true because "lintcode" can be break as "lint code". +*/ -Tags Expand -String Dynamic Programming +/* +dp[i]: for first i letters, is it valid? +for dp[i] to be valid, the substring[0 ~ i) need to be valid: +dp[i] = dp[j] && sub(j, i); j = [0 ~ i] +dp[0] = true; // +init dp[] = new boolean[n + 1]; */ + +class Solution { + public boolean wordBreak(String s, List dict) { + // check edge case + if (s == null || s.length() == 0 || dict == null || dict.size() == 0) return false; + + Set words = new HashSet<>(dict); + // init dp + int n = s.length(); + boolean[] dp = new boolean[n + 1]; + dp[0] = true; + + // for loop from dp[1] ~ dp[n] + for (int i = 1; i <= n; i++) { + for (int j = 0; j < i; j++) { + dp[i] |= dp[j] && words.contains(s.substring(j, i)); + if (dp[i]) break; + } + } + + return dp[n]; + } +} + + +// Previous notes +/* +Attempt4: same as attempt3, but reversed how to build the validation matrix. (Same style as in Word Break II) +valid[i]: 从i 到valid.length末尾是否valid +*/ +public class Solution { + public boolean wordBreak(String s, Set dict) { + if (s == null || dict.contains(s)) { + return true; + } + + boolean[] valid = new boolean[s.length() + 1]; + valid[s.length()] = true; + int maxLength = calMaxLength(dict); + for (int i = s.length() - 1; i >= 0; i--) { + for (int j = i; j < s.length() && (i - j) <= maxLength; j++) {//iterate [0 ~ i] + if (valid[j + 1] && dict.contains(s.substring(i, j + 1))) { + valid[i] = true; + break; + } + } + } + + return valid[0]; + } + + public int calMaxLength(Set dict) { + int length = 0; + for (String word : dict) { + length = Math.max(length, word.length()); + } + return length; + } +} + + /* -Attemp3: -Optimize attempt2: If the input s is super long, but Dict does not have that long string, then we should avoid that case, so to save time. That is, check dict's strings' max length, and incldue that in 2nd-level for loop +Attempt3: +Optimize attempt2: If the input s is super long, but Dict does not have that long string, +then we should avoid that case, so to save time. That is, check dict's strings' max length, +and incldue that in 2nd-level for loop j: last word's length, range from 0 to i. [i - j]: the first index of current word @@ -28,17 +143,33 @@ public boolean wordBreak(String s, Set dict) { if (s == null || dict.contains(s)) { return true; } + +/* boolean[] rst = new boolean[s.length() + 1]; rst[0] = true; int maxLength = calMaxLength(dict); for (int i = 0; i <= s.length(); i++) { - for (int j = 0; j <= i && j <= maxLength; j++) { + for (int j = 0; j <= i && j <= maxLength; j++) {//iterate [0 ~ i] if (rst[i - j] && dict.contains(s.substring(i - j, i))) { rst[i] = true; break; } } } + +*/ + boolean[] rst = new boolean[s.length() + 1]; + rst[s.length()] = true; + int maxLength = calMaxLength(dict); + for (int i = s.length() - 1; i >= 0; i--) { + for (int j = i; j < s.length() && (i - j) <= maxLength; j++) {//iterate [0 ~ i] + if (rst[i + 1] && dict.contains(s.substring(i, j))) { + rst[i] = true; + break; + } + } + } + return rst[s.length()]; } @@ -54,34 +185,65 @@ public int calMaxLength(Set dict) { /* +DP Attemp2, Thought: Use boolena to denote rst[i]: s[0,i-1] can be break to match dict. For the ease to explain, let's consider rst[i+1] with actually string s[0,i]; How to calculate rst[i+1]? - As long as there is at least 1 way to break s[0, i], that would work. so do a for loop to check on string s[0, i]: - For each i, use another index j, j = 0 ~ i. If rst[j] works and s[j,i+1] is in dict, that makes rst[i+1] = true. + As long as there is at least 1 way to break s[0, i], that would work. so do a for loop to check on string s[0, i]: + For each i, use another index j, j = 0 ~ i. If rst[j] works and s[j,i+1] is in dict, that makes rst[i+1] = true. Correct: however excceeds time limit at 97% correct */ public class Solution { public boolean wordBreak(String s, Set dict) { - if (s == null || dict.contains(s)) { - return true; - } - boolean[] rst = new boolean[s.length() + 1]; - rst[0] = true; - for (int i = 0; i < s.length(); i++) { - for (int j = 0; j <= i; j++) { - if (rst[j] && dict.contains(s.substring(j, i + 1))) { - rst[i + 1] = true; - break; - } - } - } - return rst[s.length()]; + if (s == null || dict.contains(s)) { + return true; + } + boolean[] rst = new boolean[s.length() + 1]; + rst[0] = true; + for (int i = 0; i < s.length(); i++) { + for (int j = 0; j <= i; j++) { + if (rst[j] && dict.contains(s.substring(j, i + 1))) { + rst[i + 1] = true; + break; + } + } + } + return rst[s.length()]; } } +//Timeout, DFS +//Break the word into pieces: recursively break using start,end pointer. +//End pointer always moves forward, but start pointer may not. +public class Solution { + public boolean wordBreak(String s, Set wordDict) { + if (s == null || s.length() == 0 || wordDict == null || wordDict.size() == 0) { + return false; + } + + return helper(s, wordDict, 0, 0); + } + public boolean helper(String s, Set set, int start, int end) { + if (end == s.length() - 1) { + if (start == end) { + return true; + } + return set.contains(s.substring(start)); + } + //no break + boolean noBreak = helper(s, set, start, end + 1); + + //break: + boolean hasBreak = false; + if (set.contains(s.substring(start, end + 1))) { + hasBreak = helper(s, set, end + 1, end + 1); + } + + return noBreak || hasBreak; + } +} @@ -91,4 +253,5 @@ public boolean wordBreak(String s, Set dict) { Create DP[i][j] based on dict that says: combine i number of dict strings and j number of dict strings, do we have a combined string that contains the target? However, this seems confusing and over-complex. We only have 1 set of variables: dict, so maybe it's now wise to create 2D DP[][]. -*/ \ No newline at end of file +*/ +``` diff --git a/Java/Word Ladder II.java b/Java/Word Ladder II.java old mode 100644 new mode 100755 index 8a38c03..a6bbd6f --- a/Java/Word Ladder II.java +++ b/Java/Word Ladder II.java @@ -1,9 +1,199 @@ +H +1533502615 +tags: Array, String, Backtracking, BFS, DFS, Hash Table + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + +``` +/** +LeetCode: interface has List, and input dict has to contain end word, but it does not contain start word. +Given two words (beginWord and endWord), and a dictionary's word list, +find all shortest transformation sequence(s) from beginWord to endWord, such that: + +Only one letter can be changed at a time +Each transformed word must exist in the word list. Note that beginWord is not a transformed word. +Note: + +Return an empty list if there is no such transformation sequence. +All words have the same length. +All words contain only lowercase alphabetic characters. +You may assume no duplicates in the word list. +You may assume beginWord and endWord are non-empty and are not the same. +Example 1: + +Input: +beginWord = "hit", +endWord = "cog", +wordList = ["hot","dot","dog","lot","log","cog"] + +Output: +[ + ["hit","hot","dot","dog","cog"], + ["hit","hot","lot","log","cog"] +] +Example 2: + +Input: +beginWord = "hit" +endWord = "cog" +wordList = ["hot","dot","dog","lot","log"] + +Output: [] + +Explanation: The endWord "cog" is not in wordList, therefore no possible transformation. + +*/ +// 69% /* -Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that: + +- Distance Map: shortest distance from to mutate into target key string. +- Mutation Map>: list possible source strings to mutate into target key string. +*/ +/* +- Distance Map: shortest distance from to mutate into target key string. +- Mutation Map>: list possible source strings to mutate into target key string. +*/ +// Bi-directional search +class Solution { + Set dict; + Map> mutation = new HashMap<>(); + Map distance = new HashMap<>(); + + public List> findLadders(String start, String end, List wordList) { + List> rst = new ArrayList<>(); + dict = new HashSet<>(wordList); + if (!dict.contains(end)) return rst; + + prep(start); + //dfs(start, end, new ArrayList<>(), rst); // option1 + bfs(start, end, rst); // option2 + return rst; + } + + //BFS/Prep to populate mutation and distance map. + public void prep(String start) { + //Init + Queue queue = new LinkedList<>(); + dict.add(start); + queue.offer(start); + distance.put(start, 0); + for (String s : dict) { + mutation.put(s, new ArrayList<>()); + } + // process queue + while(!queue.isEmpty()) { + String str = queue.poll(); + List list = transform(str); + + for (String s : list) { + mutation.get(s).add(str); + if (!distance.containsKey(s)) { // only record dist->s once, as shortest + distance.put(s, distance.get(str) + 1); + queue.offer(s); + } + } + } + } + + // Option2: bfs, bi-directional search + public void bfs(String start, String end, List> rst) { + Queue> queue = new LinkedList<>(); + List list = new ArrayList<>(); + list.add(end); + queue.offer(new ArrayList<>(list)); + while (!queue.isEmpty()) { + int size = queue.size(); + while (size > 0) { + list = queue.poll(); + String str = list.get(0); + + for (String s : mutation.get(str)) {//All prior-mutation sources + list.add(0, s); + if (s.equals(start)) { + rst.add(new ArrayList<>(list)); + } else if (distance.containsKey(s) && distance.get(str) - 1 == distance.get(s)) { + //Only pick those that's 1 step away: keep minimum steps for optimal solution + queue.offer(new ArrayList<>(list)); + } + list.remove(0); + } + size--; + } + } + } + + // Option1: DFS to trace back from end string to start string. If reach start string, save result. + // Use distance to make sure only trace to 1-unit distance candidate + public void dfs(String start, String str, List path, List> rst) { + path.add(0, str); + if (str.equals(start)) { + rst.add(new ArrayList(path)); + path.remove(0); + return; + } + //Trace 1 step towards start via dfs + for (String s : mutation.get(str)) {//All prior-mutation sources + //Only pick those that's 1 step away: keep minimum steps for optimal solution + if (distance.containsKey(s) && distance.get(str) - 1 == distance.get(s)) { + dfs(start, s, path, rst); + } + } + path.remove(0); + } + + //Generate all possible mutations for word. Check against dic and skip word itself. + private List transform(String word) { + List candidates = new ArrayList<>(); + StringBuffer sb = new StringBuffer(word); + for (int i = 0; i < sb.length(); i++) { + char temp = sb.charAt(i); + for (char c = 'a'; c <= 'z'; c++) { + if (temp == c) continue; + sb.setCharAt(i, c); + String newWord = sb.toString(); + if (dict.contains(newWord)) { + candidates.add(newWord); + } + } + sb.setCharAt(i, temp);//backtrack + } + return candidates; + } +} + +/* +LintCode: interface has Set, also, input dict does not contain end word. +Given two words (start and end), and a dictionary, +find all shortest transformation sequence(s) from start to end, such that: Only one letter can be changed at a time Each intermediate word must exist in the dictionary -Have you met this question in a real interview? Yes + Example Given: start = "hit" @@ -126,8 +316,8 @@ public ArrayList expand(String str, Set dict) { //2. If use StringBuffer strCheck to check if particular sequence exist, then exceed time limit. //It looks like we'd use DFS for final results. public class Solution { - private Queue q = new LinkedList(); - private Queue> backtrackList = new LinkedList>(); + private Queue q = new LinkedList(); + private Queue> backtrackList = new LinkedList>(); private Set dict; private String end; private int level = 1; @@ -136,145 +326,59 @@ public class Solution { public List> findLadders(String start, String end, Set dict) { if (start == null || end == null || dict == null || start.length() != end.length()) { - return rst; - } - this.dict = dict; - this.end = end; - ArrayList head = new ArrayList(); - head.add(start); - q.offer(start); - backtrackList.offer(head); - while(!q.isEmpty()) {//BFS - int size = q.size();//Fix size - level++; - for (int k = 0; k < size; k++) {//LOOP through existing queue: for this specific level - String str = q.poll(); - ArrayList list = backtrackList.poll(); - validateMutations(str, list); - }//END FOR K - }//END WHILE - - List> minRst = new ArrayList>(); - for (int i = 0; i < rst.size(); i++) { - if (rst.get(i).size() == len) { - minRst.add(rst.get(i)); - } - } - return minRst; + return rst; + } + this.dict = dict; + this.end = end; + ArrayList head = new ArrayList(); + head.add(start); + q.offer(start); + backtrackList.offer(head); + while(!q.isEmpty()) {//BFS + int size = q.size();//Fix size + level++; + for (int k = 0; k < size; k++) {//LOOP through existing queue: for this specific level + String str = q.poll(); + ArrayList list = backtrackList.poll(); + validateMutations(str, list); + }//END FOR K + }//END WHILE + + List> minRst = new ArrayList>(); + for (int i = 0; i < rst.size(); i++) { + if (rst.get(i).size() == len) { + minRst.add(rst.get(i)); + } + } + return minRst; } public void validateMutations(String str, ArrayList list) { - if (list.size() > len) {//No need to digger further if list is already greater than min length - return; - } - for (int i = 0; i < str.length(); i++) {//Alternate each letter position - for (int j = 0; j < 26; j++) {//Alter 26 letters + if (list.size() > len) {//No need to digger further if list is already greater than min length + return; + } + for (int i = 0; i < str.length(); i++) {//Alternate each letter position + for (int j = 0; j < 26; j++) {//Alter 26 letters if (str.charAt(i) == (char)('a' + j)) { continue; } - String newStr = str.substring(0, i) + (char)('a' + j) + str.substring(i + 1); - - ArrayList temp = (ArrayList)list.clone(); - temp.add(newStr); - if (dict.contains(newStr)) { - if (newStr.equals(end)) {//Found end - len = Math.min(len, level); - rst.add(temp); - } else { - q.offer(newStr); - backtrackList.offer(temp); - } - } - }//END FOR J - }//END FOR I - } -} - - + String newStr = str.substring(0, i) + (char)('a' + j) + str.substring(i + 1); -//Solution from NineChapter, commented: - -/* -public class Solution { - public List> findLadders(String start, String end,Set dict) { - List> ladders = new ArrayList>(); - Map> map = new HashMap>(); - Map distance = new HashMap(); - - dict.add(start); - dict.add(end); - - bfs(map, distance, start, end, dict); - //Now at this step, we have: - //a distance map of all mutated string from start, - //a map of mutation and its list of 'pre-mutation' string - //dict: includes start and end - List path = new ArrayList(); - - dfs(ladders, path, end, start, distance, map); - - return ladders; - } - //crt: is not necessarily the 'end', since this is a recursive method - //crt at first is the 'end' string, then it's switching to other strings inorder to finally matches 'start' - void dfs(List> ladders, List path, String crt, - String start, Map distance, - Map> map) { - path.add(crt); - if (crt.equals(start)) {//Now, finally if the crt makes it to start and equals to start, we found a match. - Collections.reverse(path);//We had a reversed path - ladders.add(new ArrayList(path));//add - Collections.reverse(path);//need to reverse it back, becase we need 'path' for more recursive calls. - } else { - for (String next : map.get(crt)) {//Find all possible tranformations/mutations that can turn itself into crt: we have a ArrayList of candidates (pre-mutated strings) - if (distance.containsKey(next) && distance.get(crt) == distance.get(next) + 1) { //if that mutation is just 1 step different, that's good, which means these mutation takes minimum of 1 step to happen. Note: we are comparing the distance to start point. - dfs(ladders, path, next, start, distance, map);//If that's the case, pass varibles to next level: use new path (with crt added), and use the 'next' string (which is 1 step closer to start) for next level of searching. - } - } - } - path.remove(path.size() - 1);//remove that ending crt, since 'path' is shared in recursive methods, need to keep it cleaned. - } -//map: each string in the dict (including start, end) represents a key, and the value is a ArrayList of string. - void bfs(Map> map, Map distance, String start, String end, Set dict) { - Queue q = new LinkedList(); - q.offer(start); - distance.put(start, 0);//Distance: key: str, value: distance value from start. - for (String s : dict) { - map.put(s, new ArrayList()); - } - - while (!q.isEmpty()) { - String crt = q.poll();//Get head of queue, the item currently we are looking at. Called X. - - List nextList = expand(crt, dict);//generate all possible mutations (must exist in dict) - for (String next : nextList) {//For all mutations - map.get(next).add(crt);//append X to end of all of the mutated string (this will become a reverse order). This creates a path of mutation - if (!distance.containsKey(next)) {//If that mutated string never occured: - distance.put(next, distance.get(crt) + 1);//add distance to this mutation. This is fixed and will never change, btw. This becomes a list of all mutations and distance from start. - q.offer(next);//Add this mutation to queue. - } - } - } - } -//all possible mutations based on 1 str polled from the queue. - List expand(String crt, Set dict) { - List expansion = new ArrayList(); - - for (int i = 0; i < crt.length(); i++) { - for (char ch = 'a'; ch <= 'z'; ch++) { - if (ch != crt.charAt(i)) { - String expanded = crt.substring(0, i) + ch - + crt.substring(i + 1); - if (dict.contains(expanded)) { - expansion.add(expanded); + ArrayList temp = (ArrayList)list.clone(); + temp.add(newStr); + if (dict.contains(newStr)) { + if (newStr.equals(end)) {//Found end + len = Math.min(len, level); + rst.add(temp); + } else { + q.offer(newStr); + backtrackList.offer(temp); } } - } - } - return expansion; + }//END FOR J + }//END FOR I } } - -*/ \ No newline at end of file +``` \ No newline at end of file diff --git a/Java/Word Ladder.java b/Java/Word Ladder.java old mode 100644 new mode 100755 index d15adbf..cd9d2a1 --- a/Java/Word Ladder.java +++ b/Java/Word Ladder.java @@ -1,5 +1,25 @@ +M +1527825811 +tags: BFS + +给一串string[], 需要找shortest distance to change from wordA -> wordB. (限制条件细节见原题) + +#### BFS +- 通常, 给一个graph(这道题可以把beginWord看成一个graph的起始node), 找shortest path用BFS +- 在start string基础上,string的每个字母都遍历所有26个字母 +- visited 过的 从wordList里去掉 +- time: word length m, there can be n candidates => O(mn) +- 但是总是exceed time limit on LeetCode. However, it passes LintCode: +- 原因是 LeetCode给的是list, list.contains(), list.remove() 都是 O(logn) time!!! +- convert to set first. + +#### Trie +- timeout, overkill + +``` /* -Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that: +Given two words (start and end), and a dictionary, +find the length of shortest transformation sequence from start to end, such that: Only one letter can be changed at a time Each intermediate word must exist in the dictionary @@ -20,13 +40,180 @@ Given two words (start and end), and a dictionary, find the length of shortest t Breadth First Search Thoughts: -Use the dict (check if mutation exist in dict) as base to create a directed graph, use BFS to find shortest path. +Use the dict (check if mutation exist in dict) as base to create a directed graph, +use BFS to find shortest path. Note: Be careful with queue size when trying to do for loop on it. Use a pre-fixed size = q.size(), in case queue's size changes during for loop. */ -//Solution1: nested for loop +// Pass LeetCode. Use count to track the candidates +class Solution { + public int ladderLength(String beginWord, String endWord, List wordList) { + // check edge case + if (wordList == null || !wordList.contains(endWord)) { + return 0; + } + + // build queue, visited set + Queue queue = new LinkedList<>(); + queue.offer(beginWord); + Set words = new HashSet<>(wordList); + + // process one level of queue each time, count + int count = 1; + while (!queue.isEmpty()) { + int size = queue.size(); + count++; + for (int i = 0; i < size; i++) { + String word = queue.poll(); + List candidates = transform(words, word); + for (String candidate: candidates) { + if (endWord.equals(candidate)) { + return count; + } + queue.offer(candidate); + } + } + }// END WHILE + + return 0; + } + + // switch each char with 26 lowercase letters, and return candidates + private List transform(Set words, String word) { + List candidates = new ArrayList<>(); + StringBuffer sb = new StringBuffer(word); + for (int i = 0; i < sb.length(); i++) { + char temp = sb.charAt(i); + for (char c = 'a'; c <= 'z'; c++) { + if (temp == c) { + continue; + } + sb.setCharAt(i, c); + String newWord = sb.toString(); + if (words.remove(newWord)) { + candidates.add(newWord); + } + } + sb.setCharAt(i, temp); + } + return candidates; + } +} + + +// Pass for Leetcode. Use wordNode class to track +class Solution { + class WordNode { + String word; + int numSteps; + + public WordNode(String word, int numSteps) { + this.word = word; + this.numSteps = numSteps; + } + } + public int ladderLength(String beginWord, String endWord, List wordList) { + // check edge case + if (wordList == null || !wordList.contains(endWord)) { + return 0; + } + + // build queue, visited set + Queue queue = new LinkedList<>(); + queue.offer(new WordNode(beginWord, 1)); + Set words = new HashSet<>(wordList); + + // process one level of queue each time, count + while (!queue.isEmpty()) { + WordNode node = queue.poll(); + String word = node.word; + int numSteps = node.numSteps; + + if (word.equals(endWord)) { + return node.numSteps; + } + //char[] arr = word.toCharArray(); + StringBuffer sb = new StringBuffer(word); + for (int i = 0; i < sb.length(); i++) { + char temp = sb.charAt(i); + for (char c = 'a'; c <= 'z'; c++) { + if (temp == c) { + continue; + } + sb.setCharAt(i, c); + String newWord = sb.toString(); + if (words.remove(newWord)) { + queue.offer(new WordNode(newWord, numSteps + 1)); + } + } + sb.setCharAt(i, temp); + } + }// END WHILE + + return 0; + } +} + + + +// Pass for LintCode +class Solution { + class WordNode { + String word; + int numSteps; + + public WordNode(String word, int numSteps) { + this.word = word; + this.numSteps = numSteps; + } + } + public int ladderLength(String beginWord, String endWord, Set wordList) { + wordList.add(endWord); + + // build queue, visited set + Queue queue = new LinkedList<>(); + queue.offer(new WordNode(beginWord, 1)); + + // process one level of queue each time, count + while (!queue.isEmpty()) { + WordNode node = queue.poll(); + String word = node.word; + int numSteps = node.numSteps; + + if (word.equals(endWord)) { + return node.numSteps; + } + //char[] arr = word.toCharArray(); + StringBuffer sb = new StringBuffer(word); + for (int i = 0; i < sb.length(); i++) { + char temp = sb.charAt(i); + for (char c = 'a'; c <= 'z'; c++) { + if (temp == c) { + continue; + } + sb.setCharAt(i, c); + String newWord = sb.toString(); + if (wordList.remove(newWord)) { + queue.add(new WordNode(newWord, numSteps + 1)); + } + } + sb.setCharAt(i, temp); + } + }// END WHILE + + return 0; + } +} + + + + + + +// Previous notes: +//Solution1: nested for loop, leetcode timesout public class Solution { public int ladderLength(String start, String end, Set dict) { if (start == null || end == null || dict == null || start.length() != end.length()) { @@ -124,3 +311,183 @@ else if (i == str.length() - 1) { }//END FOR I } } + + + + + + + +/* + Use a new method in Trie: + getChildrenMap(string s): get the children hashmap at last char of s. + getCurrMap(s): get the hashmap where the last char of s is at. + + However, still timeout in LeetCode. So there might be some case that's using extra calculation. + It should ideally be faster than brutle force. +*/ +import java.io.*; +import java.util.*; + +class Solution { + + class TrieNode { + String str; + boolean isEnd; + boolean visited; + HashMap children; + public TrieNode () { + this.isEnd = false; + this.visited = false; + this.str = ""; + children = new HashMap(); + } + } + + + + public TrieNode root = new TrieNode(); + public int leng = Integer.MAX_VALUE; + public int ladderLength(String beginWord, String endWord, Set wordList) { + if (beginWord == null || endWord == null || beginWord.length() != endWord.length() + || wordList == null || wordList.size() == 0) { + return 0; + } + + //build Trie + for (String s : wordList) { + insert(s); + } + dfs(beginWord, endWord, 1); + + return leng; + } + + public void dfs(String start, String end, int step) { + if (step >= leng) { + return; + } + + if (compare(start, end) == 0) { + leng = Math.min(leng, step); + return; + } + //catch last step, so it covers the case where last change is not in dictionary + if (compare(start, end) == 1) { + leng = Math.min(leng, step + 1); + return; + } + + for (int i = 0; i < start.length(); i++) {//try to replace every index + String s = start.substring(0, i + 1); + HashMap candidates = getCurrentMap(s); + + //If char(i) not in dictinary, go back up one level, + //try to find all possible children candidates of previous char. Keep going with the process + if (candidates == null) { + s = start.substring(0, i); + candidates = getChildrenMap(s); + //If prev char is not found neither, fail it. + //Example, when 'b' in "ab" not found, try to find 'a' and its children; however, if 'a' not found neither, cut it off here + if (candidates == null) { + continue; + } + } + + //Replace char at i with all candidates existing in Trie dictionary + for (Map.Entry entry : candidates.entrySet()) { + char c = entry.getKey(); + String newStr = start.substring(0,i) + c + start.substring(i+1); + if (c != start.charAt(i) && !entry.getValue().visited) { + candidates.get(c).visited = true; + dfs(newStr, end, step + 1); + } + } + } + } + + /**************************Trie section *********************/ + //In this problem ,didin't use startWith() or search(). + //Instead, use a similar function, getChildrenMap. See below + + public void insert (String s){ + char[] arr = s.toCharArray(); + TrieNode node = root; + for (int i = 0; i < arr.length; i++) { + char c = arr[i]; + if (!node.children.containsKey(c)) { + node.children.put(c, new TrieNode()); + } + node = node.children.get(c); + if (i == arr.length - 1) { + node.isEnd = true; + node.str = s; + } + } + } + + /*Return the HashMap where the last char of s is in*/ + public HashMap getCurrentMap(String s) { + char[] arr = s.toCharArray(); + TrieNode node = root; + for (int i = 0; i < arr.length; i++) { + char c = arr[i]; + if (!node.children.containsKey(c)) { + return null; + } + if (i == arr.length - 1) { + return node.children; + } + node = node.children.get(c); + } + return null; + } + + /*Return the children HashMap of the last char*/ + public HashMap getChildrenMap(String s) { + if (s == null || s.length() == 0) { + return root.children; + } + char[] arr = s.toCharArray(); + TrieNode node = root; + for (int i = 0; i < arr.length; i++) { + char c = arr[i]; + if (!node.children.containsKey(c)) { + return null; + } + node = node.children.get(c); + } + return node.children; + } + + //Helper to comapre string and return the difference if not <= 1 + public int compare(String s1, String s2) { + int count = 0; + for (int i = 0; i < s1.length(); i++) { + count += s1.charAt(i) != s2.charAt(i) ? 1 : 0; + if (count > 1) { + return count; + } + } + return count; + } + + + public static void main(String[] args) { + String beginWord = "hit";//"hit"; + String endWord = "cog"; + Set set = new HashSet(); + set.add("hot"); set.add("dot"); set.add("dog"); set.add("lot"); set.add("log"); + + + Solution sol = new Solution(); + int leng = sol.ladderLength(beginWord, endWord, set); + + System.out.println("rst " + leng ); + System.out.println("END"); + } + +} + + +``` \ No newline at end of file diff --git a/Java/Word Pattern.java b/Java/Word Pattern.java new file mode 100755 index 0000000..e77f017 --- /dev/null +++ b/Java/Word Pattern.java @@ -0,0 +1,105 @@ +E + +每个char代表一个pattern。用HashMap. +但不够,如果a也match dog, b也match dog, 纠错了。比如pattern = "abba", str = "dog dog dog dog"。 +因此第二个HashMap 反过来。 +确保pattern和str一一对应。 + +``` +/* +Given a pattern and a string str, find if str follows the same pattern. + +Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty word in str. + +Examples: +pattern = "abba", str = "dog cat cat dog" should return true. +pattern = "abba", str = "dog cat cat fish" should return false. +pattern = "aaaa", str = "dog cat cat dog" should return false. +pattern = "abba", str = "dog dog dog dog" should return false. +Notes: +You may assume pattern contains only lowercase letters, and str contains lowercase letters separated by a single space. + +Credits: +Special thanks to @minglotus6 for adding this problem and creating all test cases. + +Hide Company Tags Dropbox +Hide Tags Hash Table +Hide Similar Problems (E) Isomorphic Strings (H) Word Pattern II + +*/ + +/* + 3.1.2016 recap. + HashMap, one to one mapping +*/ +public class Solution { + public boolean wordPattern(String pattern, String str) { + if (pattern == null || pattern.length() == 0 || str == null || str.length() == 0) { + return false; + } + + String[] strArr = str.split(" "); + if (pattern.length() != strArr.length) { + return false; + } + HashMap map = new HashMap(); + + for (int i = 0; i < pattern.length(); i++) { + char c = pattern.charAt(i); + String s = strArr[i]; + if (!map.containsKey(c)) { + if (map.containsValue(s)) { + return false; + } + map.put(c, s); + } else if (!map.get(c).equals(s)) { + return false; + } + } + return true; + } +} + + +/* +Thoughts: +2 HashMap, HashMap double check +*/ +public class Solution { + public boolean wordPattern(String pattern, String str) { + if (pattern != null && str != null && pattern.length() == 0 && str.length() == 0) { + return true; + } + if (pattern == null || pattern.length() == 0 || str == null || str.length() == 0) { + return false; + } + String[] strArr = str.split(" "); + if (pattern.length() != strArr.length) { + return false; + } + + HashMap map = new HashMap(); + HashMap mapStr = new HashMap(); + + for (int i = 0; i < strArr.length; i++){ + if (!map.containsKey(pattern.charAt(i))) { + map.put(pattern.charAt(i), strArr[i]); + } else { + if (!map.get(pattern.charAt(i)).equals(strArr[i])) { + return false; + } + } + if (!mapStr.containsKey(strArr[i])) { + mapStr.put(strArr[i], pattern.charAt(i)); + } else { + if (mapStr.get(strArr[i]) != pattern.charAt(i)) { + return false; + } + } + } + return true; + } +} + + +``` \ No newline at end of file diff --git a/Java/Word Search II.java b/Java/Word Search II.java old mode 100644 new mode 100755 index 30f77a3..ce7acf6 --- a/Java/Word Search II.java +++ b/Java/Word Search II.java @@ -1,3 +1,158 @@ +H +1520570560 +tags: Backtracking, Trie, DFS + +给一串words, 还有一个2D character matrix. 找到所有可以形成的words. 条件: 2D matrix 只可以相邻走位. + +#### Trie, DFS +- 相比之前的implementation, 有一些地方可以优化: +- 1. Backtracking时候, 在board[][] 上面mark就可以, 不需要开一个visited[][] +- 2. 不需要implement trie的所有方程, 用不到: 这里只需要insert. +- 普通的trie题目会让你search a word, 但是这里是用一个board, 看board的每一个字母能不能走出个Word. +- 也就是: 这里的search是自己手动写, 不是传统的trie search() funcombination +- 3. TrieNode里面存在 end的时候存string word, 表示到底. 用完了 word = null, 刚好截断重复查找的问题. + +##### 关于Trie +- Build Trie with target words: insert, search, startWith. Sometimes, just: `buildTree(words)` and return root. +- 依然要对board matrix做DFS。 +- no for loop on words. 直接对board DFS: +- 每一层,都会有个up-to-this-point的string. 在Trie里面check它是不是存在。以此判断。 +- 若不存在,就不必继续DFS下去了。 +- Trie solution time complexity, much better: +- build Trie: n * wordMaxLength +- search: boardWidth * boardHeight * (4^wordMaxLength + wordMaxLength[Trie Search]) + + +#### Regular DFS +- for loop on words: inside, do board DFS based on each word. +- Time cpmplexity: word[].length * boardWidth * boardHeight * (4^wordMaxLength) + +#### Previous Notes +- Big improvement: use boolean visited on TrieNode! +- 不要用rst.contains(...), 因为这个是O(n) 在leetcode还是会timeout(lintcode竟然可以pass)! +- 在Trie search() method 里面,凡是visit过的,mark一下。 + + +``` +/* +LeetCode +Given a 2D board and a list of words from the dictionary, find all words in the board. + +Each word must be constructed from letters of sequentially adjacent cell, +where "adjacent" cells are those horizontally or vertically neighboring. + +The same letter cell may not be used more than once in a word. + +For example, +Given words = ["oath","pea","eat","rain"] and board = + +[ + ['o','a','a','n'], + ['e','t','a','e'], + ['i','h','k','r'], + ['i','f','l','v'] +] +Return ["eat","oath"]. +Note: +You may assume that all inputs are consist of lowercase letters a-z. + +Hint: + +You would need to optimize your backtracking to pass the larger test. Could you stop backtracking earlier? + +If the current candidate does not exist in all words' prefix, +you could stop backtracking immediately. What kind of data structure could answer such query efficiently? +Does a hash table work? Why or why not? How about a Trie? If you would like to learn how to implement a basic trie, +please work on this problem: Implement Trie (Prefix Tree) first. + + */ +/* +Thoughts: +Simplify the problem: want to find the words' existance based on the trie structure. +1. Build the trie with the words. +2. DFS and backtracking with the board and see if the combination exist. + +Build Trie: + time: O(mn), m = words.length, n = max word.length +Search with dfs + board[k][k] -> k^2 + longest word: n + O(k^2 * n) + +Overall: O(k^2 * n) + O(mn) = O(mn), k should be small +*/ +/* +Thoughts: +Simplify the problem: want to find the words' existance based on the trie structure. +1. Build the trie with the words. +2. DFS and backtracking with the board and see if the combination exist. +*/ +class Solution { + class TrieNode { + String word; + TrieNode[] children; + public TrieNode() { + this.word = null; + this.children = new TrieNode[26]; + } + } + int[] dx = {0, 0, 1, -1}; + int[] dy = {1, -1, 0, 0}; + public List findWords(char[][] board, String[] words) { + List result = new ArrayList<>(); + if (validateInput(board, words)) return result; + + // Build trie + TrieNode root = buildTrie(words); + Set set = new HashSet<>(); + // DFS and populate the result + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + dfs(root, board, i, j, set); + } + } + result.addAll(set); + return result; + } + + private void dfs(TrieNode node, char[][] board, int x, int y, Set set) { + if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) return; + char c = board[x][y]; + if (c == '#' || node.children[c - 'a'] == null) return; + + node = node.children[c - 'a']; + + // Found the match + if (node.word != null && !set.contains(node.word)) set.add(node.word); + + // Moving forward and backtracking + board[x][y] = '#'; + for (int i = 0; i < 4; i++) { + dfs(node, board, x + dx[i], y + dy[i], set); + } + board[x][y] = c; + } + + private TrieNode buildTrie(String[] dict) { + TrieNode root = new TrieNode(); + for (String word : dict) { + TrieNode node = root; + for (char c : word.toCharArray()) { + int index = c - 'a'; + if (node.children[index] == null) node.children[index] = new TrieNode(); + node = node.children[index]; + } + node.word = word; + } + return root; + } + + private boolean validateInput(char[][] board, String[] words) { + return board == null || board.length == 0 || board[0] == null || board[0].length == 0 + || words == null || words.length == 0; + } +} + /* Given a matrix of lower alphabets and a dictionary. Find all words in the dictionary that can be found in the matrix. A word can start from any position in the matrix and go left/right/up/down to the adjacent position. @@ -40,8 +195,6 @@ */ -//Well, the attempt2 uses Trie, but didn't really use find(). It just uses insert() to create Trie, and mainly -//used the end point where string is finished. /* Attemp2: Trie solution. @@ -66,84 +219,119 @@ In the search: node.subtree.get(current).isString: this determines if a string exists or not. */ + +/*NOTE: is boolean visited on Trie!!! */ public class Solution { - class TrieNode { - boolean isString; - String s; - HashMap subtree; - public TrieNode() { - this.isString = false; - this.s = ""; - this.subtree = new HashMap(); - } - } - - class TrieTree { - TrieNode node; - public TrieTree(TrieNode n) { - node = n; - } - public void insert(String s) { - TrieNode curr = node; - for (int i = 0; i < s.length(); i++) { - if (!curr.subtree.containsKey(s.charAt(i))) { - curr.subtree.put(s.charAt(i), new TrieNode()); - } - curr = curr.subtree.get(s.charAt(i)); - } - curr.isString = true; - curr.s = s; - } - public boolean find(String s) { - TrieNode curr = node; - for (int i = 0; i < s.length(); i++) { - if (!curr.subtree.containsKey(s.charAt(i))) { - return false; - } - curr = curr.subtree.get(s.charAt(i)); - } - return curr.isString; - } - } - - public void search(char[][] board, ArrayList rst, int i, int j, TrieNode node) { - if (node.isString) { - if(!rst.contains(node.s)) { - rst.add(node.s); - } - } - if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] == '#' || node == null) { - return; - } - if (node.subtree.containsKey(board[i][j])) { - char temp = board[i][j]; - TrieNode next = node.subtree.get(board[i][j]); - board[i][j] = '#';//Mark it, prevent going back-forth - search(board, rst, i, j + 1, next); - search(board, rst, i, j - 1, next); - search(board, rst, i - 1, j, next); - search(board, rst, i + 1, j, next); - board[i][j] = temp; - } - - } - public ArrayList wordSearchII(char[][] board, ArrayList words) { - ArrayList rst = new ArrayList(); - if (board == null || words == null || words.size() == 0) { - return rst; - } - TrieTree tree = new TrieTree(new TrieNode()); - for (String word : words) { - tree.insert(word); - } - - for (int i = 0; i < board.length; i++) { - for (int j = 0; j < board[i].length; j++) { - search(board, rst, i, j, tree.node); - } - } - - return rst; + class TrieNode { + String str; + boolean isEnd; + boolean visited; + HashMap children; + public TrieNode () { + this.isEnd = false; + this.visited = false; + this.str = ""; + children = new HashMap(); + } + } + public TrieNode root; + + public List findWords(char[][] board, String[] words) { + List rst = new ArrayList(); + if (board == null || board.length == 0 || board[0].length == 0 + || words == null || words.length == 0) { + return rst; + } + + //Build Trie with words + root = new TrieNode(); + for (int i = 0; i < words.length; i++) { + insert(words[i]); + } + + //Validate existance of the words in board + boolean[][] visited = new boolean[board.length][board[0].length]; + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + dfs(rst, "", i, j, visited, board); + } + } + + return rst; + } + + //4 direction DFS search + public void dfs(List rst, String s, int x, int y, boolean[][] visited, char[][] board) { + if (x < 0 || x >= board.length || y < 0 || y >= board[0].length) { + return; + } + if (visited[x][y]) { + return; + } + s += board[x][y]; + if (!startWith(s)) { + return; + } + + if (search(s)) { + rst.add(s); + } + int[] dx = {0,0,1,-1}; + int[] dy = {1,-1,0,0}; + visited[x][y] = true; + for (int i = 0; i < 4; i++) { + int nx = x + dx[i]; + int ny = y + dy[i]; + dfs(rst, s, nx, ny, visited, board); + } + visited[x][y] = false; + } + + /**************************Trie section *********************/ + public void insert (String s){ + char[] arr = s.toCharArray(); + TrieNode node = root; + for (int i = 0; i < arr.length; i++) { + char c = arr[i]; + if (!node.children.containsKey(c)) { + node.children.put(c, new TrieNode()); + } + node = node.children.get(c); + if (i == arr.length - 1) { + node.isEnd = true; + node.str = s; + } + } + } + + public boolean search(String s) { + char[] arr = s.toCharArray(); + TrieNode node = root; + for (int i = 0; i < arr.length; i++) { + char c = arr[i]; + if (!node.children.containsKey(c)) { + return false; + } + node = node.children.get(c); + } + if (node.visited || !node.isEnd) { + return false; + } + node.visited = true; + return true; + } + + public boolean startWith(String s) { + char[] arr = s.toCharArray(); + TrieNode node = root; + for (int i = 0; i < arr.length; i++) { + char c = arr[i]; + if (!node.children.containsKey(c)) { + return false; + } + node = node.children.get(c); + } + return true; } } @@ -151,6 +339,11 @@ public ArrayList wordSearchII(char[][] board, ArrayList words) { + + + + + /* Attempt1: Thoughts: @@ -159,52 +352,61 @@ public ArrayList wordSearchII(char[][] board, ArrayList words) { public class Solution { public ArrayList wordSearchII(char[][] board, ArrayList words) { - ArrayList rst = new ArrayList(); - if (board == null || words == null || words.size() == 0) { - return rst; - } - for (String word : words) { - if (exist(board, word)) { - rst.add(word); - } - } - return rst; + ArrayList rst = new ArrayList(); + if (board == null || words == null || words.size() == 0) { + return rst; + } + for (String word : words) { + if (exist(board, word)) { + rst.add(word); + } + } + return rst; } //The following are from Word Search I public boolean exist(char[][] board, String word) { - if (word == null || word.length() == 0) { - return true; - } - if (board == null) { - return false; - } - - for (int i = 0; i < board.length; i++) { - for (int j = 0; j < board[0].length; j++) { - if (board[i][j] == word.charAt(0)) { - boolean rst = search(board, word, i, j, 0); - if (rst) { - return true; - } - } - } - } - return false; + if (word == null || word.length() == 0) { + return true; + } + if (board == null) { + return false; + } + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == word.charAt(0)) { + boolean rst = search(board, word, i, j, 0); + if (rst) { + return true; + } + } + } + } + return false; } public boolean search(char[][] board, String word, int i, int j, int start) { - if (start == word.length()) { - return true; - } - if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(start)) { - return false; - } - board[i][j] = '#'; - boolean rst = search(board, word, i, j - 1, start + 1) - || search(board, word, i, j + 1, start + 1) - || search(board, word, i + 1, j, start + 1) - || search(board, word, i - 1, j, start + 1); - board[i][j] = word.charAt(start); - return rst; + if (start == word.length()) { + return true; + } + if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(start)) { + return false; + } + board[i][j] = '#'; + boolean rst = search(board, word, i, j - 1, start + 1) + || search(board, word, i, j + 1, start + 1) + || search(board, word, i + 1, j, start + 1) + || search(board, word, i - 1, j, start + 1); + board[i][j] = word.charAt(start); + return rst; } } + + + + + + + + +``` \ No newline at end of file diff --git a/Java/Word Search.java b/Java/Word Search.java old mode 100644 new mode 100755 index 013dbb9..9ba49cd --- a/Java/Word Search.java +++ b/Java/Word Search.java @@ -1,9 +1,26 @@ +M +1520573473 +tags: Array, Backtracking, DFS + +#### DFS, Backtracking: +- 找到开头的字母, 然后recursively DFS 去把word串到底: +- 每到一个字母, 朝四个方向走, 之中一个true就可以. +- Note:每次到一个字母,mark一下'#'. 4个path recurse回来后,mark it back. + +#### Note: other ways of marking visited: +- 用一个boolean visited[][] +- Use hash map, key = x@y + + +``` /* Given a 2D board and a word, find if the word exists in the grid. -The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once. +The word can be constructed from letters of sequentially adjacent cell, +where "adjacent" cells are those horizontally or vertically neighboring. +The same letter cell may not be used more than once. + -Have you met this question in a real interview? Yes Example Given board = @@ -19,52 +36,160 @@ Tags Expand Backtracking +*/ + +/* +Thoughts: +Word Search II 简化版: 只有DFS, 没有words的结构优化 +1. DFS (board, x, y) +2. Backtracking: mark visited item '#' +*/ + +class Solution { + int[] dx = {0, 0, 1, -1}; + int[] dy = {1, -1, 0, 0}; + public boolean exist(char[][] board, String word) { + if (board == null || board.length == 0 || board[0] == null || board[0].length == 0 + || word == null || word.length() == 0) { + return false; + } + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (word.charAt(0) == board[i][j] && dfs(board, word, i, j, 0)) { + return true; + } + } + } + return false; + } + + private boolean dfs(char[][] board, String word, int x, int y, int index) { + if (x < 0 || x >= board.length || y < 0 || y >= board[0].length + || board[x][y] != word.charAt(index) || board[x][y] == '#') { + return false; + } + if (index == word.length() - 1) { + return true; + } + board[x][y] = '#'; + for (int i = 0; i < dx.length; i++) { + if (dfs(board, word, x + dx[i], y + dy[i], index + 1)) { + return true; + } + } + board[x][y] = word.charAt(index); + return false; + } + +} + +/* Thoughts: 1. find starting index i,j 2. Start divde&&conqure: each iteration. - In each interation: make sure board[i][j] == word.charAt(currentCheckingIndex); If not match, return false and terminate the interation + In each interation: make sure board[i][j] == word.charAt(currentCheckingIndex); If not match, return false and terminate the interation 3. Therefore, if (start) == word.length(), that means all previous-start indexes are matched, so we have a match; return true in this case. Note: if can use boolean || boolean || boolean, use it and save processing power: once one boolean works, it won't process the rest || elements -*/ +*/ public class Solution { public boolean exist(char[][] board, String word) { - if (word == null || word.length() == 0) { - return true; - } - if (board == null) { - return false; - } - - for (int i = 0; i < board.length; i++) { - for (int j = 0; j < board[0].length; j++) { - if (board[i][j] == word.charAt(0)) { - boolean rst = search(board, word, i, j, 0); - if (rst) { - return true; - } - } - } - } - return false; + if (word == null || word.length() == 0) { + return true; + } + if (board == null) { + return false; + } + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == word.charAt(0)) { + boolean rst = search(board, word, i, j, 0); + if (rst) { + return true; + } + } + } + } + return false; } public boolean search(char[][] board, String word, int i, int j, int start) { - if (start == word.length()) { - return true; - } - if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(start)) { - return false; - } - board[i][j] = '#'; - boolean rst = search(board, word, i, j - 1, start + 1) - || search(board, word, i, j + 1, start + 1) - || search(board, word, i + 1, j, start + 1) - || search(board, word, i - 1, j, start + 1); - board[i][j] = word.charAt(start); - return rst; + if (start == word.length()) { + return true; + } + if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(start)) { + return false; + } + board[i][j] = '#'; + boolean rst = search(board, word, i, j - 1, start + 1) + || search(board, word, i, j + 1, start + 1) + || search(board, word, i + 1, j, start + 1) + || search(board, word, i - 1, j, start + 1); + board[i][j] = word.charAt(start); + return rst; + } +} + + + +//dfs: search through the board, going to different directions, while also increasing index. when index == word.length, that's end. +//use visited[][] to mark visited places. + +public class Solution { + public boolean exist(char[][] board, String word) { + if (board == null || board.length == 0 || board[0] == null + || board[0].length == 0 || word == null || word.length() == 0) { + return false; + } + int height = board.length; + int width = board[0].length; + boolean[][] visited = new boolean[height][width]; + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (word.charAt(0) != board[i][j]) { + continue; + } + if (dfs(0, i, j, visited, board, word)) { + return true; + } + } + } + return false; + } + + public boolean dfs (int index, int x, int y, boolean[][] visited, char[][] board, String word) { + if (index == word.length()) { + return true; + } + + int height = board.length; + int width = board[0].length; + if (x < 0 || x >= height || y < 0 || y >= width || board[x][y] != word.charAt(index) || visited[x][y]) { + return false; + } + + int[] dx = {1, -1, 0, 0}; + int[] dy = {0, 0, 1, -1}; + visited[x][y] = true; + for (int i = 0; i < 4; i++) { + int nx = x + dx[i]; + int ny = y + dy[i]; + if (dfs(index + 1, nx, ny, visited, board, word)) { + return true; + } + } + visited[x][y] = false; + return false; } } + + + + + +``` \ No newline at end of file diff --git a/Java/Word Squares.java b/Java/Word Squares.java new file mode 100644 index 0000000..0d12ea2 --- /dev/null +++ b/Java/Word Squares.java @@ -0,0 +1,189 @@ +H +1520728398 +tags: Backtracking, Trie + +可以开Trie class, 里面用到TrieNode. 开Trie(words) 可以直接initalize with for loop +TrieNode 里面可以有一个 List startWith: 记录可以到达这个点的所有string: 有点像树形, ancestor形状的存储. + +神操作: +根据square的性质, 如果选中了list of words, 设定 int prefixIndex = list.size(). +取出list里面的所有word[prefixedIndex], 并且加在一起, 就是下一个word candidate的 prefix. + +形象一点: +list = ["ball", "area"]; +prefixIndex = list.size(); ball[prefixIndex] = 'l'; area[prefixIndex] = 'e'; +//then +candidatePrefix = ball[prefixIndex] + area[prefixIndex] = "le"; +这里就可以用到Trie的那个 findByPrefix function, 在每个点, 都存有所有这个点能产生的candidate. +这时, 试一试所有candidate: dfs + +能想到这种倒转的结构来存prefix candidates 在 Trie里面, 这个想法非常值得思考. + +``` +/* +Given a set of words (without duplicates), +find all word squares you can build from them. + +A sequence of words forms a valid word square if the kth row and column +read the exact same string, where 0 ≤ k < max(numRows, numColumns). + +For example, the word sequence ["ball","area","lead","lady"] +forms a word square because each word reads the same both horizontally and vertically. + +b a l l +a r e a +l e a d +l a d y +Note: +There are at least 1 and at most 1000 words. +All words will have the exact same length. +Word length is at least 1 and at most 5. +Each word contains only lowercase English alphabet a-z. +Example 1: + +Input: +["area","lead","wall","lady","ball"] + +Output: +[ + [ "wall", + "area", + "lead", + "lady" + ], + [ "ball", + "area", + "lead", + "lady" + ] +] + +Explanation: +The output consists of two word squares. The order of output does not matter +(just the order of words in each word square matters). +Example 2: + +Input: +["abat","baba","atan","atal"] + +Output: +[ + [ "baba", + "abat", + "baba", + "atan" + ], + [ "baba", + "abat", + "baba", + "atal" + ] +] + +Explanation: +The output consists of two word squares. The order of output does not matter +(just the order of words in each word square matters). + +*/ + +/* +Thoughts: +Characteristic: +1. When 1st row/word is picked, the 2nd position in the string will be the next wordCandidate's prefix. +2. When 2 words are picked, the 3rd postion of both strings will add together as next wordCandidate's prefix + +Build Trie that stores all possible candidates at current node. +This time, put trie creation logic in Trie itself, since we are building the structure with input + +DFS: +1. Pick list of candidates +2. Based on size of the list, find the prefix, and find all futurer candidates. +3. add each candidate (backtracking), and DFS +*/ +class Solution { + // Define TrieNode + class TrieNode { + TrieNode [] children = new TrieNode[26]; + List startWith = new ArrayList<>(); + } + + // Define Trie + class Trie { + TrieNode root = new TrieNode(); + + // Build the trie and add list of startWith/candidates on node. + public Trie(String[] words) { + for (String word: words) { + TrieNode node = root; + for (char c: word.toCharArray()) { + int index = c - 'a'; + if (node.children[index] == null) { + node.children[index] = new TrieNode(); + } + node.children[index].startWith.add(word); + node = node.children[index]; + } + } + } + + // Get list of candidates for given prefix + public List findByPrefix(String prefix) { + List candidates = new ArrayList<>(); + if (prefix == null || prefix.length() == 0) { + return candidates; + } + TrieNode node = root; + for (char c : prefix.toCharArray()) { + int index = c - 'a'; + if (node.children[index] == null) { + return candidates; + } + node = node.children[index]; + } + candidates.addAll(node.startWith); + return node.startWith; + } + } + + public Trie trie; + public int length; + public List> wordSquares(String[] words) { + List> rst = new ArrayList<>(); + if (words == null || words.length == 0) { + return rst; + } + length = words[0].length(); + trie = new Trie(words); + for (String word: words) { + List selected = new ArrayList<>(); + selected.add(word); + dfs(rst, selected); + } + return rst; + } + + // Find the word + private void dfs(List> rst, List selected) { + if (selected.size() == length) { + rst.add(new ArrayList<>(selected)); + return; + } + + int prefixIndex = selected.size(); + StringBuffer sb = new StringBuffer(); + for (String word: selected) { + sb.append(word.charAt(prefixIndex)); + } + List newCandidates = trie.findByPrefix(sb.toString()); + + for (String candidate: newCandidates) { + selected.add(candidate); + dfs(rst, selected); + //Backtracking + selected.remove(selected.size() - 1); + } + } + +} + +``` \ No newline at end of file diff --git a/Java/Zigzag Iterator.java b/Java/Zigzag Iterator.java new file mode 100755 index 0000000..f62e510 --- /dev/null +++ b/Java/Zigzag Iterator.java @@ -0,0 +1,95 @@ +M +tags: BST + +这个题目相对简单. 做的时候我先考虑起来k条怎么办. 那么用个map把index和每个listmark一下就好了。 +每次next(), 相应的list的头拿下来就好。 +然后就跑圈呗,每次刷一个list头。不难。只要把几个variable维护清楚就行。 +``` +/* +Given two 1d vectors, implement an iterator to return their elements alternately. + +For example, given two 1d vectors: + +v1 = [1, 2] +v2 = [3, 4, 5, 6] +By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1, 3, 2, 4, 5, 6]. + +Follow up: What if you are given k 1d vectors? How well can your code be extended to such cases? + +Clarification for the follow up question - Update (2015-09-18): +The "Zigzag" order is not clearly defined and is ambiguous for k > 2 cases. +If "Zigzag" does not look right to you, replace "Zigzag" with "Cyclic". +For example, given the following input: + +[1,2,3] +[4,5,6,7] +[8,9] +It should return [1,4,8,2,5,9,3,6,7]. + +Tags: Design +Similar Problems: (M) Binary Search Tree Iterator, (M) Flatten 2D Vector, (M) Peeking Iterator + +*/ + +/* +Thoughts: +HashMap +alter the index to pick correct list +maintain a total size variable. +hashNext: size > 0? +next(): while(map.get(index).length() == 0) : find next list. +return list.get(0), also list.remove(0) + +Note: be careful with all the size, length, index. size does not change once fixed. Remember in next(): index++ and length--; +*/ +public class ZigzagIterator { + private HashMap> map; + private int length = 0; + private int size = 0; + private int index = 0; + public ZigzagIterator(List v1, List v2) { + map = new HashMap>(); + if (v1 != null && v1.size() > 0) { + map.put(size, v1); + length += v1.size(); + size++; + } + if (v2 != null && v2.size() > 0) { + map.put(size, v2); + length += v2.size(); + size++; + } + if (length == 0) { + return; + } + } + + public int next() { + while(map.get(index).size() == 0) { + index++; + if (index == size) { + index = 0; + } + } + int rst = map.get(index).get(0); + map.get(index).remove(0); + length--; + index++; + if (index == size) { + index = 0; + } + return rst; + } + + public boolean hasNext() { + return length > 0; + } +} + + +/** + * Your ZigzagIterator object will be instantiated and called as such: + * ZigzagIterator i = new ZigzagIterator(v1, v2); + * while (i.hasNext()) v[f()] = i.next(); + */ +``` \ No newline at end of file diff --git a/Java/k Sum.java b/Java/k Sum.java new file mode 100644 index 0000000..0103f21 --- /dev/null +++ b/Java/k Sum.java @@ -0,0 +1,73 @@ +H +1516774576 +tags: DP + +DP. 公式如何想到, 还需要重新理解. + +dp[i][j][m]: # of possibilities such that from j elements, pick m elements and sum up to i. +i: [0~target] + +dp[i][j][m] = dp[i][j-1][m] + dp[i - A[j - 1]][j-1][m-1] + (i not included) (i included) + +``` +/* +Given n distinct positive integers, integer k (k <= n) and a number target. + +Find k numbers where sum is target. Calculate how many solutions there are? + +Given [1,2,3,4], k = 2, target = 5. + +There are 2 solutions: [1,4] and [2,3]. + +Return 2. +*/ + +/* +Thoughts: +Once learned the equation, it becomes easy: +create dp[i][j][m]: # of possibilities such that from j elements, pick m elements and sum up to i. +i: [0~target] +HOWEVER: need to figure out how to come up with the equation + +Two ways to reach dp[i][j][m]: +If element i is not included; if i is included. + +dp[i][j][m] = dp[i][j-1][m] + dp[i - A[j - 1]][j-1][m-1] + (i not included) (i included) + +Initialization +dp[0][j][0], j=[0, A.length]: from j elemnts, pick 0 element to sum up to 0, there can be just 1 possibility, don't pick any thing: [] +Therefore: dp[0][j][0] = 1; +*/ + +public class Solution { + /* + * @param A: An integer array + * @param k: A positive integer (k <= length(A)) + * @param target: An integer + * @return: An integer + */ + public int kSum(int[] A, int k, int target) { + if (A == null || A.length == 0 || k <= 0) { + return 0; + } + + final int[][][] dp = new int[target + 1][A.length + 1][k + 1]; + for (int j = 0; j <= A.length; j++) { + dp[0][j][0] = 1; + } + for (int i = 1; i <= target; i++) { + for (int j = 1; j <= A.length; j++) { + for (int m = 1; m <= j && m <= k; m++) { + dp[i][j][m] = dp[i][j - 1][m]; + if (i - A[j - 1] >= 0) { + dp[i][j][m] += dp[i - A[j - 1]][j - 1][m - 1]; + } + } + } + } + return dp[target][A.length][k]; + } +} +``` \ No newline at end of file diff --git a/KnowledgeHash.md b/KnowledgeHash.md new file mode 100644 index 0000000..3cb36dc --- /dev/null +++ b/KnowledgeHash.md @@ -0,0 +1,1457 @@ +Table of Contents +================= + + * [Table of Contents](#table-of-contents) + * [Array](#array) + * [Honorable Problems](#honorable-problems) + * [String](#string) + * [Functions](#functions) + * [StringBuffer](#stringbuffer) + * [其他](#其他) + * [Hash Table](#hash-table) + * [HashTable](#hashtable) + * [HashSet](#hashset) + * [HashMap](#hashmap) + * [Heap](#heap) + * [Insert](#insert) + * [Extract Minimum Element](#extract-minimum-element) + * [如何想到用 Min/Max Heap](#如何想到用-minmax-heap) + * [Example](#example) + * [Stack](#stack) + * [Functions](#functions-1) + * [基本用法](#基本用法) + * [Monotonous Stack](#monotonous-stack) + * [Example](#example-1) + * [Queue](#queue) + * [Functions](#functions-2) + * [Linked List](#linked-list) + * [考法](#考法) + * [Tree](#tree) + * [Binary Search Tree](#binary-search-tree) + * [TreeSet](#treeset) + * [Traversal](#traversal) + * [Binary Tree](#binary-tree) + * [Type of Tree](#type-of-tree) + * [Balanced bianry tree](#balanced-bianry-tree) + * [Complete binary tree](#complete-binary-tree) + * [Full Binary Tree](#full-binary-tree) + * [Perfect Binary Tree](#perfect-binary-tree) + * [Binary Tree Traversal](#binary-tree-traversal) + * [Inorder Traversal](#inorder-traversal) + * [Expression Tree](#expression-tree) + * [Example](#example-2) + * [Build Tree](#build-tree) + * [Trie](#trie) + * [用法/考点](#用法考点) + * [Compare with HashMap](#compare-with-hashmap) + * [Code Model/ Sample Functions](#code-model-sample-functions) + * [Binary Indexed Tree](#binary-indexed-tree) + * [Segment Tree](#segment-tree) + * [基本知识](#基本知识) + * [functions](#functions-3) + * [用法](#用法) + * [Red Black Tree](#red-black-tree) + * [基本知识](#基本知识-1) + * [特点](#特点) + * [引申特点](#引申特点) + * [用法](#用法-1) + * [B-Tree](#b-tree) + * [基本知识](#基本知识-2) + * [AVL Tree](#avl-tree) + * [基本知识](#基本知识-3) + * [更多细节特点](#更多细节特点) + * [用途](#用途) + * [优点](#优点) + * [Graph](#graph) + * [Popular algorithms](#popular-algorithms) + * [Adjacency List](#adjacency-list) + * [Example](#example-3) + * [构建Graph](#构建graph) + * [Adjacency Matrices](#adjacency-matrices) + * [Graph Search](#graph-search) + * [DFS](#dfs) + * [BFS](#bfs) + * [Bidirectional Search](#bidirectional-search) + * [Deque](#deque) + * [Union Find](#union-find) + * [UnionFind基础操作](#unionfind基础操作) + * [UnionFind follow up](#unionfind-follow-up) + * [Topological Sort](#topological-sort) + * [建立Graph InDegree](#建立graph--indegree) + * [Topological Sort - BFS](#topological-sort---bfs) + * [Topological Sort - DFS](#topological-sort---dfs) + * [Two Pointers](#two-pointers) + * [Binary Search](#binary-search) + * [Template](#template) + * [中二思想](#中二思想) + * [Sort](#sort) + * [常用](#常用) + * [Merge Sort](#merge-sort) + * [Heap Sort](#heap-sort) + * [Quick Sort](#quick-sort) + * [Quick Select](#quick-select) + * [Bucket Sort](#bucket-sort) + * [Comparator for Arrays, Collections](#comparator-for-arrays-collections) + * [DP](#dp) + * [判断](#判断) + * [四个步骤:](#四个步骤) + * [确定状态](#确定状态) + * [转移方程](#转移方程) + * [初始条件/边界情况](#初始条件边界情况) + * [计算顺序](#计算顺序) + * [Technique](#technique) + * [基本原理](#基本原理) + * [分类](#分类) + * [网格坐标](#网格坐标) + * [特殊](#特殊) + * [序列](#序列) + * [特点](#特点-1) + * [性质](#性质) + * [特点](#特点-2) + * [关键点](#关键点) + * [序列加状态](#序列加状态) + * [划分](#划分) + * [性质](#性质-1) + * [经验](#经验) + * [解决方法](#解决方法) + * [博弈类](#博弈类) + * [背包类](#背包类) + * [多种问法](#多种问法) + * [方法策略](#方法策略) + * [放入的物品没有顺序](#放入的物品没有顺序) + * [区间类(Interval DP)](#区间类interval-dp) + * [特点](#特点-3) + * [三把斧](#三把斧) + * [难点](#难点) + * [Bitwise Operation DP](#bitwise-operation-dp) + * [优化](#优化) + * [Minimax/MaxiMin](#minimaxmaximin) + * [Optimization problems](#optimization-problems) + * [Double Sequence](#double-sequence) + * [存状态](#存状态) + * [记忆化搜索 Memoization DP](#记忆化搜索-memoization-dp) + * [什么时候用记忆化搜索](#什么时候用记忆化搜索) + * [特点](#特点-4) + * [缺点](#缺点) + * [时间空间复杂度的节省](#时间空间复杂度的节省) + * [Search](#search) + * [Breadth-first Search](#breadth-first-search) + * [Depth-first Search](#depth-first-search) + * [for loop dfs vs. pick&&skip dfs](#for-loop-dfs-vs-pickskip-dfs) + * [void dfs](#void-dfs) + * [object dfs](#object-dfs) + * [Regular Premitives](#regular-premitives) + * [Customized Object](#customized-object) + * [Tree DFS](#tree-dfs) + * [Combinatorics](#combinatorics) + * [DFS 思想](#dfs-思想) + * [DFS 注意](#dfs-注意) + * [Permutation](#permutation) + * [原理](#原理) + * [Backtracking DFS (Recursive)](#backtracking-dfs-recursive) + * [插入法 (iterative)](#插入法-iterative) + * [Sweep Line](#sweep-line) + * [Backtracking](#backtracking) + * [Greedy](#greedy) + * [When to use](#when-to-use) + * [Examples](#examples) + * [Reservoir Sampling](#reservoir-sampling) + * [Geometry](#geometry) + * [Approach](#approach) + * [遇到Array](#遇到array) + * [遇到需要排序](#遇到需要排序) + * [Divide and Conquer](#divide-and-conquer) + * [Recursion](#recursion) + * [特征](#特征) + * [Other minor problem sets](#other-minor-problem-sets) + * [回文串 Palindrome](#回文串-palindrome) + * [Windows Problem](#windows-problem) + * [Collections Functions](#collections-functions) + * [methods](#methods) + * [ArrayList](#arraylist) + * [CS Basics](#cs-basics) + * [BigO Specials](#bigo-specials) + * [Time Complexity of graph/dfs block](#time-complexity-of-graphdfs-block) + * [Sum, PrefixSum](#sum-prefixsum) + * [Math](#math) + * [Math Functions](#math-functions) + * [Numbers](#numbers) + * [Probability theory](#probability-theory) + * [Combinatorics](#combinatorics-1) + * [subset time&&space](#subset-timespace) + * [Brainteaser](#brainteaser) + * [Operating system](#operating-system) + * [Threads](#threads) + * [Two approaches](#two-approaches) + * [Bit Manipulation](#bit-manipulation) + * [Memory](#memory) + * [Java Garbage Collections](#java-garbage-collections) + * [Heap](#heap-1) + * [Garbage Collection Roots](#garbage-collection-roots) + * [Example](#example-4) + * [Marking and Sweeping Carbage](#marking-and-sweeping-carbage) + * [Pain Point](#pain-point) + * [NP-Complete problems](#np-complete-problems) + * [wiki](#wiki) + * [Knapsack](#knapsack) + * [Travelling salesman](#travelling-salesman) + * [Basics](#basics) + * [Edge case](#edge-case) + * [Advanced](#advanced) + + + + +# Array +- Arrays.asList([1,2,3]); +- Partial sort: Arrays.sort(arr, 0, arr.length()) +- Copy: Arrays.copyOf(arr, arr.length()) +- Arrays.toString(int[] arr) => string representation: "[1,2,3,4]" +- 见到数组要想到: 是否可以/需要先排序? + +## Honorable Problems +- Median of two sorted arrays: find kth element of two sorted array where k = n/2. Recursive findKth(A[],indexA, B[], indexB, k) + +# String +## Functions +- s.toCharArray() +- String.valueOf(charArrary) +- String.compareTo( "XXX" ): 排序按照字典(lexicographically)顺序 +- Character.isDigit(x) +- split: str.split("\\ "); 需要用 "\\" regular expression +- s.trim() 去掉 space +- count characters: int[256], 不需要 `c-'a'` + +## StringBuffer +- sb = new StringBuffer() +- sb.replace(i, j, "replacement string") +- sb.reverse(), sb.append(), sb.length(), sb.setCharAt(index, char) +- sb.deleteCharAt(), +- sb.insert(0, xxx): 在开头加element + +## 其他 +- 遇到找string的相关问题: 考虑string的重复性 + +# Hash Table +## HashTable +- Ex: when searching unsorted array(if you try to avoid sort O(nlogN)), probably can index with HashMap +- Can be used to store character/string frequency +- Hash: HashMap[Java]. Made of: HashMap +- TreeMap: TreeMap[Java]. Balanced Binary Tree +- TreeSet: TreeSet[Java]. Balanced Binary Tree + +## HashSet +- contains: O(1) +- set.add(...) returns false if there is duplicate. This operation won't change the existing set. +- Build HashSet set, and the set will automatically compare the equivalence of the lists within at each list element level. + +## HashMap +- Use iterator: `Iterator> iter = map.entrySet().iterator();` +- `iter.hasNext()` and `iter.next();` + +# Heap +- min-heap && max-heap has same concept +- min-heap is complete binary tree (right-most elements on last level may not be filled) +- each node is smaller than it's children +- root is the minimum value of tree +- Maintaing min-heap is about swaping node values +- Insert/extract min value both take O(logn) time +- PriorityQueue[Java]. Made of: Binary Heap (看Binary Tree section) + +## Insert +- insert at bottom right-most spot +- swap with parent if value not fitting min-heap +- swapping until min value reaches root +- O(logn) to bubble up to top + +## Extract Minimum Element +- extract root value (easy) +- set the the root value to be bottom-right-most element, also remove that bottom element +- bubble down the root value if not fitting min-heap +- overal efforts to bubble down: O(logn) + +## 如何想到用 Min/Max Heap +- 见到需要维护一个集合的最小值/最大值的时候要想到用堆 (看到Min/Max就要想到heap) +- 看到median 想到heap +- 第k小的元素,Heap用来维护当前候选集合 +- 如果给出的数组没有排序, 先排序, 然后用heap. + +## Example +- Given n items, find first k smallest items (`k closest point to the origin`): +- you would think about putting all n items into a min-heap(priorityQueue), and output top k by polling. That will be O(nlogn) +- Could do better: 1. use max-heap to store first k items; 2. if any value less than max, replace max with new value. +- Result is: always keep the smaller items in the max-heap, and replace the head/max. +- therefore, building the queue is like O(nlogk), saved space and time + + +# Stack + +## Functions +- peek(), pop(), push() +- Stack stack = new Stack<>(); Push generic Object to stack + +## 基本用法 +- 用来暂且保存有效信息 +- 翻转stack +- stack优化DFS, 变成非递归 +- Stack can be implemented with LinkedList, adding/removing from same side of the list +- Monotonous stack的运用: 找左边和右边第一个比它大的元素 +- 递归转非递归 + +## Monotonous Stack +- 找每个元素左边或者右边 第一个比它自身小/大的元素 +- 用单调栈来维护 +- 维护monotonous stack 是题目需要, 而不是stack本身性质. +- 比如, 题目需要stack.peek() O(1), 加上需要单调递增/递减的性质, 就用起来了stack. +``` +// Use monotonous stack to build minimum binary tree +// 1. Create that new node +TreeNode ndoe = new TreeNode(val); +// 2. The stack is monotonous, so loop all items >= node.val, and set as left child. +// monotonous: continuous increasing or decreasing, so the loop will end at some point. +while (!stack.isEmpty() && node.val <= stack.peek().val) { + node.left = stack.pop(); +} +// 3. The item left in stack is < node.val, so node should be a child. +if (!stack.isEmpty()) {//build right node of the tree + stack.peek().right = node; +} +// 4. Every new node needs to be tested. push to stack. +stack.push(node); + +// End result: parentheses will not be in the tree + [ - ] + / \ + [ * ] [ / ] + / \ / \ +[ 2 ] [ 6 ] [ + ] [ + ] + / \ / \ + [ 23 ][ 7 ] [ 1 ] [ 2 ] . +``` +``` +// Plain monotonous stack template: +item = someItem; // ex: item in for loop +while (!stack.isEmpty() && (item.property compareTo stack.peek().property)) { + topItem = stack.pop(); + // Do something with the topItem +} +stack.push(item); +``` + +## Example +- Maximum Binary Tree, Largest Rectangle In Histogram, Maximal Rectangle (in 2D array) +- Expression Tree (Minimum Binary Tree) + + +# Queue +## Functions +- peek(), poll(), add()/offer(), remove(object) +- queue = new LinkedList<...>() +- PriorityQueue: new Comparator 很重要 +- PriorityQueue 用完的item(top poll item 可能用完后 attribute有所变化): **如果还要继续用, 那就把item add回queue里**. +- Queue 可以用 LinkedList 实现. Add from the last/end of the list; Return/remove from the head of the list. + +# Linked List +- No concept of size(), it's all pointers: node.next.next +- how to set head/dummy, and return dummy.next as result. +- iterate over linked list +- Don't get mixed up with Java LinkedList. Here we are talking about linked list concept, not the Java data structure LinkedList + +## 考法 +- Reverse Linked List: 不断地在开头加上新node. 比较方便的方式: 用一个dummy node, 然后把reversed list 存在dummy.next +- Reverse linked list 注意: 有时候一开始的1st node, 最后还有tail 要接在上面; 所以先把1st node可以额外存下来, 用来接 tail +- 找到mid node: 快慢指针(slow = head; fast=head.next); 快指针每次走2步; 快指针到底的时候, slow指针就是mid +- merge two linked list: 用一个dummy head, 然后不断轮流加next (取决于merge的规则, 可能链接方法不同) + +# Tree +- A simple version of graph +- CAN NOT have cycle +- Can have a list of children +- 一般不会用Tree class 来实现tree, 一般都是用 TreeNode root as reference +- leaf: very end, 没孩子 + + +# Binary Search Tree +- If BST not given, can use TreeSet +- All left nodes are less than current node.val; all right nodes are greater than curr node.val +- Use DFS to traverse: divide and conquer. Similarly, usually can convert the DFS solution to interative solution. +- Use stack to traverse iteratively + +## TreeSet +- 如果BST treenode没给, 可以用TreeSet +- TreeSet还是一个set, 存values, 而好处是可以用 `treeSet.ceiling(x)` 找到 最小 >= x的值 +- 同样, 找 <= x 的value, 用 `treeSet.floor(x)` +- strict less or greater: `treeSet.lower(x)`, `treeSet.higher(x)` +- time O(nlogn) + +## Traversal +- Inorder BST traversal: 从小到大排序的ouput + +# Binary Tree +- 一定要问清楚, 是Binary Tree (双孩子而已), 还是 Binary Search Tree. 非常重要!!! +- 一个child tree的nodes总量是 2 ^ h - 1; 那么一个child tree + root 的nodes 总量就是 2 ^ h了. + +## Type of Tree + +### Balanced bianry tree +- has the minimum posible maximum height(depth) for left nodes; for given leaf nodes, the leaf nodes will be placed at greatest height possible. +- More like 'not terriably imbalanced'; NOT super balanced like 'perfect binary tree' +- can support O(logn) times for insert and find + +### Complete binary tree +- all levels are filled, except maybe the last level. 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) +- Nodes are filled from left to right on the last level + +### Full Binary Tree +- node has 0 or 2 children +- NO node will have 1 child + +### Perfect Binary Tree +- Both Complete and Full +- Have all nodes +- Last level has maximum nodes +- DO NOT assume tree is pefect tree; very rare in life/interview + +## Binary Tree Traversal +- preorder, inorder, post-order +- inorder more often +- draw the map +- can implement with dfs, bfs + +### Inorder Traversal +- DFS: check leaf => dfs left => process root => dfs right +- stack: in while loop: deep dive to left leaf => stack.pop() => `node = node.right` +``` +stack.push(root); +TreeNode node = root; +while(!stack.isEmpty()) { + //Left first + while (node != null && node.left != null) { + stack.add(node.left); + node = node.left; + } + //Process left/curr + node = stack.pop(); + + // do something with node + + node = node.right; // VERY IMPORTANT + if (node != null) { + stack.push(node); + } +} +``` +- `node = node.right` is critical, otherwise it'll be in infinite loop +- alternatively: we could set left = null, but that's disruptive to original structure, not recommended. + + +# Expression Tree +- Binary tree, used to evaluate certain expression +- All leaf nodes of the expression tree have a number/string value. +- All non-leaf node of the expression tree have an operation string value. + +## Example +``` +Example +For the expression (2*6-(23+7)/(1+2)) +which can be represented by ["2" "*" "6" "-" "(" "23" "+" "7" ")" "/" "(" "1" "+" "2" ")"]. +The expression tree will be like + + [ - ] + / \ + [ * ] [ / ] + / \ / \ + [ 2 ] [ 6 ] [ + ] [ + ] + / \ / \ + [ 23 ][ 7 ] [ 1 ] [ 2 ] . + +``` +- Expression Evaluation, Expression Tree Build +- Basic Calculator + +## Build Tree +- Use Monotonous Stack to build Minimum Binary Tree +- Use weight to associate parentheses, signs, numbers +- Write getWeight(base, String s) function to calculate the weight. + +# Trie +- 一个字母一个字母查找,快速判断前缀 +- Prefix Tree +- n-ary tree +- Can tell if string is a valid prefix in O(K) time, k = str.length +- example: Word Search II + +## 用法/考点 +- Autocomplete +- Spell check +- IP routing +- T9 text prediction (old NOKIA phone) +- solve world game +- 一个一个字母遍历 +- 需要节约空间 +- 查找前缀 + +## Compare with HashMap +- Trie can help find all strings with prefix +- Trie can validate a list of words +- Trie can enumerate a data set of strings in lexicographical order +- Trie saves space because of the prefix +- Trie can potentially faster than hashMap, when there are lots collisions for the map. + +## Code Model/ Sample Functions +- children map: Map. Also can use char[26], but it's more scalable to us a map. +- always have isEnd which marks a end of a particular string +- insert +- search +- exist of prefix +- node when the prefix end + +# Binary Indexed Tree + +# Segment Tree +## 基本知识 +- Also called, Interval Tree. It support queries like: +- https://en.wikipedia.org/wiki/Segment_tree +- https://en.wikipedia.org/wiki/Interval_tree +``` +public class SegmentTreeNode { + public int start, end, max; + public SegmentTreeNode left, right; + public SegmentTreeNode(int start, int end, int max) { + this.start = start; + this.end = end; + this.max = max + this.left = this.right = null; + } +} +/* +Given [3,2,1,4]. The segment tree will be: + + [0, 3] (max = 4) + / \ + [0, 1] (max = 3) [2, 3] (max = 4) + / \ / \ +[0, 0](max = 3) [1, 1](max = 2)[2, 2](max = 1) [3, 3] (max = 4) +*/ +``` +- Each segment tree node needs to possess some value to be useful. + +## functions +- build: divide and conquer; each step record necessary value: count, min, max, sum +- query: `mid = (root.start + root.end) / 2`, and compare target start/end with mid; call query() recursively + +## 用法 +- which of these intervals contain a given point +- which of these points are in a given interval +- track max in the range +- track count of nodes in the range + +# Red Black Tree +### 基本知识 +- one kind of self-balancing binary search tree +- Each node of the binary tree has an extra bit, and that bit is often interpreted as the color (red or black) of the node +- search O(logn), n = total # of nodes in the tree +- deletion/insertion/tree rearrangement && coloring: O(logn) + +### 特点 +- Each node is either red or black. +- The root is black. (This rule is sometimes omitted) +- All leaves (NIL) are black. +- If a node is red, then both its children are black. +- Every path from a given node to any of its descendant NIL nodes contains the same number of black nodes. + +### 引申特点 +- the path from the root to the farthest leaf is no more than twice as long as the path from the root to the nearest leaf. + +### 用法 +- offer worst-case guarantees for insertion time, deletion time, and search time +- used in time-sensitive application, real-time application +- valuble building block in other data structures +- valuble in functional programming: most common `persistent data structure` + + +# B-Tree +### 基本知识 +- https://en.wikipedia.org/wiki/B-tree +- 是generalization of Binary Search Tree. 每个node可以有超过2个children +- 'B' stands for nothing: actually noboday knows lol. +- Search/Insert/Delete O(log n) +- 每个children node 里面都是range of keys, 那么就不需要太多self-balancing operation +- 可能会浪费一些空间, 因为并不是每一个child node里面都full +- 根据root node 来分割child node 里面的range +``` + [7, 16] + / | \ + [1,2,5,6] [9, 12] [18, 21] +``` +- 每个node的这些keys, 其实就是是seperation values, 他们决定了subTree 如何divide. +- 也就是说, 如果 有 3 个subTree, 至少需要2个root key, 这样才能分成三份, 如上example. +- 通常: 大家会选 [d, 2d] keys, d = minimum number of keys +- 那么就至少有 (d + 1) 个subTree, 那么这个tree minimum degree 就是 (d+1). 也就是(d+1)个edge嘛, 简单. + +# AVL Tree +### 基本知识 +- The sub-trees of every node differ in height by at most one. +- Every sub-tree is an AVL tree + +### 更多细节特点 +- 1) All leaves are at same level. root一层, leaf一层. +- 2) A B-Tree is defined by the term minimum degree ‘t’. The value of t depends upon disk block size. +- 3) Every node except root must contain at least t-1 keys. Root may contain minimum 1 key. +- 4) All nodes (including root) may contain at most 2t – 1 keys. +- 5) Number of children of a node is equal to the number of keys in it plus 1. +- 6) All keys of a node are sorted in increasing order. The child between two keys k1 and k2 contains all keys in range from k1 and k2. +- 7) B-Tree grows and shrinks from root which is unlike Binary Search Tree. Binary Search Trees grow downward and also shrink from downward. +- 8) Like other balanced Binary Search Trees, time complexity to search, insert and delete is O(Logn). + +### 用途 +- Storage system than read/write large blocks of data. +- Commonly used in database, filesystem + +### 优点 +- keeps keys in sorted order for sequential traversing +- uses a hierarchical index to minimize the number of disk reads +- uses partially full blocks to speed insertions and deletions +- keeps the index balanced with a recursive algorithm + +# Graph +- Tree is a type of graph: connected graph, without cycles +- Graph is a collection of nodes, with edges between (some of, not all of) them +- It can be presented as ** map of Nodes ** +- Each node should be able to lead to a list of children nodes +- Can be directed (one-way street), or undirected (two-way street) +- May contain multiple isolated subgraphs. +- If there is a path between every pair of ndoes, it's a 'connected graph' +- graph can have cycle +- If no cycle, it's called 'acyclic' +- Two popular ways to store graph: Adjacency List, Adjacency Matrices + +## Popular algorithms +- Topological sort: figure out if cycle exist (course schedule), output order (course schedule II, Alien Dictinary) +- Union Find: count unions (Number of islands I, II) +- Given a matrix, or some structure: consider graph data structure, and convert into graph `Map` + +## Adjacency List +- The graph can be presented with a graph class, which is just a list of node, a map of nodes +- Or it can me an array (or a hash table) of lists (arrays, arraylists, linked lists ... etc) +- each node has it's own propety, and a list of adjacent nodes +- This graph can be directed, or undirected (modeled by adjacent node list in each Node) + +### Example +- 很多题目里面都是用 `Map`, 或者 `List[] edges` 来表示graph +- Course Schedule, Alien Dictionary +``` + +class Graph { + public Node[] nodes; +} + +class Node { + public String name; // property of the node + public Node[] children; // children nodes +} +``` +- Also, adjacent list: +``` +// Use Hashmap +// Number of Connected Components in an Undirected Graph +private Map> buildGraph(int n, int[][] edges) { + Map> graph = new HashMap<>(); + for (int i = 0; i < n; i++) { + if (!graph.containsKey(i)) { + graph.put(i, new ArrayList<>()); + } + } + for (int[] edge: edges) { + graph.get(edge[0]).add(edge[1]); + graph.get(edge[1]).add(edge[0]); + } + return graph; + } +``` + +### 构建Graph +- 建立edges: 1. 创建所有node -> neighbor list; 2. 把满足条件的neighbor 填进去(每个题目不同) +- 如果是做Topological Sort, 还要建立 `inDegree`: `Map`, 或者就是 `int[]` + +## Adjacency Matrices +- matrix[i][j] = true, indicate an edge from node i t node j + +## Graph Search +- DFS: start at root, explore each branch competely (go deep first) +- BFS: start at root, explore each neighbor before going to the children (go wide first) + +### DFS +- is preferred when visiting **every node** in the graph, a bit simplier to write +- IMPORTANT: in graph, must mark node **'visited'**, otherwise can be infinite loop + +### BFS +- Better when finding **shortest path** (or any path) betwen 2 nodes +- Use a queue, of course + +### Bidirectional Search +- Find shortest path between a source and destination node +- Running 2 simultaneous BFS +- when two searches collide, a path if found +- It's faster than one BFS +- Regular BFS time O(k^d), k = nodes at one level, d = seach for d times +- Bidirectional search time: O(k^(d/2)). Faster by K^(d/2) + + +# Deque +- linear collection that supports insertion and removal at both ends. Pronounced 'deck' +- It's a queue && stack +- new ArrayDeque() +- head/top: offerFirst(), pollFirst(), peekFirst() +- tail/bottom: offerLast(), pollLast(), peekLast() +- 双端queue: 维护一个候选可能的最大值集合 +- ex: Sliding WIndow Maximum + + +# Union Find +- 集合合并,查找元素在集合里面 +- Find and Union functions +- Time Complexity: log(n), https://en.wikipedia.org/wiki/Proof_of_O(log*n)_time_complexity_of_union%E2%80%93find +- 在UnionFind function里维护不同的状态, expose with public helper functions +- 似乎对index进行union find 操作 比较方便 (而并不是给实际value union find. index不重复, 而value会重复) + +## UnionFind基础操作 +- 查询两个元素是否在同一个集合内. connected() +- 合并两个元素所在的集合 + +## UnionFind follow up +- 查询某个元素所在集合的元素个数 +- 查询当前集合的个数 + +# Topological Sort +- Sort vertices in a graph +- Run DFS, output reverse of finishing times of vertices (stored in a list) +- G graph has a cycle? If there is a back edge, there is a cycle +- Job Schedule/ Course Schedule: should not have cycle, so acyclic + +## 建立Graph + InDegree +- Graph可能是 `map`, 也可能是 `List[]` +- InDegree在graph建立好的基础上, 建立起来: `map`, 或者简单的`int[]` +- 记住: InDegree跟graph里面的node, 利用neibor -> node 的反向关系建立起来的: +``` +private Map buildInDegree(Map> graph) { + Map inDegree = new HashMap<>(); + + // Build baseline + for (char c : graph.keySet()) { + inDegree.put(c, 0); + } + + // Aggregate inDegree + for (char c: graph.keySet()) { + for (char edgeNode : graph.get(c)) { + inDegree.put(edgeNode, inDegree.get(edgeNode) + 1); + } + } + + return inDegree; + } +``` + +## Topological Sort - BFS +- https://www.youtube.com/watch?v=_LuIvEi_kZk +- Can be used to return the topologically sorted list +- The final sorted list will not include the element on a cycle; these vertices won't reduce to in-degree 0. +- 给一个graph of nodes +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. 比如: 如果两个课互相是dependency, 就变成了cyclic dependency. +- 注意, 有些有cycle的题目里面要求直接fail掉, 因为有了cycle, topological sort的结果是缺element的, 也许对某个场景来说就是没有意义的. +- 相比DFS, BFS 更容易理解和walk through +- sort 的关键是最后用Queue, reduce inDegree, inDegree == 0 的node, 就是符合条件的candidate +``` +// Example of Alien Dictionary: +// Build queue with 0 indegree +for (char c : inDegree.keySet()) { + if (inDegree.get(c) == 0) { + queue.offer(c); + } +} + +StringBuffer sb = new StringBuffer(); +// IMPORTANT: +while (!queue.isEmpty()) { + char c = queue.poll(); + sb.append(c); + + for (char edgeNode : graph.get(c)) { + inDegree.put(edgeNode, inDegree.get(edgeNode) - 1); + if (inDegree.get(edgeNode) == 0) { + queue.offer(edgeNode); + } + } +} +``` + + +## Topological Sort - DFS +- https://youtu.be/AfSk24UTFS8?t=42m +- 还是要先构建好 edges = map; 或者edges = List[arraylist]; +- dfs到底, 再没有其他child node的时候, 把这个node加进stack (垫底) +- 最终按照stack的顺序, pop()出来的顺序 (reverse) 就是正确的topological sort +- 这个比BFS有时候要灵活一些: 可以detect cycle on the fly, 而BFS的做法会到最后再结算cycle是否存在 + +# Two Pointers +- Two pointers in array +- Two pointers in Linked List + +# Binary Search + +## Template +- 记得二分的template +- 按值二分,需要怎么二分性 +- 找到可能的解的范围 +- 猜答案 +- 检验答案 (match/if/else ...) +- 调整搜索范围 +- Find Peak Element II + +## 中二思想 +- 往往不会有个数组让我们二分, 但是同样是要去找满足某个条件的最大/最小值 +- 二分是个思想, 不是简单的array二分公式. +- 有时候在index上二分, mid是index; 但是有时候, 会在数值上二分, 那么mid就是value, 忌讳不要死板地套用nums[mid] + + +# Sort + +## 常用 +- Merge Sort. Runtime: O(nlogn) average/worst. Memory: depends. +- Quick Sort. Runtime: O(nlogn) average, O(n^2) worst. Memory: O(nlogn). +- Heap Sort +- Radix Sort. Runtime: O(nk) +- Bucket Sort. +- Insertion Sort +- Bubble Sort. Runtime: O(n^2) average/worst. Memory: O(1) +- Selection Sort. Runtime: O(n^2) average/worst. Memory: O(1) + +### Merge Sort +- Can be used on Linked List, Array, divide and conquer + +### Heap Sort + +### Quick Sort +- pick random pivot, all elements smaller sits before pivot, and all elements larger sits after the pivot +- while loop (and two inner while loop) to find the 2 indexes to swap, comparing with pivot +- use the left pointer to partition the array, and keeps sorting left part and right part +- Usually not used on Linked List, harder to perform partition + +#### Quick Select +- quick sort 的变形 +- A selection algorithm to find the k-th smallest element in an unordered list. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- still worst case O(n^2) +- 用 end index作为pivot, 每次根据nums[pivot]排序之后, 在 < low的位置, 都小于 nums[pivot] +- 把nums[pivot]换到low的位置, 那么换好之后, 所有在low前面的值, 都是小于 nums[pivot]了 +- 那么其实 nums[low]也就是 kth 最小值 +- 主要步骤: partition, dfs, only recur on one part of the array + +``` +private int quickSelect(int[] nums, int start, int end, int target) { + int index = partition(nums, start, end); + if (index == target) { + return nums[index]; + } else if (index < target) { + return quickSelect(nums, index + 1, end, target); + } else { + return quickSelect(nums, start, index - 1, target); + } +} + +private int partition(int[] nums, int low, int high) { + int pivot = high; // end + int pivotValue = nums[pivot]; + + while (low < high) { + while (low < high && nums[low] < pivotValue) { + low++; + } + while (low < high && nums[high] >= pivotValue) { + high--; + } + swap(nums, low, high); + } + + swap(nums, low, pivot); + + return low; +} +``` + +### Bucket Sort +- P: + +## Comparator for Arrays, Collections +``` +public int compare(x, y) { + return x - y; // or x.compareTo(y) +} +``` + +# DP +## 判断 +- 计数: how many. 加法原理 +- 求最大/最小值: min/max +- 求存在性: 能否, AND/OR dp=true/false +- 主要cover 递推(recurrence)的DP, 从小到大计算 + +## 四个步骤: +- 确定状态 +- 转移方程 +- 初始条件/边界情况 +- 计算顺序 + +#### 确定状态 +- 最后一步: 遍历最后一步可以取的情况 +- 化成子问题: 切掉最后一块, 剩下的问题跟原问题应该是一样的 + +#### 转移方程 +- 数学表达式来判断 dp[x]的结果 + +- 纸面上工作结束 + +#### 初始条件/边界情况 +- 非常重要, 必须有初始条件, 才可以让dp计算出正确结果. + +#### 计算顺序 +- 递推: 从小到大, 从 dp[0], dp[1] 开始 +- 记忆化: 从大到小, 先算一遍 dp[n-1] + +## Technique +- 滚动数组 +- 记忆化搜索 + +## 基本原理 +- 加法原理: 无重复, 无遗漏 +- dp = new int[n] 还是 dp = new int[n + 1]: 看角标是否表示坐标, 还是前i items的状态 + +## 分类 +- 网格坐标 * 20% +- 序列类 * 20% +- 划分类 * 20% +- 区间类(interval DP) +- 背包类 +- 双序列 +- 博弈 +- combos +- Bitwise Operation动态规划 + +### 网格坐标 (Coordinate) +- 可能是网格, 或者是序列 +- `dp[i]: 以第i个元素结尾的某种性质` +- `dp[i][j]: 到格子(i,j)的路径的某种性质`. +- dp index [i][j] = coordinate (i,j), 坐标小标就是grid下标 +- 2D的初始条件: f[0][0] +- 边界: i = 0, j = 0, 第一行和第一列 +- 计算顺序: 比如第一行, 第二行...etc. 目的: 为了保证, 需要用到的状态, 都已经算到了. +- Example: Unique Path I, II +- 可以出print path的题目 + +##### 特殊 +- 最长序列型动态规划: 恰恰是坐标类 +- 以i点结束, 很可能可以考虑坐标型 +- Longest Increasing Subsequence (祖师爷级别老题目) + + +### 序列 (Sequence) + +##### 特点 +- 变种多 +- 没有固定模板 + +##### 性质 +- `dp[i]种, 表示 '前i个元素a[0], a[1] ... a[i - 1] 的某种性质`. (坐标类:dp[i]就代表a[i]结尾的性质) +- dp[0] 表示空序列的性质 (坐标类: dp[0]表示以a[0]结尾的性质) +- 题目中遇到 前i个, '并且': 序列 + 状态 + +##### 特点 +- 可以选择让: DP[i]存的是以 1-based index的状态, 求前i个. +- 那么, 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1]. +- 初始化(简单): dp[0]往往是有特殊状态的: 0 index有时候初始化就是0 +- 边界情况(简单): 0 是虚拟的位, 所以大多数时候, 就留成0, 不太有所谓. + +##### 关键点 +- 不关心前面n-1是怎么组合出的状态, 但是可以先确定末尾的状态 +- 及时思考的时候从结尾, 在代码写的时候, 其实是模拟末尾是i = [0 ~ n] 的情况, 一圈圈计算. +- 最后再给出 dp[n] 的末尾解答. + +##### 序列加状态 +- 当思考动态规划最后一步时, 这一步的选择依赖于 前一步 的某种状态: 一维 + 状态 +- 序列+状态:如果n-1步有多种状态, 且n步也有多种可能, 添加一种状态记录, 变成2D dp[sequence index][状态] +- Example: Paint House + + +### 划分(Partition) + +##### 性质 +- 给长度是N的序列或者字符串, 要求分成若干段 +- 段数不限, 或者指定K段 +- 每一段满足一定性质 +- 类似于序列型动态规划, 但是通常要加上 段数信息 +- 一般用 `dp[i][j]记录, 前i个元素(元素是 0 ~ i - 1) 分成 j 段的性质`, 比如, 最小cost +- 划分j段的时候, j的大小跟i肯定是不同的scope的, j是分段, i可能就是普通的index + +##### 经验 +- '分/ partion/ several segment', 求最值 => 划分型动态规划 +- 不管怎么划, 总有最后一段! 根据题目给出的条件, 从末尾划刀 +- 也可能是看dp[i] 前 i 个item的状态 [0, i - 1] +- Example: Decode Ways + +##### 解决方法 +- 最后一步 => 考虑最后一段 +- 枚举最后一段的起点 +- 如果不指定段数, 用dp[i]表示前i个元素分段后的最值/可行性/ways. Perfect Squares, Palindrome Partition II +- 如果指定段数, 用dp[i][j]表示前i个元素分成j段后的最值/可行性/ways. Copy Books + +### 博弈类 (Game Theory) +- 常常问: 先手必胜的情况 +- 必胜状态/必败状态的分析 +- 通常从第一步分析, 从简单的来分析 (唯一一个需要考虑第一步的 ) +- 必胜: 在当下局面走出一步, 让对手无路可逃. 只要对手有一种必败的路线, 就是我必胜. + +### 背包类 (Backpack) + +##### 多种问法 +- 前i个物品能不能拼出重量W. 是 可行性 问题: OR , AND +- 填一个什么包, 有一个条件, 重量不超过M +- 不撑爆背包的前提下: +- 装下最多重量物品 +- 装下最大价值的物品 +- 有多少种方式带走满满一书包物品 +- 注意: 如果同一种物品可以拿无限次, 那么`dp[i][w] 表示: 前i 种 物品, 装weight w 的性质`. + +##### 方法策略 +- 还有几个物品 +- 还剩多少跟**总承重**有关 +- 用总承重M的大小来开数组. +- 不管几维数组, 总有一维是总承重M +- 比如: dp[i][w] = 能否用前i个物品, 拼出重量W (true/false) + +##### 放入的物品没有顺序 +- 放入的物品没有顺序: 最后一个背包内的物品是哪个 +- 放入的物品有顺序: +- 1. 最后一个背包内的物品是哪个 +- 2. 最后一个物品有没有进背包 + +### 区间类(Interval DP) +- 要么砍头, 要么砍尾 +- `dp[i][j] 表示 [i~j]之间的状态`, i, j 都是index! +- 求一段区间的解: max/min/count +- 转移方程通过区间更新: len = x +- 从大到小的更新 + +##### 特点 +- 给定一个序列/字符串, 进行操作 +- 最右一步会把 序列/字符串 去头/去尾 +- 剩下的是一个区间 [i, j] +- 状态自然定义为dp[i][j], 表示面对子序列 [i, ....., j]时的最优性质 +- 坐标型的下标模式 +- 切割后的三种情况: 首字母不相关, 末尾字母不相干, 首字母和末尾字母都相干. + +##### 三把斧 +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### 难点 +- 计算顺序: 不能按照i的顺序去算!!! 掐头/去尾的时候, 有 [i+1], 也有[i], 所以不能按照i +- 应该: 按照 **区间长度从小到大** 的顺序去算: +- 按照区间: 长度, 长度, 长度! +``` +for (len = ..; len <= n; len++) { + for (int i = 0; i <= n; i++) { + int j = i + len - 1; + ... + ... + } +} +``` + +### Bitwise Operation DP + + +### 优化 +- 空间优化: 1. Rolling array (easy) . 2. 观察dp的计算顺序, 看双行的左右计算方向, 找是否有舍弃不用的格子, 可以降维 +- 考虑空间降维优化时, 不必要死记硬背, 画一画, 看有没有优化的可能 +- 时间优化: 分析重复计算, 争取减少. + + +## Minimax/MaxiMin + +## Optimization problems +- memoization && subproblems +- Fibonacci +- Shortest paths +- guessing && DAG View + +## Double Sequence +- Sequence problem, have dp[] length of n + 1. +- Look at last index for clues +- Usually can start for loop at index = 0, and we handle the init conditions within the for loop (ex: assign particular value or skip i=0 rows) +- Rolling array (curr, prev pointer) to optimize space; note the rolling dimension should be apply at the top-for loop. + +## 存状态 +- 复杂的dp, 有时候会需要存一个状态: 拿/不拿, 放1/0, 输/赢 +- 通常加上一个维度 + + +# 记忆化搜索 Memoization DP +- 本质是DP, 所有DP也都是为了解决重复计算 +- 计算 f(0, N-1), 递归, recursive: 你要求什么, 直接上! +- 走过的老路, 就不要走了. f(i, j) 结束后, 存在数组 f[i][j]里. +- **从大到小**搜索, **自顶向下**, 其实是暴利解决的思路, 只是在深入到底的的过程中存了状态, 不需要重复计算. +- 时间复杂度跟递推的DP一样: O((n^2)/2) + +## 什么时候用记忆化搜索 +- 1. 状态转移特别麻烦,不是顺序性; +- 2. 初始化状态不是很容易找到; +- 3. 从大到小 +- 区间搜索(Interval dp), 适合用 memoization, 因为计算顺序稍微比较难 +- recursive function calls with slow runtime: think about memoization. + +## 特点 +- 需要开全局变量 +- 需要初始化: Mark算过的, 和没算过的 +- 递归里面: 第一行, 一定要记得查visited, 记忆化搜索的精髓. 算过了, 直接返回 + +## 缺点 +- 不能空间优化, 所有值必须记下来 + +## 时间空间复杂度的节省 +- 比如一个binary tree, 全部traverse 下来, 有将近 O(2^n) nodes; 如果存结果, 可能只需要visit unique的 O(n) nodes + +# Search + +## Breadth-first Search +- Track queue size, use the queue as in rotation +- When use BFS or DFS? In the background, DFS is built over stack memory (for each function call), which is limited (10k iterations), if space complexity is too high, then it's not sufficient. +- BFS is iterative, so all the space used will be in heap memory: as much memory you can add + +## Depth-first Search +- backtracking: do not repeatly visit a node +- DFS traverse O(n) +- 注意结束condition + +### for loop dfs vs. pick&&skip dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. +- 2. for loop dfs: for loop + backtracking. (ex: Combination Sum, Subset) + +### void dfs +- Pass around result object, and build into it +- `result` 是一个object (usually list) +- 每个dfs里面, 都会填充这个result object. + +### object dfs +- take front part, and dfs(remaining), where usually rst = dfs(remaining) = list +- cross-match front X rst list +- Note1: dfs returns partial solution, and the highest level builds the final result: `return dfs(...0, .)` +- Note2: there may not be backtracking, because there isn't any result or partial result parameter passing around +- optimization: the 'remaining' part can be cached/memoized so we don't make redundant calculation +- p: Word Break II, where `suffix -> rst list` are cached to avoid extreme case + +#### Regular Premitives +- usually object = int, string +- 每次dfs都会 build on 这个return value + +#### Customized Object +- 有时候单个value不足以track dfs的情况 +- 比如Binary Tree Maximum Path Sum: 要track single path, 又要track combinded path +- 这样就把每一个node的结果status, 存在一个 customized object 里面, pass around +- 这样做, 其实跟return一个premitive type并无不同, 但是能存更多information. +``` +private class PathSum { + int singlePathMax; + int combinedPathMax; + PathSum(int singlePathMax, int combinedPathMax) { + this.singlePathMax = singlePathMax; + this.combinedPathMax = combinedPathMax; + } +} +``` + +### Tree DFS +- 有时候要考虑: 包括root, 不包括root. 有个technique: +- 1. 写一个helper function dfs(always deal with root, never skip) +- 2. 在main function 自己身上recursive: call dfs(..), mainFunc(skip root) +- ex: `Path Sum III`, `Binary Tree Longest Consecutive Sequence II` + +# Combinatorics +- ex: study n-choose-k problems +- 两种方法: Backtracking 或者 插入法 + +## DFS 思想 +- 在每个index上面都要面临: pick/not pick的选择 +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + +## DFS 注意 +- input 有duplicate的时候, 必须sort +- 考虑candidate重复使用的规则(不可以重复使用): +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfslevel里面是会被考虑到的, 这里必须skip (这个就记住吧) +- 考虑candidate重复使用的规则(可以重复使用): 那么for loop里面dfs的时候, 使用curr index + +# Permutation +## 原理 +- Permutation的核心: 对于某一个index, 取, 或者不取 +- DFS的时候, 注意, 可能要从 index = 0 重新开始permutate +- 用 boolean visited[] 来track, 上一轮visited过得index +- 用两种方式做permutation: Backtracking(dfs, recursive), 或者是插入法(iterative) +- Find small string's permudation in large string: time O(n), using a HashSet of missingString: when the set.length == 0, that is a match of small string. + +## Backtracking DFS (Recursive) +- 把candidates 做成一个 remaining list +- 取/不取, 并且从 remaining list 里面去掉, 继续下一层dfs + +## 插入法 (iterative) +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element + +# Sweep Line +- 见到区间需要排序就可以考虑扫描线 +- 事件往往是以区间的形式存在 +- 区间两端代表事件的开始和结束 +- 需要排序 +- Meeting Room I, II + +# Backtracking +- Finding all (or some) solutions to some computational problems, notebaly constraint satisfaction problems +- It attemps to build/find all candidates and abandon partial candidate when the candidates appears not to be suitable(backtracking, backing off from wrong candidates) +- 尽量不要改变source data, 否则会变得难track +- 注意! 在for loop 和 end condition 里面改变 buffer object (ex: list), 一定都要backtracking: ex: `list.remove(list.size() - 1)` + +# Greedy +- follows the problem sovling approach of making the locally optimal choice at each stage with the hope of finding a global optimum. +- pro: simply, quick, easy to program +- cons: only locally optimal decision, **NOT** globally applicable. + +### When to use +- 1. Greedy - choice property: a global optimum can be formed by selecting a local optimum. (making a best choice at the moment, leading to global optimum) +- 2. Optimal Substructure: an optimal solution to be problem contains an optimal solution to subproblems. +- Note: the second point is very similar to sub-problem in DP, **BUT** greedy algorithm never re-consider the processed choices (main diff from DP). + +### Examples +- these examples can be found on GeekForGeeks +- Activity Selection +- Huffman Coding +- Job Sequencing +- Fractional Knapsack (backpack) +- Prim's Minimum Spanning Tree + +# Reservoir Sampling + +# Geometry + +# Approach +## 遇到Array +- Sorted? value boundary? + +## 遇到需要排序 +- Arrays.sort() + +## Divide and Conquer + +## Recursion +- Recursion 至少用O(n) space, n = depth of recursive call. Each recursive call takes a space in stack(limited) +- 所有recursive problem 都可以 interatively 解决, 但是有可能代码更复杂 +- ex: dfs +- always find the entry point or terminating point +- watch out for the return or result of recursed function + +### 特征 +- "compute nth ...", "list the first n ...", "compute all ..." 常常是recursive solution. +- space inefficient. 占用空间, at least O(n) + + +# Other minor problem sets +## 回文串 Palindrome +- 奇数串, 中间有个unique字符 +- 偶数, 中间开始有两个相同的字符 +- 找到所有的回文串, 只需要 O(n^2): isPalin[i][j]表示 S[i...j] 是否是palindrome + +## Windows Problem +- 加一个数 +- 删一个数 + +# Collections Functions + +## methods +- Collections.sort() +- Sort sub list: Collections.sort(list.subList(x, y)). [x, y), exclusive at index y. + +## ArrayList +- Integer[] array = {1, 2, 3}; +- new ArrayList(Arrays.asList(array)) +- list.add(index, object) +- list.removeRange[x, y) +- O(1) insertion on average. +- Underneath, the insertion cause the arrayList to doubles the size and copy all contents to a new array. Over time, the cost is 1 + 2 + 4 + ... n/8 + n/4 + n/2 = O(n), over time +- Therefore, the average insertion time is O(1) + +# CS Basics +## BigO Specials +- logN Runtime: when space gets halved each time, it indicates runtime of O(logN) +- Recursive Runtime: When the recursive call makes x branches, the runtime is likely to be O(x ^ N). [it's not always] +- Note: the base x of the recursive runtime DOES MATTER! 2^n is very different from 8^n = 2^3n + +## Time Complexity of graph/dfs block +- 如果一个算法(ex: tree, or dfs)种: 每个level的每个node衍生出 x 个 child, 然后不断衍生 n 遍截止 +- 那么整个structure的所有node总和: `x^n` +- 如果是个binary tree, 每个node出 x = 2 child, 然后 height = n, 那么node总量就是 `2^n` +- 从公式角度: 假设T(n)是每一个level的node总数 +- T(n) = x * T(n-1), 每一个node衍生 x children +- 那么 T(n) = x * (x * T(n - 2)) = .... = x^(n - 1) * T(1) +- => BigO time will be `O(x^n)` + +## Sum, PrefixSum +- PrefixSum: sum of [0 ~ i] items +- 找 sum[i, j] = preSum[i] - preSum[j - 1]; +- 根据题目具体情况 (ex: Copy Books), sum[i, j] 也可以倒序加出来, 存在int sum 里面 +- ex: Subarray Sum Closest + +## Math +- 转换成string +- % mod, 除法 +- Integer.MAX_VALUE, Integer.MIN_VALUE; if overflow, use long +- Integer.valueOf(number), where number is int + +### Math Functions +- Math.pow(x, 3) = x ^ 3; Math.pow(x, 1/3) = x ^ (1/3) + + +### Numbers +- Long a = 10; a.intValue() => int +- Integer: Integer.parseInt("123") +- parse binary string into integer: Integer.parseInt("010", 2) = 2; + +### Probability theory + +### Combinatorics +- a selection of items from a collection, and order does not matter +- The complexity is O(C(n,k)): O(min(n^k, n^(n-k))) + +#### subset time&&space +- independent choice of either pick&¬ pick. You pick n times: O(2^n) + + +# Brainteaser + +# Operating system +- processes +- threads +- concurrency issues +- locks +- mutexes +- semaphores +- monitors +- deadlock, livelock and how to avoid +- what resources a process and a thread needs. +- how context switching works, how it's initiated by the operating system and underlying hardware. +- scheduling and the fundamentals of "modern" concurrency constructs. + +# Threads +## Two approaches +- Implement the java.lang.Runnable interface +- Extend the java.lang.Thread class + +# Bit Manipulation +- Bit OR |, AND &, XOR ^ +- Bit shift: <<, >>; the result of shift has to be stored : `a = a >> 1`; +- A << 1: binary of A shifted left for 1 bit, which result in value x 2 +- A >> 1: divide by integer 2. Note: decimals are ignored in the result. +- bit shift is a lot faster than reqular 'times' operation. +- 32 bit number: leading bit = 1, negative numbjer; leading bit = 0, positive number. +- '>>' add leading '1' if the 32 bit number originally has leading '1'. +- Java/python: logical shift >>>, always add leading '0' regardless of the sign of the 32-bit number. That is, it may turn a negative number to positive, if the leading bit is originally '1' +- Because with '( )', make sure to surround the desired operation +- & 0000 = clean up; | ABC = assign ABC +- A^B=C, then A = B^C +- bits可以用来表示不同的状态, 比如2bit可以表示4种状态: 00, 01, 10, 11 +- Math.pow(2, h) = 2 << (h - 1); 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- Also, 1 << h = 2 ^ h; 1 << h 就是 2 * 2 * 2* ....乘h次. +- bit operation should be in parentheses + +# Memory +- Heap +- Stack + +# Java Garbage Collections +- https://www.dynatrace.com/resources/ebooks/javabook/how-garbage-collection-works/ +- downside: garbade collection adapts to all kinds of complex situations, which makes it hard to optimize -> performance problem. + +### Heap +- operating system allocates heap memory to JVM, where JVM uses the heap to store/removes objects. +- As long as a object is referenced, JVM will not delete the object +- There exist a `first reference` in the tree: `Garbage Collection Roots`, GC roots. + +### Garbage Collection Roots +- Four types of Garbage Collection roots: +- 1. Local variable: kept alive by the stack of a thread. This is not a real object virtual reference and thus is not visible. For all intents and purposes, local variables are GC roots. +- 2. Active Java threads: live object, and are GC root. +- 3. Static variables: referenced by their classes, can be removed when classes are garbage-collected. +- 4. JNI references: java objects that the native code has created as part of JNI call. + +#### Example +- A typical java application has these GC roots: +- local varialbes in main method +- the main thread +- static variables of the main class + +### Marking and Sweeping Carbage +- two simple steps in garbage collection: +- 1. traverses all object references, starting with GC roots, and mark all found objects as live +- 2. all heap memory that is NOT occupied by marked objects are reclaimed (cleaned up for reuse, marked as free) +- This resolves the classic memory leak: unreachable but not deleted objects +- However, this does not solve this memory leak: developer forgot to `dereference` object, which will always be treated as live. +- My thoughts: Java objects should be used and not forgotten; also, we can set object to `NULL` if no longer used but still referenced. +- Setting to `NULL` will simply make the object elligible for garbage collection. + + +# Pain Point +- For any array access, make sure to check the boundary!!! +- subsequence: not continuous, can skip candidate! +- subarray: continous! + +## NP-Complete problems +### wiki +- https://en.wikipedia.org/wiki/NP-completeness + +### Knapsack +- Backpack problem: Given a set of items, each with a weight and a value, determine the number of each item to include in a collection so that the total weight is less than or equal to a given limit and the total value is as large as possible. +- https://en.wikipedia.org/wiki/Knapsack_problem + +### Travelling salesman +- Given a list of cities and the distances between each pair of cities, what is the shortest possible route that visits each city and returns to the origin city +- https://en.wikipedia.org/wiki/Travelling_salesman_problem + +## Basics +- Coding, speeding, readability +- Data structure: given a problem, how to model it into data structure? What type of class interface to use? 这个很重要, 如果不能很好地model一个问题, 后面就不知道该怎么写, 一定懵逼. +- Algorithm: How do we solve the problem. +- communication: think, and communicate ideas +- : colon | ; semicolon | ! exclamation mark | { curly bracket } | [ square bracket] | ( parentheses ) +- 写思路, 写skeleton guide (even on the side), 然后再开始coding + +## Edge case +- consider edge case +- 解释edge case的原因; 也可以throw exception呀 + +## Advanced +- 写出的function, 加入要解决更大scale的问题, 比如说call 10k 遍, 是否有冗余计算或者空间? 如何优化? + +## Pre-Computation +- If the problem is on a server, we can maintain data storage, cache, or inmemory data structure: precompute +- Knowing a 'server' is available in the problem, that gives indications of precompute appraoch: and then just use the result/resource over and over. + +## Knowledge && experience +#### Java Design Pattern +- https://www.journaldev.com/1827/java-design-patterns-example-tutorial +- Singleton: create a static instance of class that can be accessed globally without recreation. +- Factory: Used when a super class has multiple sub-classes and we may return one of the sub-classes on demand. Example: returning different client based on region diff --git a/LevelReviewPage.md b/LevelReviewPage.md new file mode 100644 index 0000000..1d1d69d --- /dev/null +++ b/LevelReviewPage.md @@ -0,0 +1,9772 @@ + + + +## Easy (153) +**0. [Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Hamming%20Distance.java)** Level: Easy Tags: [] + +bit: XOR, &, shift>> + + + +--- + +**1. [Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Happy%20Number.java)** Level: Easy Tags: [] + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + + + +--- + +**2. [HashWithArray.java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithArray.java)** Level: Easy Tags: [] + + + + +--- + +**3. [Heaters.java](https://github.com/awangdev/LintCode/blob/master/Java/Heaters.java)** Level: Easy Tags: [] + +第一步: +生题型, 理解题意需要时间: +从字面和画图而言, 就是定住房子一个个过,房子左右的distance需要足够达到heater. 目标是招尽可能小的radius, 所以house和heater紧贴着是必要的. +在for loop里面定下house,把heater当作一个区间移动, 达到的第一个合适区间,这就是当下最小的理想radius,取这个值跟既定radius作比较。 +比较之后,继续移动house,再试着移动heater区间去match。 + +第二步: +Binary Search + +注意! +题目没有说given array是否sort, 我们必须sort才能够区间移动或者binary search. +TODO: +http://www.cnblogs.com/grandyang/p/6181626.html + + + +--- + +**4. [IndexMatch.java](https://github.com/awangdev/LintCode/blob/master/Java/IndexMatch.java)** Level: Easy Tags: [] + +有序, 假设有这样的数字:target. +target 左边的数字,一定不比index大,target右边的数字,一定比index大。 +这样可以binary search.O(logn) + + + +--- + +**5. [Insert Node in a Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Node%20in%20a%20Binary%20Search%20Tree%20.java)** Level: Easy Tags: [BST] + + +往Binary Search Tree里面加东西,一定会找到一个合适的leaf加上去。 + +那么:就是说someNode.left or someNode.right是null时,就是insert node的地方。 + +找到那个someNode就按照正常的Binary Search Tree规律。 + + + +--- + +**6. [Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/Jewels%20and%20Stones.java)** Level: Easy Tags: [Hash Table] + +1524017454 + +给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 + +#### Basic HashSet + + + +--- + +**7. [Longest Univalue Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Univalue%20Path.java)** Level: Easy Tags: [] + +弄明白path的意义: 连接node的edge. +要找MAX, 可以在class scope里面定义一个max variable. + +用minimum amount of code来概括几种不同的情况: left == root, right == root, or left == root == right. + + + +--- + +**8. [Matrix Zigzag Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Matrix%20Zigzag%20Traversal.java)** Level: Easy Tags: [] + +分析4个step:right, left-bottom,down,right-up +implement时注意index.有点耐心 + + + +--- + +**9. [Minimum Absolute Difference in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Absolute%20Difference%20in%20BST.java)** Level: Easy Tags: [BST] + + +BST: inorder-traversal: 先left node(adding to stack till left leav), 再process stack.peek (mid node), 再 add rightNode && dive to rightNode.left leaf + + + +--- + +**10. [O(1) Check Power of 2.java](https://github.com/awangdev/LintCode/blob/master/Java/O(1)%20Check%20Power%20of%202.java)** Level: Easy Tags: [Bit Manipulation] + + + + +--- + +**11. [Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)** Level: Easy Tags: [Array, Two Pointers] + + +- 更正常的start/end partition pointer类似: when condition meet, swap +- Clean up TODO + + + +--- + +**12. [Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Pascal's%20Triangle%20II.java)** Level: Easy Tags: [] + +简单处理array list. + + + +--- + +**13. [Permutation Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Index.java)** Level: Easy Tags: [] + +和Permutation Sequence相反的题目。思想类似。 + +题目为Easy,琢磨很久,分析: +每个数位的数字,都是跳过了小于这数字开头的多种可能。 + +举例【6,5,2】吧。我们找6,5,2是permudation里面的第几个。 +正常排序,也就是permutation的第一个,应该是【2,5,6】 +如果要从首位,2,变成6,要跨过多少可能性呢? +很简单,就是问:小于6的数字有多少个呢?(2,5).每个数字变成head,都有各自的一套变化,都有(n-1)!种可能。 + +本题做法:每个(n-1)!加起来。 Note:(n-1) means, 开头的数字(2,5)各带出多少种排列,也就是不就是(n-1)!嘛。 + 这一步,计算数量很简单: (有几个小于6的数字) ×(除去head剩下有多少个数字)! + +以上 ,都是为了把6推上皇位,而牺牲的条数。 + +那么把6推上去以后,还有接下去的呢。 + +接下去要看5,2. +6确定,后面permudation可变的情况有可能是【6,5,2】,那还可能是【6,2,5】呢。 + +Same process, 看given 数组的第二位5,算它接下去: +1. 有几个数字小于5呢? +2. 除去5,还有几个数字可以 factorial呢? +3. 一样的。第一步就结果乘以第二步。 + +最后接下去要看最后一个元素2了。 + + +6,5,2全看过了以后,加起来。 +就是【6,5,2】上位,所踏过的所有小命啊! + +我这解释太生动了。因为耗费了好长时间思考... + + + +--- + +**14. [Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array] + + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**15. [Reshape the Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Reshape%20the%20Matrix.java)** Level: Easy Tags: [] + +读例子理解题意. +理清counter case. Basic implementation + + + +--- + +**16. [Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)** Level: Easy Tags: [] + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + + + +--- + +**17. [Search Insert Position.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Insert%20Position.java)** Level: Easy Tags: [] + +一般的binary search. +在结尾判断该return 哪个position。 + + +--- + +**18. [Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Word%20Distance.java)** Level: Easy Tags: [] + +找short distance, wordB可以在wordA的前后;而同一时间,只需要计算一个最近的up to date的distance。 +greedy不断变更A/B index再做比较即可。 + + + +--- + +**19. [Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number.java)** Level: Easy Tags: [] + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + + + +--- + +**20. [String Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/String%20Permutation.java)** Level: Easy Tags: [] + +把#of occurrences 存进HashMap, 第一个string 做加法,第二个string做减法。最后看是否有不等于0的作判断。 + + + +--- + +**21. [Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)** Level: Easy Tags: [Math] + + + + +--- + +**22. [Two Strings Are Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Strings%20Are%20Anagrams.java)** Level: Easy Tags: [] + +方法1:char ascii 用count[256] +坑:不要想象这个是个26letter lowercase. may not be true. + +方法2: 若是其他字符encoding, 而不只是utf16-encoding (java char)? +那么就继续用string去做 + + + +--- + +**23. [Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited value. +- 在nest for loop里面validate row,col,and block. +- validate block要利用i 和 j 增长的规律。 +- 说白了,i && j是按照0~n增长的index, 具体怎么用是可以flexible的。这个方法在同一个nest for loop解决所有运算。 +- `int c = 3 * (i % 3) + j % 3;` //make use of how i and j increases +- `int r = 3 * (i / 3) + j / 3;` + +#### A bit Slower approach +- 单独做block validation: validate block的时候虽然看到了4层for. 其实也就是n^2 +- 可能代码稍微复杂一点 + + + +--- + +**24. [Word Pattern.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Pattern.java)** Level: Easy Tags: [] + +每个char代表一个pattern。用HashMap. +但不够,如果a也match dog, b也match dog, 纠错了。比如pattern = "abba", str = "dog dog dog dog"。 +因此第二个HashMap 反过来。 +确保pattern和str一一对应。 + + + +--- + +**25. [Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Anagram%20Mappings.java)** Level: Easy Tags: [Hash Table] + + +比较简单, 用HashMap 存index list. 最后再遍历一遍数组A, 列举出所有元素. +O(n) + + + +--- + +**26. [Judge Route Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/Judge%20Route%20Circle.java)** Level: Easy Tags: [String] + + +简单的character checking. 各个方向, 加加减减. + + + +--- + +**27. [Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/Island%20Perimeter.java)** Level: Easy Tags: [Hash Table] + + +#### Brutle +- 每个格子4个墙;每个shared的墙要-2 (墙是两面, -1 * 2) +- 最后合计结果就好. + +#### Hash Table +- 不必想太多用HashMap做.但是也可以思考一下: +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么久可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不打,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + + + +--- + +**28. [Power of Three.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Three.java)** Level: Easy Tags: [Math] + + +方法1: +Power of 3: 3 ^ x == n ? +意思是 n / 3 一直除, 最后是可以等于1的, 那么就有了 n/=3, check n%3, 最后看结果是不是整除到1的做法. 用while loop. + +方法2: +如果n是power of 3, 那么 3 ^ x的这个 x一定是个比n小的数字. 那么可以在 0 ~ n 之间做binary serach, 但是就比较慢. + +方法3: +巧妙的想法.最大的3^x integer是 3^19. 那么找到这个数, 一定可以被n整除. 一步到位. + + + +--- + +**29. [Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)** Level: Easy Tags: [Array, Math] + + +简单的实现, 加1, 进位. 唯一取巧的地方, 最后如果要多一位, 一定是10000...这个模式, 可以走捷径, 直接来个+1size的array, 然后第一位=1. +注意,转换成long也不合理,用太多memory. + + +--- + +**30. [Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)** Level: Easy Tags: [Bit Manipulation, Math] + + +跟powerOfThree一样: 可以loop, check mod; 也可以用binary search找合适的数字. + + + +--- + +**31. [Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i>> 避免leading负数补位. + + + +--- + +**44. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**45. [Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer] + + +#### Vote 计数 +- vote++, vote--到最后剩下的就是winner. Time O(n), Space O(1) +- Majority Number是指超半数. 超半数的数字, 最后都会至少有vote>=1: match current majority number,vote++;if not, vote--. +- 注意:assume valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 + +#### HashMap count occurance +- Time, Space: O(n) + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**46. [Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS] + + +给一串integers, list里面可能有nest list. 算总的sum. 规则, 如果是nested list, 每深一个depth, sum要乘以depth. + +#### DFS +- New interface to understand: object contains integer or object +- Visit all && sum, consider dfs. +- bottom->up is easier: pick nested object and execute dfs, which returns sum of it, add with (level value * weight). +- 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) +- Note1: not multiplying on overall level sum. Only multiply level with single value at this level. +- Note2:top->bottom is not necessary: there is not need of passing added object into next level. + +#### BFS +- bfs, queue, 处理queue.size(). +- use a level variable to track levels + + + +--- + +**47. [Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Same%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### BFS +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**48. [Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +如题, build balanced BST from sorted array + +#### DFS +- Binary Search Tree特点: 左边的node都比右边的node小. +- height balance, subtree height 相差<1, 必须左右sub tree均分. 做DFS(num, start, end) +- 在每一个level, 找到中间点, 然后分割2半, 继续dfs +- Divide and Conquer +- time/space: O(n), visit all nodes, no redundant visits. + + + +--- + +**49. [Add Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Digits.java)** Level: Easy Tags: [Math] + + +方法1: 普通做法就是按照题意, double-while loop把数字加起来. 第一层循环是O(n), 然后第二层循环就少很多数位, overall O(n) + +方法2: 找数学规律, 每过9个数字, 取mod就会开始重复, 所以给所有数字取mod 就可以间接找到答案. O(1) + + + +--- + +**50. [Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +HashMap + + + +--- + +**51. [Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)** Level: Easy Tags: [Backtracking, Binary Tree, DFS] + + +给一个binary tree, 返回所有root-to-leaf path + +#### DFS, backtracking +- Find all paths, bfs/dfs all works. dfs will be simplier to write +- Recursive:分叉. dfs. +- top->bottom: enumerate current node into the list, carry to next level, and backtrack +- top->bottom is trivial to consider: path flows from top->bottom + +#### DFS, bottom->up +- We can also take current node.left or node.right to generate list of results from the subproblem +- let dfs return list of string candidates, and we can run pair the list with currenet node, once they come back. +- TODO: can write code to practice + +#### Iterative +- Iterative, 非递归练习了一下 +- 因为要每次切短list, 所以再加了一个Stack 来存level +- 单这道题用dfs更简单, 因为找的就是从头到尾的path, 是dfs的pattern + + + + +--- + +**52. [Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Two Pointer: Slow Fast Pointer +- O(1) sapce: 用快慢指针。一个跑.next, 一个跑.next.next。 总有一次,fast会因为cycle而追上slow。 +- 那个时候其实slow.val = fast.val. + +#### Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**53. [Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)** Level: Easy Tags: [Design, Stack] + + +双Stack:一个正常stack,另一个minStack存当下level最小值. 注意维护minStack的变化 + +另外. 如果要maxStack,也是类似做法 + + + +--- + +**54. [Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)** Level: Easy Tags: [Design, Stack] + + +#### 双Stack +画图, 知道最后maintain的stack是那个 reverseStack: pop(), peek(), empty() 都在这个stack上, 无需变换. +push()里面做stack和reverseStack的来回倾倒. +相比老的code, 在PUSH里面做倾倒, 更容易读. + +#### Previous notes +双Stack. 一个是等于是queue,一个是backfillStack. +Tricky: 是在pop()和peek()的时候backfill, 并且要等到stack用完再backfill. +写一下例子就知道,如果提早backfill,stack.peek()就不是queue的head了. + + + + +--- + +**55. [Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Integer.java)** Level: Easy Tags: [Math] + + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + + + +--- + +**56. [Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + + +#### s- qrt(int x) +- 理解题意, 从[0, x]找一个可以m*m=x的值. +- 注意, 如果找不到, 最后问考官该return一个什么值:按道理,因为return int, 会取整,那么return一个平方最close to x就可以. +- 注意 mid 用 long, 因为很可能超过最大int. + +#### sqrt(double x) +- 二分float number, 应该用精度来定义结尾. +- 还是二分, 但是判断条件变成: while ( end - start > eps) +- eps = 1e-12,也就是精度到1e-12 + + + +--- + +**57. [First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Bad%20Version.java)** Level: Easy Tags: [Binary Search] + + +Binary Search + +根据isBadVersion的性质,判断还如何end=mid or start=mid. +isBadVersion 是有方向的嘛,一个点错了,后面全错。 + + + +--- + +**58. [Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### 方法1: +找是否有overlap. priorityQueue 按照start time排序好以后, 比较current和peek: current.end > peek.start? + +#### 方法2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 + + + +--- + +**59. [Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Recursive +- 在自己的基础上recursive, 不用helper function +- Divide and Conquer, with helper(dfs) method +- O(n) time, no extra space + +#### Iterative: Stack +- Add left nodes all the way +- Print curr +- Move to right, add right if possible +- O(n) time, O(h) space + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. + +若不右移, 很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + +#### HashMap +? How? + + + +--- + +**60. [Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Change%20to%20Anagram.java)** Level: Easy Tags: [String] + + +HackerRank里面的random 题目: 给一个string, 一切成两半, 看两半要变化多少个字符, 能变成anagram. + +- 切两半成两个String A,B. 分别在int count[26]里面++, --. +- 记录 26个小写字母的频率. 如果全部抵消, 就是anagram. +- 注意: 最后count出来要除以2:字母不同,会在不同的字母位上加减count,那么就是刚好重复计算了一遍。所以除以二 + +- Note: HackerRank里要注意自己写: Scanner, import java.util, non-static method ...etc. + + + +--- + +**61. [Classical Binary Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Classical%20Binary%20Search.java)** Level: Easy Tags: [Binary Search] + + +#### Binary Search Template +- while: start + 1 < end +- mid = start + (end - start) / 2; +- 根据mid作比较 +- 末尾double check start, end. + + + + +--- + +**62. [Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)** Level: Easy Tags: [DP, Memoization, Sequence DP] + + +每一步可以走1步或者2步, 求总共多少种方法爬完梯子. + +#### Recursive + Memoization +- 递归很好写, 但是重复计算, timeout. time: O(2^n) +- O(2^n): each n can spawn 2 dfs child, at next level, it will keep spawn. Total 2^n nodes will spawn. +- 用全局变量int[] memo 帮助减少重复计算 +- O(n) time, space + +#### DP +- 加法原理, 最后一步被前两种走法决定: dp[i] = dp[i - 1] + dp[i - 2] +- 基础sequence DP, int[] dp = int[n + 1]; +- DP[]存的是以 1-based index的状态 +- dp[i]: count # of ways to finish 前i个 台阶 +- 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1] +- dp[0]往往是有特殊状态的 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**63. [Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +#### Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, +- 找到 node.val == target, 或者走位走完, return closest + + + +--- + +**64. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**65. [Closest Number in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Number%20in%20Sorted%20Array.java)** Level: Easy Tags: [Binary Search] + + +- Binary Search 的一种变型, LintCode无法再跑一边. +- 考虑mid-1, mid+1. +- 一旦没有mid = target.index。 那么target最终就narrow down在(mid-1,mid) 或者(mid,mid+1) + + + +--- + +**66. [Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +A complete binary tree is a binary tree in which every level, except possibly the last, + +is completely filled, and all nodes are as far left as possible + +#### BFS +- 当出现了第一次有 null children的node的时候, 说明到了leaf level, mark flag = true; +- 自此以后,queue再不该有node再有child; queue后面出现的node的left/right child应该都是null +- 否则就是有问题, return false; + + + + +--- + +**67. [Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Strings.java)** Level: Easy Tags: [String] + + +看StringA是不是包括所有 StringB的字符. + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + + + +--- + +**68. [Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate.java)** Level: Easy Tags: [Array, Hash Table] + + +无序数组, 找是否有重复element, return true/false. + +#### HashSet +- No brain: HashSet. +- Time O(n), Space O(n) + +#### Sort, Binary Search +- Arrays.sort(x): Time O(nLogN), Space O(1) +- 排序后, 重复数会排在一起, 然后 binary search + + + +--- + +**69. [Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20II.java)** Level: Easy Tags: [Array, Hash Table] + + +Unsorted array, 找出是否有duplicate elemenets: 必要条件是, 这两个element的index i,j 的大小最多相差k. + +#### HashSet +- 很巧妙地根据k range地条件, 把HashSet里面的值控制在[i - k, i] +- 每次不断地往set里面加新元素, 从set里减去末尾index的元素 +- 而set.add(x)如果遇到重复, 会return false. +- 一旦在这个length k 的 range里面, 有重复, 就符合条件. +- Time O(n) + +#### HashTable +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k +- Time O(nm), m = # of duplicates + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**70. [Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)** Level: Easy Tags: [Brainteaser, DP, Game Theory] + + +#### Brainteaser +- 著名Nim游戏 +- 写一些,发现n=4,5,6,7,8...etc之后的情况有规律性: 谁先手拿到4就输了. +- 最终很简单n%4!=0就可以了, time, space O(1) + +#### DP +- 正规地找规律做, 就跟 coins in a line 一样, 按照先手后手来做 +- 可以rolling array 优化空间 +- Time O(n), 当然啦, 这个题目这样会timeout, 可以使用brainteaser的做法写出结果. + + + +--- + +**71. [Convert Integer A to Integer B.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Integer%20A%20to%20Integer%20B.java)** Level: Easy Tags: [Bit Manipulation] + + +把Integer A 转换成 Integer B 需要改变多少bits? + +#### Bit Manipulation +- a^b 显示出bit format里面有不同binary code的数位. +- 每次 (a^b)>>i 移动i位之后, 再 & 1时其实是指留下这一位的数字. +- count +- 其实用到了 ^ 找不同的bit, >> 移位, &1 mask + + + +--- + +**72. [Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)** Level: Easy Tags: [Basic Implementation] + + +根据 Cosine Similarity 的公式, basic implementation + + + +--- + +**73. [Count 1 in Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%201%20in%20Binary.java)** Level: Easy Tags: [Bit Manipulation] + + +count 一个 32-bit number binary format 里面有多少1 + +#### Bit Manipulation +- shift >> i +- apply mask & 1 + +#### Convert to string O(n) space +可以把integer -> string -> char array. + + + +--- + +**74. [Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)** Level: Easy Tags: [Basic Implementation, String] + + +介绍一种count数字的方法, 然后每一行读出上一行的结果, 一行一行推算. 问nth行是啥样? + +#### Basic Implementation +- 主要是题意很难理解, 非常misleading, 等到看明白题目, 其实没有什么算法要求. +- Count duplicates and print + + + +--- + +**75. [Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + +time: O(nm), m = # of colors +space: O(nm) + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, 但是不知道最后一个房子选什么颜色, 那么就遍历最后一个房子(i - 1)的颜色 +- 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- 考虑DP最后一个位置的情况(颜色选择):需要附带颜色status在DP[i]上: 定义二维数组, 其中一位是status +- dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. +- dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- 然后选ith 房子的 color, 再选(i-1)th 房子的color. Double for loop, skip same color + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 + + + +--- + +**76. [Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +找连续的持续上升子序列的长度. + +#### Coordinate DP +- 1D coordinate, dp 的角标, 就是代表 index i 的状态 +- 求最值, dp[i] = 在index i位置的最长子序列 +- 如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1 +- 如果没有持续上升, 那么dp[i] = 1, 重头来过 +- maintain max + +#### Basic +- 用一个数存current count, maintain max + + + +--- + +**77. [House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) or rolling array O(1) + +搜刮房子, 相邻的不能碰. 每个房子里有value, 求max. + +#### Sequence DP +- dp[i]: 前i个房子拿到的max gain +- 看最后结尾状态的前一个或前两个的情况,再综合考虑当下的 +- 搞清楚当下[i]的和之前[i-x]的情况的关系: 不可以连着house, 那么就直接考虑 dp[i-2]的情况 +- Sequence DP, new dp[n + 1]; + +#### Rolling Array +- [i]'只和前两个位子 [i-1], [i - 2]'相关 +- 用%2来标记 [i], [i - 1], [i - 2]三个位置. +- 其他滚动时惯用curr/prev来表示坐标, 这里%2虽然抽象, 但是更加实用. + + + + +--- + +**78. [Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Anagrams%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, Sliding Window] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### HashTable +- count character apperance 就想到hashtable +- 注意countS, countP 的技巧: 作比较只需要O(26) +- Overall timeO(n) +- 小心不要用一个int[] count 来技术 查0, 复杂度是O(n) + + + +--- + +**79. [Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Primes.java)** Level: Easy Tags: [Hash Table, Math] + + +计数: 所有小于n的prime number. + +#### prime number定义 +- >=2的没有除自己和1以外公约数的数。 +- 还有另外一个定义方法: 这个n,有没有小于n的一个i, 而达到: i * i + # of i = n. 如果有,那就不是 prime + +#### Steps +- 一个boolean长条,存isPrime[]。 然后从i=2, 全部变true. +- hash key: the number itself +- 然后利用这个因子的性质,非prime满足条件: self*self, self * self + self ... etc. +- 所以就check每一个j, j+i, j+i+i, 然后把所有non-prime全部mark成false. +- 最后,数一遍还剩下的true个数就好了 + + + +--- + +**80. [Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Node%20in%20a%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +Given Singlely linked list, 删除一个任意node (不能是head node) + +#### Basic +- update node.val +- Link curr.next to curr.next.next + + + +--- + +**81. [Excel Sheet Column Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Number.java)** Level: Easy Tags: [Math] + + +#### Math +- 26位的运算, 根据10位运算去思考 +- 'A' - 'A' = 0. 所以 char - 'A' + 1 = 题目里的对应数位 +- 或者: 26位运算和10位一样:num += 每位的digit * Math.pow(26, 数位号) + + + + +--- + +**82. [Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Title.java)** Level: Easy Tags: [Math] + + +#### 基本换算 +- 26位 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + + + +--- + +**83. [Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game.java)** Level: Easy Tags: [String] + + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + + + +--- + +**84. [Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20strStr().java)** Level: Easy Tags: [String, Two Pointers] + + +给两个string A, B, 找一个 B 在 A 种的起始位置. + +#### Two Pointer +- 找到B在A中的起始位置, 然后看一下从这个点开始的substring是否等于B就可以了 +- 还挺多坑的, 这些可以帮助优化: +- 1. 当B是“”的时候,也就是能在A的其实位置找到B....index = 0. +- 2. edge condition: 如果 haystack.length() < needle.length() 的话, 必须错, return -1 +- 3. 如果在某个index, A后面剩下的长度, 比B的长度短, 也是误解, return -1 + + + +--- + +**85. [Last Position of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Last%20Position%20of%20Target.java)** Level: Easy Tags: [Binary Search] + + +给一个sorted integer array, 找target出现的最后的index. array 里有重复数字 + +有重复,不是末尾点,继续binary search + + + +--- + +**86. [Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/Length%20of%20Last%20Word.java)** Level: Easy Tags: [String] + + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + + + +--- + +**87. [Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + +O(n)跑2遍for. +O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. +特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 + + + +--- + +**88. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**89. [Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)** Level: Easy Tags: [Array, Quick Select, Quick Sort] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- quickSelect 可以找到 kth 最小的元素 +- 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- quick select 的template要熟悉一下, 一下子可能想得到, 但写不出来 +- 主要步骤: partition, dfs, only recur on one part of the array + + + + +--- + +**90. [Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Middle%20of%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +找Linked List的中间node + +- 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + + + +--- + +**91. [Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)** Level: Easy Tags: [Design] + + +让一个class 是 singleton + + + +--- + +**92. [Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Linked%20List%20Elements.java)** Level: Easy Tags: [Linked List] + + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + + + +--- + +**93. [Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)** Level: Easy Tags: [DP, Math, Memoization] + + +#### Memoization +- fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + +#### DP array. +- 滚动数组, 简化DP + +#### recursively calculate +- recursively calculate fib(n - 1) + fib(n - 2). 公式没问题, 但是时间太长, timeout. + + + + +--- + +**94. [Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单, 但是要在Linkde List random access坐标, 是很难得: 所以需要把一半 ListNode 翻转 +- reverse linked list: 遍历接开头 +- 用快慢指正找到mid point +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + +#### Previous Note +- Palindrome都是要两边回溯相等 +- linkedlist不能reverse iterating, 那么就reverse the list, 从中间开花作比较。 + + + +--- + +**95. [Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +#### Reverse List +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + + + +--- + +**96. [Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Linked%20Lists.java)** Level: Easy Tags: [Linked List] + + +给两个 linked list, 问从哪个node开始, 两个 linked list 开始有重复? + +#### Basics +- 长短list,找重合点 +- 长度不同的话,切掉长的list那个的extra length +- 那么起点一样后,重合点就会同时到达 +- Time O(n) * 2, constant space + + + +--- + +**97. [Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation.java)** Level: Easy Tags: [Hash Table] + + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count occurrance +- 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assum lower case letter. 应该至少是所有ASCII code + + + +--- + +**98. [Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Check Palindrome +- 前后两个指针, 往中间移动, 查看是否字母重合 + +#### 过滤 alphanumeric +- 可以用 ASCII code 来手动过滤, 只要 '0' ~ '9', 'a' ~ 'z', 'A' - 'Z' 之间的 +- 也可以用 regular expression: match 所有这些字母, 是 [a-zA-Z0-9] +- 那凡是不是这些字母的 match, 就是取反: "[^a-zA-Z0-9]". 测试: https://regex101.com/ + + + +--- + +**99. [Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)** Level: Easy Tags: [Design, Stack] + + +如题. + +#### Queue, 倒水 +- 两个Queue,交互倒水 +- 用一个Temp做swap + +##### 做法1 +- 逻辑在push里面: +- 1. x 放q2。 +- 2. q1全部offer/append到q2. +- 3. 用一个Temp做swap q1, q2. +- q1的头,就一直是最后加进去的值. + + +##### 做法2 +- 逻辑在top()/pop()里, 每次换水,查看末尾项. + + + + +--- + +**100. [Implement Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack.java)** Level: Easy Tags: [Stack] + + +随便用一个data structure, implement stack. + +#### Stack, 先入, 后出 +- ArrayList: return/remove ArrayList的末尾项。 +- 2 Queues + + + +--- + +**101. [Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### DFS +- 简单处理swap +- recursively swap children + +#### BFS +- BFS with Queue +- 每次process一个node, swap children; 然后把child加进queue里面 +- 直到queue process完 + + + +--- + +**102. [Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree, 找最深depth + +#### DFS +- 这里要走过所有的node, 所以dfs非常合适 +- Divide and conquer. +- 维持一个最大值: Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +- 注意check root == null + +#### Note +- BFS is doable as well, but a bit more code to write: tracks largest level we reach + + + +--- + +**103. [Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### BFS +- Shortest path; minimum depth: 想到BFS, check level by level, BFS更能确保更快找到结果 +- depth definition: reach to a leaf node, where node.left == null && node.right == null +- BFS using queue, track level. + + +#### DFS +- Divide and Conquery一个最小值. +- 注意处理Leaf的null: null leaf 出现的时候, 就忽略这个leaf, 直接return算有leaf +- 另一种count的方法: 用Integer.MAX_VALUE代替 null leaf,这样可以避免错误counting. (不能直接recursive) +- 这个无论如何都要走所有node, 所以dfs应该比较适合. + + + + +--- + +**104. [Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**105. [Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +检查binary tree是否 identical. + +特点: subtree如果是有旋转的, 只要tree node value相等, 就可以算是identical + +#### DFS +- 在DFS的基础上, 比对左左,左右,右左,右右 + + + +--- + +**106. [Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)** Level: Easy Tags: [DFS, Tree] + + +#### DFS +- 基础binary tree traversal. 注意对null child的判断 + + + +--- + +**107. [Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree s, 和一个binary tree t, 检查t是不是s的subtree. + +#### DFS +- 跟 identical binary tree的写法很像 +- 只有 current s.val = t.val 的时候才需要compare same tree. +- 其他情况, 继续recursively isSubtree +- 注意:即使找到T1 == T2, 但很可能只是数字相同(这里不是binary search tree!!), 而children不同 +- 所以同时要继续recursively isSubtree(T1.left, T2) ...etc. + + + +--- + +**108. [Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Tree] + + +给一个Binary Tree root, 以及两个node A, B. 特点: node里面存了parent pointer. 找 lowest common ancestor + + +#### Hash Set +- 这个题有个奇葩的地方, 每个node还有一个parent, 所以可以自底向上. +- save visited in hashset. 第一个duplicate, 就是A B 的 lowest common ancestor + +#### Save in lists +- 自底向上。利用parent往root方向返回 +- 把所有parent存下来, 然后在两个list里面找最后一个 common node + +#### 注意 +- 无法从root去直接搜target node 而做成两个list. 因为根本不是Binary Search Tree! + + + + +--- + +**109. [Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/Hash%20Function.java)** Level: Easy Tags: [Hash Table] + + +#### Hash Function +- 解释Hash怎么做. +- Hash function例子: +- hashcode("abcd") = (ascii(a) * 33^3 + ascii(b) * 33^2 + ascii(c) *33^1 + ascii(d)*33^0) % HASH_SIZE +- 用到的参数比如: magic number 33, HASH_SIZE. + +- Hash的意义是:给一个string key, 转换成数字,从而把size变得更小。 +- 真实的implementation还要处理collision, 可能需要design hash function 等等。 + + +##### 每一步都%HASH_SIZE的原因 +- hashRst = hashRst * 33 + (int)(key[i]); +- hashRst = hashRst % HASH_SIZE; +- 原因是,hashRst会变得太大,所以不能算完和 再 %... + + + +--- + +**110. [Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Sorted%20Lists.java)** Level: Easy Tags: [Linked List] + + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + + + +--- + +**111. [Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)** Level: Easy Tags: [Array, Bit Manipulation, Math] + + +给一串unique数字, 数字取自 [0 ~ n], 无序, 找第一个skipped的数字. + +#### Swap +- 跟First Missing Positive 非常像, 只有一行代码的区别. +- swap 所有的数字, 到自己的correct position +- 最后一个for loop找到错位的index, 也就是缺的数字. + +#### Bit Manipulation +- XOR will only retain bits that are different 1 ^ 0 = 1, but 0^0, 1^1 == 0 +- Use that feature, 把所有value都和index XOR了 +- 剩下的多余的数字, 其实是那个index无法被XOR消掉, 也就是那个缺的number value. +- 注意: 题目告诉数字是 [0 ~ n], 然而缺一个数字, 那么在[0 ~ n - 1] 里面, 最大的数字(不管缺没缺), 一定是 n = nums.length. + +#### HastSet +- 全存, 找missing +- O(n) space, 不合题意 + +#### sorting +- sort, 找1st missing +- O(n log n) 太慢, 不合题意 + + + +--- + +**112. [Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- Two pointers 其实也可以是一个 for loop pointer, 另一个 dynamic variable. +- track unique index +- skip duplicated items +- O(n) + +#### 思考模式: +- Remove Duplicate from Array 不同于remove from linked list. +- LinkedList里面我们是最好不要动node.val的,直接把node去掉。 +- 而array我们很难直接把node去掉,又不能用新array,那么就要: +- 把不重复的element一个个放到最前面。 +- 这个思想跟merge two sorted array (其中一个后续非常长的array可以放下arr1,arr2) 类似。 +- 就是找个不会事后mess up,不会去动得index,把满足条件的element 填进去。这样保证了in place. +- *反向思维*:remove duplicate, 实际上也是找unique elements, and insert into original array + + + +--- + +**113. [Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List.java)** Level: Easy Tags: [Linked List] + + +从Linked list 里面摘掉重复元素, 只留下unique元素. + +#### Linked List +- sorted list, 重复元素都在一起 +- 知道如何构建Linked List. +- 一点遇到重复元素: node.val == node.next.val, 就去掉. +- 用一个dummy node 来跑路 +- 注意: +- 只有当没有重复的时候, 才node = node.next; +- 有重复的时候, 当后面第三个元素被提上来之后, 还是可能跟当下元素重复, 所以不能前移node. +- ex: A -> A -> A +- while loop 里面check node 和 node.next 比较好, 这样ending position会非常清晰 + + + +--- + +**114. [Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序, 排序以后才能逐个看是否partial string已经存在 +- 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 +- 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: 1. 长 string 排在前; 2. 相等length, 按照dictionary order 排序 +- 全部放入Trie. Trie.insert() +- 针对sorted words array, 从最长的开始找 Trie.startWith. +- 一旦找到, 就是符合题意的, 直接return. +- 注意: startWith 必须每一个node都是 isEnd, 才满足'逐个字母拼出' 的条件. +- Time: build Trie O(mn) + sort:O(nlogn) => O(nlogn) +- Space: O(mn) + +#### +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**115. [Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum.java)** Level: Easy Tags: [DFS, Tree] + + +给一个inputSum, 然后dfs, 找到是否有一条path, 得出的path sum 跟 inputSum 一样. + +#### DFS +- 确定好结尾的条件: is leaf && val == sum +- 每一层减掉node.val, 然后dfs. +- 写一写: root == null => false 对parent nodes的影响. 这里发现没影响, 所以可以简化成用1个functionDFS. + + + + +--- + +**116. [Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)** Level: Easy Tags: [Backtracking, DFS, Tree] + + +给一个inputSum, 然后dfs, 找到所有path, 满足: path sum 跟 inputSum 一样. + +#### DFS, Backtracking +- 用remaining sum 来检测是否满足 input path sum 条件 +- 满足的时候add to result list +- 两种backtracking: +- 1. backtrack 当下node, 加进list, 然后dfs. dfs结束后删掉之前加进去的元素. 非常clean. +- 2. backtrack 下一个dfs level增加的value. dfs return 之后, 删掉list里面的末尾元素: 但是删掉的dfs余下的value. +- 第一种backtrack更加好掌握. + +#### Previous Notes: +- Binary Tree的一个基本题: 找到所有满足条件的path +- 遍历到底,比较sum vs. target +- 注意divide的情况。要把遍历的例子写写 + + + +--- + +**117. [Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)** Level: Easy Tags: [DFS, Double Recursive, Tree] + + +count所有存在的 path sum == target sum. 可以从任意点开始. 但是只能parent -> child . + +#### DFS +- 对所给的input sum 做减法, 知道 sum 达到一个目标值截止 +- 因为可以从任意点开始, 所以当sum达标时候, 需要继续recursive, 从而找到所有情况 (有正负数, sum可能继续增加/减少) +- 经典的 helper dfs recursive + self recursive +- 1. helper dfs recursive 处理包括root的情况 +- 2. self recursive 来引领 skip root的情况. + +#### 特点 +- 与 `Binary Tree Longest Consecutive Sequence II` 在recursive的做法上很相似: +- 利用dfs做包括root的recursive computation +- 利用这个function自己, 做`不包括root的recursive computation` + + + +--- + +**118. [Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20String.java)** Level: Easy Tags: [String] + + +给两个String, 看A rotate之后 能不能变成B + +#### LeetCode +- Basics +- StringBuffer.deleteCharAt(xx), StringBuffer.append(xx) +- O(n) + + +#### LintCode +- Different problem: 给一个char[], 要rotate offset times. +- *三步rotate* +- 有个坑:offset可能很长,那么要%length,才能得到真正需要rotate的部分。 +- Note: rotate 一个 full length之后,是string 不变 + + + +--- + +**119. [Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Prefix.java)** Level: Easy Tags: [String] + + +找一串String里面最长的公共prefix. + +#### Sort, compare string +- Sort O(nlogn) +- first and last string should share common prefix +- 这里假设题目要求的是所有string的公共 prefix, 而不是部分strings + +#### Brutle +- Nested loop, 每一次比较所有string 同位是否相等 +- 相等,append string. 不等,return. +- O(mn) + + + +--- + +**120. [Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20III.java)** Level: Easy Tags: [String] + + +给一个String, 里面的Word被single space split开来, 目的是reverse里面所有的Word, 但preserve Word 和 space order. + +#### Reverse function +- Reverse Words in a String II 的降级版, 去掉第一个overall reverse就好了 + + + +--- + +**121. [Merge Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array%20II.java)** Level: Easy Tags: [Array] + + +如题, merge two sorted array into 新的 sorted array + +- 长度已经固定. Basic Implementation +- 如果一个array足够大, merge into this array, 那么就是从末尾merge. + + + +--- + +**122. [Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/Nth%20to%20Last%20Node%20in%20List.java)** Level: Easy Tags: [Linked List] + + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth + + + +--- + +**123. [Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum.java)** Level: Easy Tags: [Array, Hash Table] + + +#### HashMap +- 相对暴力简洁: 找到一个value, 存一个index +- 若在HashMap里面 match 到结果, 就return HashMap里存的index. +- O(n) space && time. + +#### Sort array, two pointer +- 前后++, --搜索. Sort 用时O(nlogn). +- 1. 第一步 two pointer 找 value. +- 2. 注意,要利用额外的空间保留original array, 用来时候找index. (此处不能用HashMap,因为以value 为key,但value可能重复) +- O(n) space, O(nlogn) time. + + + + +--- + +**124. [Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)** Level: Easy Tags: [Array, DFS] + + +#### DFS +- 虽然Easy, 但用到DFS最基本的想法. +- 1. dive deep +- 2. mark VISITED +- 3. sum it up +- Time: worst O(mn), traverse all possible nodes + +- 更要注意, 要从符合条件value==1的地方开始dfs. +- 对于什么island都没有的情况,area应该位0, 而不是Integer.MIN_VALUE, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**125. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**126. [Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- 就是pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**127. [Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Words.java)** Level: Easy Tags: [Hash Table, String] + + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**128. [Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Characters.java)** Level: Easy Tags: [Array, String] + + +determine if characters are unique in string + +#### HashSet +- space O(n), time O(n) + +#### char[] +- space O(n), time O(nlogn) + +#### no additional data structure +- double for loop: O(n^2) + + + + +--- + +**129. [Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)** Level: Easy Tags: [Bit Manipulation] + +time: O(n), n = # of bits +space: O(1) + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + + + +--- + +**130. [Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array] + +time: O(n) +space: O(1) + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, track start/end +- start/end point, 然后比较大小记录dist +- 注意1: 如果第一个座位没有人, 特殊处理, dist = [0 ~ end] +- 注意2: 如果最后一个座位没有人, 特殊处理: dist = [n - 1 - start]; +- 其余: `dist = Math.max(dist, (end - start) / 2)` +- 相关题目: 几乎同样概念 `Binary Gap`, 升级复杂版`Exam Room` + + + + +--- + +**131. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) + +#### DP +- 最多2个fence 颜色相同 +- 假设i是和 i-1不同,那么结果就是 (k-1)*dp[i - 1] +- 假设i是何 i-1相同,那么根据条件,i-1和i-2肯定不同。那么所有的结果就是(k-1)*dp[i-2] +- dp[i]: count # of ways to paint 前i个 fence +- 加法原理 +- time, space: O(n) +- rolling array: space O(1) + +#### Previous Notes +- 这题目很有意思. 一开始分析的太复杂, 最后按照这个哥们的想法(http://yuanhsh.iteye.com/blog/2219891) 的来做,反而简单了许多。 +- 设定T(n)的做法,最后题目化简以后就跟Fibonacci number一样一样的。详细分析如下。 +- 做完,还是觉得如有神。本来是个Easy题,想不到,就是搞不出。 + + + + +--- + +**132. [Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### 理解意思是关键 +- 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出 +- 记录每天最小值Min是多少. O(n) +- 每天都算和当下的Min买卖,profit最大多少. + +#### DP +- Find min value for first i items, new dp[n + 1]. +- dp[i]: 前i天, prices最小的price是多少: min cost of first i days +- 然后用当天的price做减法dp[i]算max profit. +- Time, Space: O(n) +- 更进一步, 用一个min来表示min[i], 因为计算中只需要当下的min. + +#### Rolling array +- index i only depend on [i - 2] +- Space O(n) + +#### Brutle Failed +- 每天都试着买进,然后之后的每一天尝试卖出. double for loop, O(n^2). timeout. +- 其中很多都是没必要的计算:[7, 1, 5, 3, 6, 4]。 if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. + + + +--- + +**133. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**134. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**135. [Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +#### Tree +- Traverse tree: left, right +- Concept of partial compare vs. whole compare + + + +--- + +**136. [Two Sum IV - Input is a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20IV%20-%20Input%20is%20a%20BST.java)** Level: Easy Tags: [Tree] + + +HashSet to store visited items. Same old 2 sum trick. + + + +--- + +**137. [Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4.java)** Level: Easy Tags: [Enumeration, String] + + +Read4 题目. 理解题目: 是有个input object buff, 会被populated with data. + +#### String in char[] format +- 理解题目: 其实就是track `可以read多少bytes by read4() response` +- 另外一个有用的function `System.arraycopy(src, srcIndex, dest, destIndex, length)` + + + +--- + +**138. [Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素。 +- 注意,从尾部,是大数字优先排末尾的. + + + +--- + +**139. [Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome%20II.java)** Level: Easy Tags: [String] + + +#### Palindrome String +- delete an index = jump over the index +- 注意 boolean chance 可以用一个helper function + + + +--- + +**140. [Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison + + + +--- + +**141. [Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Move%20Zeroes.java)** Level: Easy Tags: [Array, Two Pointers] + + +Move non-zero elements to front of array; preseve order. + +#### Two Pointers +- Outside pointer that moves in certain condition. +- Save appropirate elements + + + +--- + +**142. [Flood Fill.java](https://github.com/awangdev/LintCode/blob/master/Java/Flood%20Fill.java)** Level: Easy Tags: [DFS] + + +Same as MS Paint + +#### DFS +- track `boolean[][] visited`, validate before dfs + + + +--- + +**143. [Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Diameter%20of%20Binary%20Tree.java)** Level: Easy Tags: [Tree] + + +找longest path (include or not include root) + +跟Binary Tree Maximum Path Sum 的想法一样: 处理single path, 或者combined path (do not include curr root) + +#### Singlepath, combined path +- `int[]{combinedPath, singlePath}`; +- pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; +- complete left/right child, or join curr root: `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`; + + + +--- + +**144. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + + +--- + +**145. [Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Roman%20to%20Integer.java)** Level: Easy Tags: [Math, String] + + +#### String +- 熟悉罗马字母规则 +- 1. 'I V X L C D M' 分别代表的数字 +- 2. 列举combo的情况,需要从原sum里面减掉多加的部分: 'IV, IX'减2, 'XL, XC'减20, 'CD, CM'减200. +- Leading `I(1*2)`, `X(10*2)`, `C(100*2)` causes double counting + +https://en.wikipedia.org/wiki/Roman_numerals + + + +--- + +**146. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**147. [Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math] + + +根据题意枚举, 再根据规则basic implementation + +#### Alter input + +#### HashTable + Two Pointer + + + +--- + +**148. [Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +剥皮过程。解铃还须系铃人 +左边的外皮'{['在stack底部 +右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**149. [First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +方法1: 按照题意, 找到第一个 first index == last index的字母. + +方法2: 用hashmap存字母的index, 有些重复字母的index就会是个list. 找到单一index, 结合成list, sort, return list.get(0) + + + +--- + +**150. [Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)** Level: Easy Tags: [Math, String, Two Pointers] + + +#### Two pointers +- Use two pointers i, j to track the 2 strings +- Add when i and j are applicable. While (i >= 0 || j >= 0) +- StringBuffer.insert(0, x); +- handle carry + +#### wrong: convert to int +- 土办法没技术,把binary换成数字,加起来,再换成binary +- 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**151. [Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Isomorphic%20Strings.java)** Level: Easy Tags: [Hash Table] + + +#### HashMap +- two failture cases: +- same key, value not matching +- two key maps to same value + +#### Previous note +1. Match. 就是map.containsKey, map.containsValue, and char1 == char2. Perfect. +2. Either Key not exist, or Value not exit. False; +3. Both key and Value exist, but map.get(char1) != char2. Miss-match. False. +4. None of Key or Value exist in HashMap. Then add the match. + + + +--- + +**152. [Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack? + + + +--- + + + + + + + +## Medium (247) +**0. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**1. [Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)** Level: Medium Tags: [Hash Table, Math] + + +TODO: no need of hashMap, just use set to store the existing + +不难想到处理除法:考虑正负,考虑小数点前后。主要是小数点以后的要着重考虑。 +很容易忽略的是integer的益处。 + + +--- + +**2. [Gray Code.java](https://github.com/awangdev/LintCode/blob/master/Java/Gray%20Code.java)** Level: Medium Tags: [Backtracking] + + +TODO: +1. backtracking, using set to perform contains() +2. BFS: use queue to keep the mutations + +题目蛋疼,目前只接受一种结果。 + +BackTracking + DFS: + Recursive helper里每次flip一个 自己/左边/右边. Flip过后还要恢复原样.遍历所有. + +曾用法(未仔细验证): +基本想法就是从一个点开始往一个方向走,每次flip一个bit, 碰壁的时候就回头走。 + + + +--- + +**3. [Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)** Level: Medium Tags: [Enumeration, Greedy] + + +#### Array +- 分三份:a b c考虑 +- 若a: countA++; 或b: countB++ +- 或c:countA--, countB-- +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 +- 最后出现的两个count>0的a和b,自然是potentially大于1/3的。其中有一个大于1/3. +- 比较countA和countB哪个大,就return哪一个。 + + + +--- + +**4. [Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)** Level: Medium Tags: [Hash Table, Linked List] + + +TODO: +1. hash table solution not passing +2. Find O(n) solution + +#### Hash Table +- 与其他Majority Number一样。 +- 出现次数多余1/k,就要分成k份count occurance.用HashMap。 存在的+1;不存在map里的,分情况: +- 若map.size() == k,说明candidate都满了,要在map里把所有现存的都-1; +- 若map.size() < k, 说明该加新candidate,那么map.put(xxx, 1); +- 最后在HashMap里找出所留下的occurance最大的那个数。 +- 但这样的worst case是 O(nk) + + + +--- + +**5. [Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)** Level: Medium Tags: [BFS, Graph] + + +#### Graph + BFS +- Build graph `map` +- BFS to find the shortest path: when the neibhbor has the curr node as the only one neighbor, it is leaf. +- record shortest path in Map> as result +- TODO: code it up. + +#### Previous Solution +- removing leaf && edge + + + +--- + +**6. [Missing Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Ranges.java)** Level: Medium Tags: [Array] + + +#### Basic Implementation +- O(n) +- 两个pointer, 每次计较prev和curr之间的部分. +- 然后prev = curr,向前移动一格 +- TODO: check the edge case and make sure max/min of int are checked + + + +--- + +**7. [Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Permutation.java)** Level: Medium Tags: [Array] + + +需斟酌: why reverse is need? why we are looking for k? + +Permutation的规律: +1. 从小的数字开始变化因为都是从小的数字开始recursive遍历。 +2. 正因为1的规律,所以找大的断点数字要从末尾开始: 确保swap过后的permutation依然是 前缀固定时 当下最小的。 + +steps: +1. 找到最后一个上升点,k +2. 从后往前,找到第一个比k大的点, bigIndex +3. swap k && bigIndex +4. 最后反转 (k+1,end) + + + + +--- + +**8. [Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)** Level: Medium Tags: [Backtracking, Permutation] + + +TODO: need to review permutation + +permutation的综合题: +1. validate Input 是不是可以做palindromic permutation. 这个就是(Palindrome Permutation I) +2. 顺便存一下permutation string的前半部分和中间的single character(if any) +3. DFS 做unique permutation: given input有duplicate characters。 + + + +--- + +**9. [Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)** Level: Medium Tags: [Backtracking, Math] + + +TODO: what about regular DFS/backtracking to compute the kth? dfs(rst, list, candiate list, k) + +k是permutation的一个数位。而permutation是有规律的。 + +也就是说,可以根据k的大小来判断每一个数位的字符(从最大数位开始,因为默认factorio从最大数位开始变化)。 + +于是先求出n!, 然后 k/n!就可以推算出当下这一个数位的字符。然后分别把factorio 和 k减小。 + +另外, 用一个boolean[] visited来确保每个数字只出现一次。 + +这个方法比计算出每个permutation要efficient许多。 + + + + +--- + +**10. [Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array] + + + + + +--- + +**11. [Remove Duplicates from Unsorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Unsorted%20List.java)** Level: Medium Tags: [Linked List] + + +基本方法: O(n) sapce, time +遍历。 +遇到duplicate(可能多个), while直到node.next不是duplicate. +接下去,既然不是duplicate,那就add 进 set + + +如果不用extra memory, do it in place: +那就要sort linked list. 用merge sort. + +复习merge sort: +1. find middle. +2. recursively: right = sort(mid.next); left = sort(head). +3. within sort(), at the end call merge(left, right) + + +--- + +**12. [Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)** Level: Medium Tags: [Array, Enumeration] + + +#### 找公式规律 +- 找到个转角度的规律公式: r = c; c = (w - r) +- 用temp variable, swap in place. + + + +--- + +**13. [Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +Allow duplicates之后: +因为最终binary search的结果也是O(n) +所以这道题要记得: 既然是O(n), 那来个简单的for loop 也就好了。 + +当然,要跟面试官提起来原因。别一上来就只有for。。。 + + +--- + +**14. [Single Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20II.java)** Level: Medium Tags: [Bit Manipulation] + + +一串数字里面, 所有数字都重复三次, 除了一个数字. 找到这个数字, linear time, without extrace space (constant space) + +TODO: bits + + + +--- + +**15. [Single Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20III.java)** Level: Medium Tags: [Bit Manipulation] + + +TODO: wut? + + +--- + +**16. [Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)** Level: Medium Tags: [String] + + + + +--- + +**17. [Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)** Level: Medium Tags: [DP] + + +这个DP有点诡异. 需要斟酌。 +NOT DONE YET + + +--- + +**18. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + + +--- + +**19. [Total Occurrence of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Occurrence%20of%20Target.java)** Level: Medium Tags: [] + +想法很简单。写起来有点长。 +找total number of occurance. 首先找first occurance, 再找last occurance. + + + +--- + +**20. [Two Lists Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Lists%20Sum.java)** Level: Medium Tags: [Linked List] + + +给两个Linked list, sum up and 合成新的list + + + +--- + +**21. [Zigzag Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Zigzag%20Iterator.java)** Level: Medium Tags: [BST] + + +这个题目相对简单. 做的时候我先考虑起来k条怎么办. 那么用个map把index和每个listmark一下就好了。 +每次next(), 相应的list的头拿下来就好。 +然后就跑圈呗,每次刷一个list头。不难。只要把几个variable维护清楚就行。 + + +--- + +**22. [Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)** Level: Medium Tags: [Hash Table, Math] + + +其实想到了切入点, 是个可难可简单的题目. 这里的encode就是想办法把url存起来, 然后给个 key. +那么具体怎么做这个key, 简单就可以用一个map, 然后counting. 复杂一点就可以做random letter/number组成key. + + + +--- + +**23. [Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)** Level: Medium Tags: [Array, Sort] + + +方法1: +排序, nLog(n). 然后把直线上坡变成层叠山峰, 需要每隔几个(题目中是每隔2位)就做个swap 造成高低不平. +Note: 每隔山峰之间是相互没有关系的, 所以每次只要操心 [i], [i-1]两个位置就好了. + +方法2: +O(n) +看好奇数偶数位的规律, 然后根据题目给出的规律, 跑一遍, 每次只关注两个位置: 把不合适的[i], [i-1]调换位置就好了. + +方法3: +跟法2一样, 只是更巧妙一点罢了: +第一遍想太多. 其实做一个fall-through就能把问题解决,原因是因为: +这样的fall-through每次在乎两个element,可以一口气搞定,无关乎再之前的elements。 +特别的一点:flag来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**24. [Queue Reconstruction by Height.java](https://github.com/awangdev/LintCode/blob/master/Java/Queue%20Reconstruction%20by%20Height.java)** Level: Medium Tags: [Greedy] + + +别无他法, 只能写一遍例子, 找规律,然后greedy.  +需要写一遍发现的规律比如: 从h大的开始排列, 先放入k小的. 写comparator的时候要注意正确性. +如果要sort, 并且灵活insert:用arrayList. 自己做一个object. +最后做那个'matchCount'的地方要思路清晰, 找到最正确的spot, 然后greedy insert. + +O(n) space, O(nLog(n)) time, because of sorting. + +可能有简化的余地, 代码有点太长. +比如试一试不用额外空间? + + + +--- + +**25. [Two Sum II - Input array is sorted.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20II%20-%20Input%20array%20is%20sorted.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +升序array, 找2SUM. + +#### Two pointers +- 排序好的array. Two pointer移动start和end,核查sum. +- 注意sum用long. +- O(n) time + +#### Binary Search, 因为已经排好序了啊 +- 定住一个valueA, 然后在剩下的里面 binary serach 找 (target - valueB) +- for loop O(n), binary search O(logn) +- overall time: O(nLogN), 就不写了 + + + +--- + +**26. [2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +与 2sum II - input array is sorted类似. 都是sort array, 然后two pointer. + +LintCode的题. 注意找的是greater/bigger than target。 + +由于给定条件允许O(nLogn): + sort + two pointer + +while里面two pointer移动。每次如果num[left]+num[right] > target,那么其中所有num[left++]的加上num[right]都>target. +也就是,num[right]不动,计算加入挪动left能有多少组,那就是: right-left这么多。 全部加到count上去。 +然后right--.换个right去和前面的left部分作比较。 + + + +--- + +**27. [Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + +#### DP +- 找对方程dp[x], 积累到amount x最少用多少个coin: #coin是value, index是 [0~x]. +- 子问题的关系是: 如果用了一个coin, 那么就应该是f[x - coinValue]那个位置的#coins + 1 + +##### initialization +- 处理边界, 一开始0index的时候, 用value0. +- 中间利用Integer.MAX_VALUE来作比较, initialize dp[x] +- 注意, 一旦 Integer.MAX_VALUE + 1 就会变成负数. 这种情况会在coin=0的时候发生. + +##### Optimization +- 方法1: 直接用Integer.MAX_VALUE +- 方法2: 用-1, 稍微简洁一点, 每次比较dp[i]和 dp[i - coin] + 1, 然后save. 不必要做多次min比较. + +#### Memoization +- dp[i] 依然表示: min # of coints to make amount i +- initialize dp[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录dp[amount] 如果已经给过value, 不要重复计算, 直接return. +- 但是这道题没必要强行做memoization, 普通DP的状态和方程相对来说很好找到 + + + +--- + +**28. [Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### DP +- 求最值, 想到DP. Time/Space O (n) +- 两个特别处: +- 1. 正负数情况, 需要用两个DP array. +- 2. continuous prodct 这个条件决定了在Math.min, Math.max的时候, +- 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. +- 这也就注定了需要一个global variable 来hold result. + +#### Space optimization, rolling array +- maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins +- Time: O(n), space: O(1) + + + +--- + +**29. [3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/3%20Sum%20Closest.java)** Level: Medium Tags: [Array, Two Pointers] + + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**30. [Triangle Count.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangle%20Count.java)** Level: Medium Tags: [Array] + + +其实也就是3sum的变形, 或者而说2sum的变形. 主要用2 pointers来做. +注意, 在选index时候每次定好一个 [0 ~ i], 在这里面找点start, end, 然后i 来组成triangle. +Note巧妙点: +在此之中, 如果一种start/end/i 符合, 那么从这个[start~end]中, 大于start的都可以, 所以我们count+= end-start. +反而言之, nums[i] + + + +--- + +**31. [3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum.java)** Level: Medium Tags: [Array, Two Pointers] + + + +#### sort array, for loop + two pointer. O(n^2) +- 处理duplicate wthin triplets: +- 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. +- 如果是triplet内有重复, 用完start point, 移动到结尾. + +Previous notes: +注意: + 1. 找 value triplets, 多个结果。注意,并非找index。 + 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 + +步骤: + 1. For loop 挑个数字A. + 2. 2Sum 出一堆2个数字的结果 + 3. Cross match 步骤1里面的A. + +时间 O(n^2), 两个nested loop + +另外, 还是可以用HashMap来做2Sum。稍微短点。还是要注意handle duplicates. + + + + +--- + +**32. [Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DP, Tree] + + +Not quite clear. +根据左右分割而总结出了原理, 每次分割, 左右两边都会有一定数量的permutation, 总体上的情况数量当然是相乘. +然后每一个不同的分割点都加一遍: +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +然后把数学公式转换成DP的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**33. [Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +跟unique path的grid一样, 目标走到右下角, 但是grid里面可能有obstacle, 不能跨越. 求unique path 的count. + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- 考虑最终结尾需要的状态:如何组成,写出公式. +- 公式中注意处理能跳掉的block, marked as 1. '到不了', 即为 0 path in dp[i][j]. + + + +--- + +**34. [Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Corrdinate DP +- Space, Time: O(MN) +- dp[i][j] 就是(i, j)上最多能炸掉的enemy数量 +- dp[i][j] 需要从4个方向加起来, 也就是4个方向都要走一遍, 所以分割成 UP/Down/Left/Right 4个 int[][] +- 最后一步的时候求max +- 分方向考虑的方法很容易想到,但是四个方向移动的代码比较繁琐. +- 往上炸: 要从顶向下考虑 +- 往下炸: 要从下向上考虑 +- 熟练写2D array index 的变换. + +似乎还有一个更简洁的方法, 用col count array: http://www.cnblogs.com/grandyang/p/5599289.html + + + +--- + +**35. [3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum%20Smaller.java)** Level: Medium Tags: [Array, Two Pointers] + + +一般的O(n3)肯定不行。在此基础上优化。 +发现j,k满足条件时候,(k - j)就是所有 sum target, 又因为j不能后退,只能k--,那么问题就被锁定了. 这样可以做到O(n2) + + + +--- + +**36. [Update Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Update%20Bits.java)** Level: Medium Tags: [Bit Manipulation] + + +熟悉bits的一些trick: +- ~0 = -1 = 111111...11111111 (32-bit) +- Create mask by shifting right >>>, and shifting left +- Reverse to get 0000...11110000 format mask +- & 0000 = clean up; | ABC = assign ABC + + + +--- + +**37. [Maximum XOR of Two Numbers in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20XOR%20of%20Two%20Numbers%20in%20an%20Array.java)** Level: Medium Tags: [Bit Manipulation, Trie] + + +比较难想到. 利用到XOR性质A^B=C, then A=B^C. +1. 枚举可能的A, 2. 然后一个个猜. + +1. 枚举A: 因为求MAX肯定是找leading-1最多的数字, 那么枚举A从(1000000...000)开始, +每次多一位取1或者0 +2. 因为枚举A的时候是按照每个bit来, 那么B和C也要以同样数位出现. +这里吧B和C变成了prefix的形式, 放在了set里面. +跟2sum用hashmap的思想类似, 每次用枚举的 A^B=C, 看看结果C是否已经在set里面. +如果在, 证明枚举的A可能被B^C得出, 那么就找到了一种情况. + +还用到一些技巧: +mask = (1 << i); // i位mask +mask = mask | (1 << i); // prefix mask + + + +--- + +**38. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**39. [Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)** Level: Medium Tags: [Backpack DP, DP] + + +给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法. + +nums 里的数字, 可以重复使用. 不同的order可以算作不同的拼法. + +#### Backpack DP +- dp[i] 表示: # of ways to fill weight i +- 1维: dp[w]: fill weigth w 有多少种方法. 前面有多少种可能性, 就sum多少个: +- dp[w] = sum{dp[w - nums[i]]}, i = 0~n + +##### 分析 +- 拼背包时, 可以有重复item, 所以考虑'最后被放入的哪个unique item' 就没有意义了. +- 背包问题, 永远和weight分不开关系. +- 这里很像coin chagne: 考虑最后被放入的东西的value/weigth, 而不考虑是哪个. + + + + + + +--- + +**40. [Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)** Level: Medium Tags: [BST, Design, Stack, Tree] + + +画一下, BST in order traversal. 用stack记录最小值, 放在top. O(h) space. +每次消耗TreeNode, 都看看rightNode(其实就是下一个最小的candidate), 并且一条龙stack叠上rightNode所有的left子孙. + +Previous Notes: +用O(h)空间的做法: + +理解binary search tree inorder traversal的规律: + 先找left.left.left ....left 到底,这里是加进stack. + 然后考虑parent,然后再right. + +例如这题: + stack里面top,也就是tree最左下角的node先考虑,取名rst. + 其实这个rst拿出来以后, 它也同时是最底层left null的parent,算考虑过了最底层的parent。 + 最后就考虑最底层的parent.right, 也就是rst.right. + +注意: + next()其实有个while loop, 很可能是O(h).题目要求average O(1),所以也是okay的. + + +用O(1)空间的做法:不存stack, 时刻update current为最小值。 + +找下一个最小值,如果current有right child: + 和用stack时的iteration类似,那么再找一遍current.right的left-most child,就是最小值了。 + +如果current没有right child: + 那么就要找current node的右上parent, search in BinarySearchTree from root. + +注意: + 一定要确保找到的parent满足parent.left == current. + 反而言之,如果current是parent的 right child, 那么下一轮就会重新process parent。 + 但是有错:binary search tree里面parent是小于right child的,也就是在之前一步肯定visit过,如此便会死循环。 + + + + +--- + +**41. [Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, Stack] + + +方法1: 用queue, 把需要的item全部打出来 +方法2: 用stack, 把需要的item先存一行, 每次打开子序列时候, 全部加回stack. + + + +--- + +**42. [Best Time to Buy and Sell Stock with Cooldown.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Cooldown.java)** Level: Medium Tags: [DP] + + +Sequence DP +跟StockIII很像. 分析好HaveStock && NoStock的状态, 然后看最后一步. + + + +--- + +**43. [Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)** Level: Medium Tags: [Array, Binary Search] + + +binary search. +Goal: find peak, where both sides are descending +最左边, 最右边是Integer.MIN_VALUE时候, 也能构成中间数mid是peak的条件. + +Note: +没有必要特别check (mid-1)<0或者(mid+1)>=n. +证明: +1. 最左端: 当start=0, end = 2 => mid = 1, mid-1 = 0; +2. 最右端: 当end = n - 1, start = n - 3; mid = (start+end)/2 = n - 2; +那么mid + 1 = n - 2 + 1 = n - 1 < n 是理所当然的 + + + +--- + +**44. [Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP] + + +给两个string, A, B. 找这两个string里面的LCS: 最长公共字符长度 (不需要是continuous substring) + +#### Double Sequence DP +- 设定dp长度为(n+1), 因为dp[i]要用来表示前i个(ith)时候的状态, 所以长度需要时i+1才可以在i位置, hold住i. +- 双序列: 两个sequence之间的关系, 都是从末尾字符看起, 分析2种情况: +- 1. A最后字符不在common sequence 或者 B最后字符不在common sequence. +- 2. A/B最后字符都在common sequence. 总体count + 1. + + + +--- + +**45. [Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)** Level: Medium Tags: [Backtracking, String] + + +方法1: Iterative with BFS using queue. + +方法2: Recursively adding chars per digit + + + +--- + +**46. [Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)** Level: Medium Tags: [Binary Search, Math] + + +傻做就O(n), 要更好就考虑O(logN). +减少重复计算, 一切两半. + +注意: +- n/2的奇数偶数 +- n的正负 +- n == 0的情况, x == 0, x == 1 的情况. + + +--- + +**47. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**48. [Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. +遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + +跟Add Binary的理解方式一模一样. + +注意: +Linked List 没有天然size. +用DummyNode(-1).next来hold住结果. + + + + +--- + +**49. [Add Two Numbers II.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers%20II.java)** Level: Medium Tags: [Linked List] + + +Singly-linked list需要reverse, 用stack. +最终结果要恢复成input list 那样的sequence方向, 用stack一个个pop()刚好就可以做到. + +加法都一样: + 1. sum = carry + 2. carry = sum / 10 + 3. sum = sum % 10; + + + +--- + +**50. [Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Balanced%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个binary tree, 看是否是height-balanced + +#### DFS +- DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. +- 一旦有 <0 或者差值大于1, 就全部返回Integer.MIN_VALUE. Integer.MIN_VALUE比较极端, 确保结果的正确性。 +- 最后比较返回结果是不是<0. 是<0,那就false. +- Traverse 整个tree, O(n) + + +#### DFS, maxDepth function +- Same concept as in 1, but cost more traversal efforts. + + + +--- + +**51. [Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +给一个特殊的binary tree, treeNode里面有一个 next pointer. + +写一个function, 把所有node都更同level的node 连在一起. 最右边的node.next = NULL + +#### DFS + Divide and Conquer +- 题目要求DFS. 想清楚了如何在DFS level把几种情况都考虑了, 写起来很简单. NOT BFS, because requires O(1) space +- 对于一个root来说, 只有几个点可以顾忌到: root.left, root.right, root.next. +- 想办法把这三个方向的点, 能连起来的都连起来: +- 1. `node.left.next = node.right` +- 2. If `node.next != null`, link `node.right.next = node.next.left`; +- 然后在dfs(root.left), dfs(root.right) +- Time: visit && connect all nodes, O(n) + +#### BFS +- 不和题意,用了queue space,与Input成正比。太大。 +- BFS over Tree。 用Queue 和 queue.size(),老规矩。 +- process每层queue时, 注意把next pointer加上去就好. + + + +--- + +**52. [Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +如题, 验证是否是BST. + +#### DFS +- 查看每个parent-child关系: leftchild < root < rightChild; +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest +- imagine we know the two extreme border: Integer.MIN_VALUE, Integer.MAX_VALUE; pass node around and compare node vs. node.parent. +- 方法: 把root.val 传下来作为 max 或者 min, 然后检查children +- + +##### Note: +- min/max需要时long type. +- 如果题目真的给node.val = Integer.MAX_VALUE, 我们需要能够与之比较, long就可以. + + + +--- + +**53. [Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List] + + +如题, 把一个sorted singly linked list 转换成一个 height balanced BST + +#### DFS +- Divide and Conquer +- 找到mid node +- 然后分割两半, 分别dfs做各自两个subtree: node.left,node.right +- 用长度来定位mid, 每次找中间点做root, 然后前半段, 后半段分别dfs with length. +- 用快慢pointer 找到mid. Better: 不用traverse entire linked list + +#### Details +- slowPointer = node; +- fastPointer = node.next; +- 然后把root = mid.next +- 然后开始sortedListToBST(mid.next.next); //后半段 +- mid.next = null;//非常重要,要把后面拍过序的断掉 +- sortedListToBST(head); //从头开始的前半段 +- 最后root.left, root.right merge一下。 + + + +--- + +**54. [Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### DFS +- 分析题意后, 按照题意: Flatten the tree, no extra space. +- 1. reserve right child: `reservedRightNode` +- 2. Connect `root.right = root.left`, DFS flatten(root.right) +- 3. 移花接木, coneect end of list -> reservedRightNode +- 4. flatten 剩下的. root.right... + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**55. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**56. [Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)** Level: Medium Tags: [Hash Table, String, Two Pointers] + + +方法1: +Two Pointers +双指针: 从start开始遍历, 但是第一步是while loop来推进end; 直到推不动end, 然后start++ +巧妙点: 因为end是外围variable, 在start的loop上, end不会重置;[star ~ end] 中间不需要重复计算. +最终可以O(n); + +Previous verison of two pointers: +用两个pointer, head和i. +注意:head很可能被退回到很早的地方,比如abbbbbba,当遇到第二个a,head竟然变成了 head = 0+1 = 1. +当然这是不对的,所以head要确保一直增长,不回溯 + +方法2: + HashMap: + 一旦有重复, rest map. + 没有重复时候, 不断map.put(), 然后求max值 + +问题: 每次reset map之后就开始从新从一个最早的index计算, 最坏情况是O(n^2): +'abcdef....xyza' + + + + +--- + +**57. [Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +O(n), one pace, no extra space +找到窗口, 然后平移, 最后pre 和 head之间 skip一个node就好. + + + +--- + +**58. [Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Linked List, Math, Two Pointers] + + +LinkedList 里面有 cycle, 找到cycle的起始点(第一个重复出现的element). + +#### Slow, fast Pointer +- 快慢指针, O(1)space. +- 1. 确认有cycle后 2. 数学问题:找到开头. +- 当head == slow.next时候, head就是cycle starting point. +- 也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +#### 证明 +- 1. 假设慢指针走t步, 快指针走快一倍, 也就是2t. +- 2. 我们假设cycle的长度是Y, 而进入cycle之前的长度为X. +- 3. 假设慢指针走了m圈cycle, 而快指针走了n圈cycle之后, 两个pointer相遇. +- 4. 最终在Y cycle里面的K点相遇, 也就是两个指针都在这最后一圈里面走了K 步. +- 那么: +- t = X + mY + K +- 2t = X + nY + K +- 整合公式: X + K = (n - 2m)Y +- 这里的m和n不过是整数的跑圈数, 也就是说X和K加在一起, 总归是结束cycle. X 和 K 互补 +- 结论: 当slow/fast 指针在K点相遇后, 再走X步, 就到了cycle的起点, 也就是题目要求的起点. + +#### Hash Table, O(n) space + + + + +--- + +**59. [Kth Smallest Element in a Sorted Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20Sorted%20Matrix.java)** Level: Medium Tags: [Binary Search, Heap] + +time: O(n + klogn) +space: O(n) + +给一个sorted matrix, 找kth smallest number (not distinct) + +Related: `Kth Largest Element in an Array` + +#### PriorityQueue +- 和Merge K sorted Array/ List 类似:使用PriorityQueue。 +- 因为Array的element无法直接找到next,所以用一个class Node 存value, x,y positions. +- Initial O(n) time, also find k O(k), sort O(logn) => O(n + klogn) +- 变型: Kth Largest in N Arrays + +#### Binary Search +- we know where the boundary is start/end are the min/max value. +- locate the kth smallest item (x, y) by cutt off partition in binary fasion: +- find mid-value, and count # of items < mid-value based on the ascending matrix +- O(nlogn) + + + + +--- + +**60. [Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +画图, 最小值被rotate之后, 变成array中间的最低谷. +并且, 左边山坡的最小值, 大于右边山坡的最大值. +以此来给binary search做判断. + +O(nlogn) + + + +--- + +**61. [Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)** Level: Medium Tags: [Union Find] + + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + + + +--- + +**62. [Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)** Level: Medium Tags: [Union Find] + + +Lint还不能跑, 全部按照题意和答案document的. + + + +--- + +**63. [Connecting Graph III.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20III.java)** Level: Medium Tags: [Union Find] + + +还是UnionFind的变形, 这次是算有剩下多少个union. 其实非常简单, 维持一个全局变量count: +一开始count=n, 因为全是散装elements; 每次union, count--. + + + +--- + +**64. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**65. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + +**66. [Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- HashMap构建Trie. Trie三个Method: +- 1. Inset: 加 word +- 2. Search: 找word +- 3. StartWith: 找prefix + +##### 特点 +- 只有两条children的是binary tree. 那么多个children就是Trie +- 那么没有left/right pointer怎么找孩子? +- 用HashMap,以child的label为Key,value就是child node。 HashMap走位 + +##### 其他 +- node里的char在这是optional. 只要在每个TrieNode里面用map存储向下分布的children就好了. +- 另外有种题目,比如是跟其他种类的search相关,在结尾要return whole string,就可以在node里存一个up-to-this-point的String。 + +##### Previous Note +- 如果是遇到一个一个字查询的题,可以考虑一下。 +- 构建TrieNode的时候要注意:如何找孩子?如果是个map的话,其实就挺好走位的。 +- 而且,每个node里面的 char 或者string有时候用处不大, +- 可以为空。但是有些题目,比如在结尾要return一些什么String,就可以在end string那边存一个真的String。 + + + + + +--- + +**67. [Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +Trie结构, prefix tree的变形: '.'可以代替任何字符,那么就要iterate这个node所有的children. + +节点里面有char, isEnd, HashMap +Build trie = Insert word:没node就加,有node就移动。 +Search word:没有node就报错. 到结尾return true + +这题因为'.'可以代替任何possible的字符,没一种都是一个新的path,所以recursive做比较好些。 +(iterative就要queue了,麻烦点) + + + +--- + +**68. [Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)** Level: Medium Tags: [Array, Backtracking, DFS] + + +#### DFS, Backtracking: +- 找到开头的字母, 然后recursively DFS 去把word串到底: +- 每到一个字母, 朝四个方向走, 之中一个true就可以. +- Note:每次到一个字母,mark一下'#'. 4个path recurse回来后,mark it back. + +#### Note: other ways of marking visited: +- 用一个boolean visited[][] +- Use hash map, key = x@y + + + + +--- + +**69. [Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)** Level: Medium Tags: [DFS, Divide and Conquer, Stack] + + +给一个expression string. 里面包括数字, 字母, 括号. 其中数字代表括号里面的内容重复几次. + +括号里面可以是String, 也可能是expression. + +目的: 把expression展开成一个正常的String. + + +#### Stack, Iteratively +- Process inner item first: last come, first serve, use stack. +- Record number globally and only use it when '[' is met. +- Stack存 [ ] 里面的内容, detect 括号开头结尾: 结尾时process inner string +- 有很多需要注意的细节才能做对: +- Stack 也可以用, 每个地方要注意 cast. 存进去的需要是Object: String, Integer +- 几个 type check: instanceof String, Character.isDigit(x), Integer.valueOf(int num) +- 出结果时候: `sb.insert(0, stack.pop())` + + +#### DFS +- Bottom->up: find deepest inner string first and expand from inside of `[ ]` +- 与Stack时需要考虑的一些function类似. 特别之处: **检查`[ ]`的结尾** +- 因为DFS时候, 括号里的substring会被保留着进入下一个level, 所以我们在base level要keep track of substring. +- 用int paren 来track 括号的开合, 当paren再次==0的时候 找到closure ']' +- 其他时候, 都要继续 append to substring + + + + +--- + +**70. [Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)** Level: Medium Tags: [Stack, Tree] + + +给一串数字, 做一个 maximum binary tree: 最顶上的root最大; 左child也是一个max tree, 右child也必须是max tree. + +#### Monotonous Stack +- 用到bottom->top递减的stack: 最底下的root维持成最大的element. +- 过程当中, 一旦遇到currNode.val > stack.peek(), 就意味着需要把这个currNode放在 stack的底层位置. +- 也就是说, 遇到这个条件, process, pop()所有 currNode.val > stack.peek(), 最后把currNode加进去. + +- maxTree题目本身的要求是: 大的在最中间, 左右两边的subTree也要是maxTree: +- Monotonous Stack在这里帮助 keep/track of max value, 但是left/right tree的logic是MaxTree独有的. +- left/right node的assignment是根据题目要求: 中间最大值分开后, 左边的是左边subTree, 右边的作为右边subTree. + +#### Previous notes +- Should memorize MaxTree. 依次类推,会做Min-Tree, Expression Tree +- Stack里,最大的值在下面。利用此性质,有这样几个step: + +##### Step1 +- 把所有小于curr node的,全Pop出来, while loop, keep it going. +- 最后pop出的这个小于Curr的node:它同时也是stack里面pop出来小于curr的最大的一个,最接近curr大小。(因为这个stack最大值靠下面) +- 把这个最大的小于curr的node放在curr.left. + +##### Step2 +- 那么,接下去stack里面的一定是大于curr: +- 那就变成curr的left parent. set stack.peek().right = curr. + +##### Step3 +- 结尾:stack底部一定是最大的那个,也就是max tree的头。 + + + + + +--- + +**71. [Swap Nodes in Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Nodes%20in%20Pairs.java)** Level: Medium Tags: [Linked List] + + +#### enumurate +基本原理, 写出来, 然后连线: +pre -> A -> B -> C -> ... +需要一个虚拟 preNode做起始node, 不然无法把后面的node换到开头. + +#### 注意 +用dummy = pre作为head前一格. +用 `pre.next == null && pre.next.next` 来check是否为NULL. +pre.next.next 保证了至少有一次swap. + + + +--- + +**72. [Wood Cut.java](https://github.com/awangdev/LintCode/blob/master/Java/Wood%20Cut.java)** Level: Medium Tags: [Binary Search] + + +二分的思想: 判断的是一个 validate() function, 而不是简单的'==' + +不需要sort! 为什么呢? 因为我们不是在given array上面二分, 我们是根据最大值在[0, max]上二分. + +Overall time: O(nLogM), where M = largest wood length + + + +--- + +**73. [Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +- 注意不要思维定式: 以为mid是index +- 这里mid其实是binary search on value [1, n] 的一个value. +- 再次用到validate() function + +Time: O(nLogN) + + + +--- + +**74. [Game of Life.java](https://github.com/awangdev/LintCode/blob/master/Java/Game%20of%20Life.java)** Level: Medium Tags: [Array] + + +#### basic +- 简单的implementation, 把count function写清楚就好. +- time: O(mn), extra space: O(mn) +- 注意结尾要一个个board[i][j]copy + +#### follow up +unlimited border? 可能需要分割board. 用大框分割, 每次换行的时候, 重复计算2行就好了. see details below. + +#### improvement: do it in place! +- time: O(mn), extra space: O(1) +- bit manipulation: 用第二个bit来存previous value. +- 因为我们只考虑 0 和 1 而已, 所以可以这样取巧. 但是并不scalable. +- 如果需要存multiple state, 可能需要移动更多位, 或者用一个 state map +- 注意 bit manipulation 的细节: <<1, >>1, 还有 mast的用法: |, & + + + + +--- + +**75. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**76. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**77. [Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +##### DP +- 计数DP.注意方程式前两位置加在一起: 前两种情况没有overlap, 也不会缺情况. +- 注意initialization, 归1. +- 需要initialize的原因是,也是一个reminder: 在方程中会出现-1index +- Of course, row i = 0, or col j = 0, there is only 1 way to access +- time O(mn), space O(mn) + +##### 滚动数组 +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + + + + +--- + +**78. [Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Square.java)** Level: Medium Tags: [Coordinate DP, DP] + + +只能往右边,下面走, 找面积最大的 square. 也就是找到变最长的 square. + +#### DP +- 正方形, 需要每条边都是一样长度. +- 以右下角为考虑点, 必须满足条件: left/up/diagonal的点都是1 +- 并且, 如果三个点分别都衍生向三个方向, 那么最长的 square 边就是他们之中的最短边 (受最短边限制) +- dp[i][j]: max square length when reached at (i, j), from the 3 possible directions +- dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; +- Space, time O(mn) + +##### init +每个点都可能是边长1, 如果 matrix[i][j] == '1' + +##### 滚动数组 +[i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + +--- + +**79. [Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)** Level: Medium Tags: [DP, Game Theory, Greedy] + + +拿棋子游戏, 每个人可以拿1个或者2个, 拿走最后一个子儿的输. 问: 根据给的棋子输, 是否能确定先手的输赢? + +Game Theory: 如果我要赢, 后手得到的局面一定要'有输的可能'. + +#### DP, Game Theory +- 要赢, 必须保证对手拿到棋盘时, 在所有他可走的情况中, '有可能败', 那就足够. +- 设计dp[i]:表示我面对i个coins的局面时是否能赢, 取决于我拿掉1个,或者2个时, 对手是不是会可能输? +- dp[i] = !dp[i - 1] || !dp[i-2] +- 时间: O(n), 空间O(n) +- 博弈问题, 常从'我的第一步'角度分析, 因为此时局面最简单. + +#### Rolling Array +空间优化O(1). Rolling array, %2 + + + +--- + +**80. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**81. [Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Recursive +trivial, 先加left recursively, 再加right recursively, 然后组成头部. + +#### Stack +- 双stack的思想, 需要在图纸上画一画 +- 原本需要的顺序是: 先leftChild, rightChild, currNode. +- 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild +- 这样出来的结果是reverse的, 那么翻转一下就可以了. +- v1做的时候用了stack1, stack2, 因为根据这个双stack的思想而来 +- v2简化, 可以放在一个stack里面, 每次record result 的时候: rst.add(0, item); + +##### 利用stack的特点 +- 每次加element进stack的时候, 想要在 bottom/后process的, 先加 +- 想要下一轮立刻process的, 最后push进stack. + +##### 注意 +这些binary tree traversal的题目.常常有多个做法:recursive or iterative + + + +--- + +**82. [Compare Version Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Version%20Numbers.java)** Level: Medium Tags: [String] + + +给两串version number, 由数字和'.' 组成. 比较先后顺序. + +If version1 > version2 return 1, if version1 < version2 return -1, otherwise return 0. + +#### divide and conquer +- 用 str.split("\\.") 分割string +- Convert成integer, 逐个击破 + +#### 注意 +- '1.0' 和 '0' 是相等的 +- 如果可以假设version integer都是valid, 直接Integer.parseInt()就可以了 +- 不然的话, 可以compare string + + + +--- + +**83. [Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样, 如果一样, 直接 2 ^ h - 1就好 +- 不一样的话, 再DFS + +##### Trick +- 直接DFS会timeout, O(n), 其实可以optimize +- to pass the test with O(h^2), 位运算: Math.pow(2, h) = 2 << (h - 1). 神奇! +- 2 << 1就是把所有bits往左移动一位, 也就是 * 2 + +#### Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + + + +--- + +**84. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**85. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**86. [Contains Duplicate III.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20III.java)** Level: Medium Tags: [BST] + + +给一个unsorted array, 问, 是否有两个element, value相差最大为t, 而两个element的index 相差最大为k. + +Note: 虽然题目名字是Contains Duplicate, 但其实要找的两个element不是duplicate, 而是Math.abs(value1 - value2) <= t. + +#### TreeSet +- TreeSet还是一个set, 我们用来装已经visit过得item +- 如果window大小超过K, 那么把nums[i - k - 1] 去掉, 并且加上新的element +- 这里有个公式推算: (Math.abs(A-B) <= t) =>>>>> (-t <= A - B <= t) =>>>>>> A >= B - t, A <= B + t +- 也就是说, 如果对于 B = nums[i], 来说, 能找到一个target A, 满足上面的公式, 那么就可以 return true. +- Time O(nLogk), treeSet的大小不会超过k, 而 treeSet.ceiling(), treeSet.add(), treeSet.remove() 都是 O(logK) +- Space O(k) + +#### Note +- 与Contains Duplicate II 类似概念. TreeSet有BST 因此可以直接用, 而不用自己构建BST +- 简化题目里面的重要条件 Math.abs(A-B) <= t 而推断出 A >= B - t, A <= B + t +- 并且需要需要用 TreeSet.ceiling(x): return number greater or equal to x. 这个用法要记住吧, 没别的捷径. + + + +--- + +**87. [Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)** Level: Medium Tags: [Array, DP, Greedy] + + +给出步数,看能不能jump to end. + +#### Greedy - start from index = 0 +- Keep track of farest can go +- 一旦 farest >= nums.length - 1, 也就是到了头, 就可以停止, return true. +- 一旦 farest <= i, 也就是说, 在i点上, 已经走过了步数, 不能再往前跳, 于是 return false +- This can be done using DP. However, greedy algorithm is fast in this particular problem. + +#### Greedy - start from index = n - 1 +- greedy: start from end, and mark last index +- loop from i = [n - 2 -> 0], where i + nums[i] should always >= last index +- check if last == 0 when returning. It means: can we jump from index=0 to the end? +- Time: O(n), beat 100% + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (A[j] >= i - j), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- Time: O(n^2) + + + + +--- + +**88. [Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change%202.java)** Level: Medium Tags: [Backpack DP, DP] + + +给串数字, target amount, 求总共多少种方式可以reach the amount. + +#### DP +- O(MN): M, total target amount; N: size of coins +- 类似于: 网格dp, unique path 里面的2种走法: 从上到下, 从左到右 +- 状态: dp[i]: sum of ways that coins can add up to i. +- Function: dp[j] += dp[j - coins[i]]; +- Init: dp[0] = 1 for ease of calculation; other dp[i] = 0 by default +- note: 避免重复count, 所以 j = coins[i] as start +- 注意 coins 需要放在for loop 外面, 主导换coin的流程, 每个coin可以用无数次, 所以在每一个sum value上都尝试用一次每个coin + +#### knapsack problem: backpack problem + + + +--- + +**89. [Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Partition DP +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- 确定末尾的2种状态: single letter or combos. 然后计算出单个letter的情况, 和双数的情况 +- 定义`dp[i] = 前i个digits最多有多少种decode的方法`. new dp[n + 1]. +- 加法原理: 把不同的情况, single-digit, double-digit 的情况加起来 +- dp[i] += dp[i - x], where x = 1, 2 +- note: calculate number from characters, need to - '0' to get the correct integer mapping. +- 注意: check value != '0', 因为'0' 不在条件之中(A-Z) +- Space, Time O(n) + +#### 引申 +- 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + + + +--- + +**90. [Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +#### DP +- Time, Space O(MN) +- 往右下角走, 计算最短的 path sum. 典型的坐标型. +- 注意: init 第一行的时候, 要accumulate dp[0][j - 1] + grid[i][j], 而不是单纯assign grid[i][j] + +#### Rolling Array +- Time O(MN), Space O(1) +- 需要在同一个for loop里面完成initialization, 和使用dp[i][j] +- 原因: dp[i % 2][j] 在被计算出来的时候, 是几乎马上在下一轮是要被用的; 被覆盖前不备用,就白算 +- 如果按照第一种方法, 在开始initialize dp, 看起来固然简单, 但是不方便空间优化 + + + +--- + +**91. [Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)** Level: Medium Tags: [Bit Manipulation, Bitwise DP, DP] + + +给一个数组, 算里面有多少bit 1. + +#### Bitwise DP +- 对于每一个数字, 其实很简单就能算出来: 每次 >>1, 然后 & 1 就可以count 1s. Time: 一个数字可以 >>1 O(logN) 次 +- 现在要对[0 ~ num] 都计算, 也就是N个数字, 时间复杂度: O(nLogN). +- 用DP来优化, 查找过的number的1s count, 存下来在 dp[number]里面. +- 计算你顺序从 0 -> num, count过的数字就可以重复利用. +- Bit题目 用num的数值本身表示DP的状态. +- 这里, dp[i] 并不是和 dp[i-1]有逻辑关系; 而是dp[i] 和dp[i>>1], 从binary representation看出有直接关系. + + + +--- + +**92. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**93. [House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)** Level: Medium Tags: [DP, Sequence DP, Status DP] + + +和House Robber I 类似, 搜刮房子, 相邻不能动. 特点是: 现在nums排成了圈, 首尾相连. + +#### Sequence DP +- dp[i][status]: 在 status=[0,1] 情况下, 前i个 房子拿到的 max rob gain. status=0, 1st house robbed; status=1, 1st house skipped +- 根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]); +- 特别的是,末尾的last house 和 first house相连. 这里就需要分别讨论两种情况: 第一个房子被搜刮, 或者第一个房子没被搜刮 +- be careful with edge case nums = [0], only with 1 element. +- Time,space: O(n) + +#### 两个状态 +- 是否搜刮了第一个房子, 分出两个branch, 可以看做两种状态. +- 可以考虑用两个DP array; 也可以加一dp维度, 补充这个状态. +- 连个维度表示的是2种状态(1st house being robbed or not); 这两种状态是平行世界的两种状态, 互不相关. + +#### Rolling array +- 与House Robber I一样, 可以用%2 来操作rolling array, space reduced to O(1) + + + +--- + +**94. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**95. [Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20in%20String.java)** Level: Medium Tags: [Two Pointers] + + +#### Two Pointer +- 如果做s1的permudation, 时间复杂度是O(n!) 肯定不可以 +- 这里用HashTable的做法 (因为26字母, 所以用int[26]简化) 来记录window内的 character count +- 如果window内的character count 相等, 那么就是permudation +- 更进一步优化: 找两个map相互对应, 不如用一个 int[26]: s1对遇到的character做加法, s2对遇到的character做减法 +- two pointer 运用在 n1, n2 的把控; 以及 s2.charAt(i - n1) 这一步 + + + +--- + +**96. [Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations%20II.java)** Level: Medium Tags: [Backtracking] + + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +#### Backtracking +- 排序, +- Mark visited. 通过permutation规律查看是否排出了重复结果 +- 并且要检查上一层recursive时有没有略过重复element +- time O(n!) + +##### 背景1 +- 在recursive call里面有for loop, 每次从i=0开始, 试着在当下list上加上nums里面的每一个。 +- 从i=0开始,所以会依次recursive每一个nums: +- 因此,例如i=2,肯定比i=3先被访问。也就是:取i=2的那个list permutation肯定先排出来。 + +##### 背景2 +- 重复的例子:给出Input[x, y1, y2], 假设y的值是一样的。那么,{x,y1,y2}和{x,y2,y1}是相同结果。 + +##### Note +- 综上,y1肯定比y2先被访问,{x,y1,y2}先出。 紧随其后,在另一个recursive循环里,{x,y2...}y2被先访问,跳过了y1。 +- 重点:规律在此,如果跳过y1,也就是visited[y1] == false, 而num[y2] == num[y1],那么这就是一个重复的结果,没必要做,越过。 +- 结果:那么,我们需要input像{x,y1,y2}这样数值放一起,那么必须排序。 + +#### Non-recursive, manuall swap +- Idea from: https://www.sigmainfy.com/blog/leetcode-permutations-i-and-ii.html +- 用到 sublist sort +- 用 swap function, 在原数组上调节出来新的permutation +- 注意: 每次拿到新的candidate, 都要把没有permutate的数位sort, 然后再开始swap. +- 这是为了确保, [j]和[j-1]在重复时候, 不用重新记录. + +#### Queue +- 给一个visited queue +- 和queue在所有的地方一同populate. +- 然后visited里面存得时visited indexes。 (Not efficient code. check again) + + + +--- + +**97. [Shuffle an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Shuffle%20an%20Array.java)** Level: Medium Tags: [Permutation] + + +像shuffle music 一样, 做一套shuffle array的functions: + +shuffle() 给出random的permutation + +reset() 给出最初的nums + +#### Permutation +- Permutation 实际上就是在list/array/... 上面给元素换位置 +- 硬换位置, 每次换的位置不同, 用random来找到要换的index +- 维持同一个random seed +- O(n) + +##### Note +- compute all permutations 太慢, 不可行. + + + +--- + +**98. [Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Anagrams.java)** Level: Medium Tags: [Hash Table, String] + + +给一串string, return list of list, 把anagram 放在一起. + +#### Hash Table, key 是 character frequency +- 存anagram +- 用 character frequency 来做unique key +- 用固定长度的char[26] arr 存每个字母的frequency; 然后再 new string(arr). +- 因为每个位子上的frequency的变化,就能构建一个unique的string +- O(nk), k = max word length + + +#### Hash Table, key 是 sorted string +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**99. [Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 背包有自己的大小M, 看最多能放多少重量的书? + +#### Backpack DP 1 +- 简单直白的思考 dp[i][m]: 前i本书, 背包大小为M的时候, 最多能装多种的书? +- **注意**: 背包问题, 重量weight一定要是一维. +- dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + A[i - 1]); +- 每一步都track 最大值 +- 最后return dp[n][m] +- 时间空间 O(mn) +- Rolling array, 空间O(m) + +#### Backpack DP 2 +- true/false求解, 稍微曲线救国: 重点是, 最后, 按照weight从大到小遍历, 第一个遇到true的, index就是最大值. +- 考虑: 用i个item (可跳过地取), 是否能装到weight w? +- 需要从'可能性'的角度考虑, 不要搞成单一的最大值问题. +- 1. 背包可装的物品大小和总承重有关. +- 2. 不要去找dp[i]前i个物品的最大总重, 找的不是这个. + dp[i]及时找到可放的最大sum, 但是i+1可能有更好的值, 把dp[i+1]变得更大更合适. + +##### 做法 +- boolean[][] dp[i][j]表示: 有前i个item, 用他们可否组成size为j的背包? true/false. +- (反过来考虑了,不是想是否超过size j, 而是考虑是否能拼出exact size == j) +- **注意**: 虽然dp里面一直存在i的位置, 实际上考虑的是在i位置的时候, 看前i-1个item. + +##### 多项式规律 +- 1. picked A[i-1]: 就是A[i-1]被用过, weight j 应该减去A[i-1]. 那么dp[i][j]就取决于dp[i-1][j-A[i-1]]的结果. +- 2. did not pick A[i-1]: 那就是说, 没用过A[i-1], 那么dp[i][j]就取决于上一行d[i-1][j] +- dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]] + +##### 结尾 +- 跑一遍dp 最下面一个row. 从末尾开始找, 最末尾的一个j (能让dp[i][j] == true)的, 就是最多能装的大小 :) +- 时间,空间都是:O(mn) + + + + +--- + +**100. [Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 每本书有value int[] V + +背包有自己的大小M, 看最多能放多少value的书? + +#### Backpack DP +- 做了Backpack I, 这个就如出一辙, 只不过: dp存的不是max weight, 而是 value的最大值. +- 想法还是,选了A[i - 1] 或者没选A[i - 1]时候不同的value值. +- 时间空间O(mn) +- Rolling Array, 空间O(m) + +#### Previous DP Solution +- 如果无法达到的w, 应该mark as impossible. 一种简单做法是mark as -1 in dp. +- 如果有负数value, 就不能这样, 而是要开一个can[i][w]数组, 也就是backpack I 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**101. [Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)** Level: Medium Tags: [Backpack DP, DP] + + +#### Backpack DP +- 与背包1不同: 这里不是check可能性(OR)或者最多能装的size是多少; 而是计算有多少种正好fill的可能性. +- dp[i][w]: 用前i本书, 正好fill到 w weight的可能性. +- 对于末尾, 还是两种情况: +- 1. i-1位置没有加bag +- 2. i-1位置加了bag +- 两种情况可以fill满w的情况加起来, 就是我们要的结果. +- 如常: dp[n + 1][w + 1] +- 重点: dp[0][0] 表示0本书装满weight=0的包, 这里我们必须 dp[0][0] = 1, 给后面的 dp function 做base +- Space, time: O(MN) +- Rolling array, 空间优化, 滚动数组. Space: O(M) + +#### 降维打击, 终极优化 +- 分析row(i-1)的规律, 发现所有row(i)的值, 都跟row(i-1)的左边element相关, 而右边element是没用的. +- 所以可以被override. +- Space: O(M), 真*一维啊! +- Time: O(MN) + + + +--- + +**102. [Evaluate Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Reverse%20Polish%20Notation.java)** Level: Medium Tags: [Stack] + + +给一个 RPN string list, 根据这个list, 计算结果. + +#### Stack +- stack 里面 存数字 +- 每次遇到operator, 都拿前2个数字计算 +- 计算结果存回到stack里面, 方便下一轮使用. +- Time,Space O(n) + + + + +--- + +**103. [Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)** Level: Medium Tags: [Linked List, Sort] + + +input一串数字, 需要出sorted output. 每次insert一个数字时, 都要放到正确的sorted的位置 + +每次insertion的时候, 都从input里面减掉这个数字 + +#### Linked List +- 把list里面每个元素都拿出来,scan and insert一遍 +- Time O(n^2), worst case, 每次放入n个数字里面的element, 刚好都是最大的 +- 所以每次要traverse n nodes, 然后走n次 + +##### 思考方法 +- 如果已经有个sorted list, insert一个element进去。怎么做? +- while 里面每个元素都小于 curr, keep going +- 一旦curr在某个点小了,加进去当下这个空隙。 + + + +--- + +**104. [Interleaving Positive and Negative Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20Positive%20and%20Negative%20Numbers.java)** Level: Medium Tags: [Two Pointers] + + +给一串数组 有正负数. 重新排列, 让数组里面 正数 和 负数 相隔开. 原来的order无所谓 + +#### Two pointer +- 需要知道正负的位置, 所以排序 O(nlogN) +- 考虑: 正数多还是负数多的问题, 举栗子就看出来端倪了 +- 然后Two Pointer, swap +- Time O(nlogn), space O(n) + +#### extra space +- 用extra O(n) space, 把正负分成两个list +- 然后分别按照index填回去 +- time O(n). space O(n) +- 但是就么有用到Two pointer了 + + + +--- + +**105. [Largest Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number.java)** Level: Medium Tags: [Sort] + + +给一串数字, 非负数, 把所有数字串联起来, 组成最大数字. + +因为结果很大, 所以用string表示 + +#### Sort, Comparator +- 考虑 more significant spot 应该拿到更大的值 +- 如果sort number, comparator 会比较难写: 每个digit的weight不同, 要分别讨论个位数和多位数. +- goal: 让较大的组合数排在前面, 让较小的组合数排在后面 +- 不如: 组合两种情况, 用String比较一下大小 (也可以用 integer来比较组合数, 但是为保险不超Integer.MAX_VALUE, 这里比较String) +- String.compareTo() 是按照 lexicographically, 字典顺序排列的 +- 利用compareTo, 来倒序排列 string, 刚好就得到我们要的结果. +- O(nlogn), 排序 + + + +--- + +**106. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**107. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**108. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**109. [Reverse Linked List II .java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List%20II%20.java)** Level: Medium Tags: [Linked List] + + +reverse 一个 linked list 中 [m ~ n] 的一部分. + +#### Reverse linked list +- 在基本的reverse linked list 上面 多了一层: 找到front node, 接下来的 [m ~ n] node 需要被reverse +- 只需要reverse中间的部分. +- Reverse的时候: 用一个dummyNode, 这道题里面, 其实就用 nodeFront, 那么 dummy.next 就是整个reversed list. + +##### 注意 +- 一定要Mark开头的那个mth node, 最后用它接上 剩下node tail. 不然后面的node会断掉 + +#### Previous notes +- 遍历到M前, +- 存一下那个点, +- 从M开始, for loop, reverse [m~n]。 然后把三段链接在一起。 + + + + +--- + +**110. [Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个Binary Tree root, 以及两个node p, q. 找 p 和 q 的 lowest common ancestor + +#### DFS +- 因为是 binary tree, 所以直接盲目搜索搜索path不efficient, use extra space and waste time +- 巧用DFS来找每一个node的common ancestor. +- Need the assumption: 1. unique nodes across tree; 2. must have a solution +- 当root == null或者 p q 任何一个在findLCA底部被找到了(root== A || root == B),那么就return 这个root. +- 三种情况: +- 1. A,B都找到,那么这个level的node就是其中一层的ancestor: 其实,最先recursively return到的那个,就是最底的LCA parent. +- 2. A 或者 B 找到,那就还没有公共parent, return 非null得那个。 +- 3. A B 都null, 那就找错了没有呗, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time/space O(n) + + + +--- + +**111. [Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的list +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Brutly寻找p和q的common ancestor, 然后recursively drive left/right +- 非常巧妙, 但是也比较局限; 稍微变条件, 就很难recursive. +- 几种情况: +- 1. one of p, q 在leaf, 那么此时的root其实就是lowest common ancestor +- 2. 如果p, q 在root的左右两边, 这就是分叉口, 那么root就是lowest common ancestor +- 3. 如果p,q 在root的同一边 (左,右), 那么继续dfs +- O(1) extra space, O(logn) time + + + +--- + +**112. [Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- 跟 `Remove Duplicates from Sorted Array` 几乎一模一样, 只不过unique index现在可以 validate 2 位 +- 其余一模一样, use index to track unique item; skip if duplicated for more than 2 times +- O(n) time, O(1) space +- 这里也可以真的用2个pointers 写while loop, 但是没有必要, 只是单纯地走一个for loop其实就足够. + + + +--- + +**113. [Remove Duplicates from Sorted List II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List%20II.java)** Level: Medium Tags: [Linked List] + + +从Linked list 里面摘掉重复元素: 只要重复过, 全部都删掉; 重复出现过得元素一个不留. + +#### Linked List +- sorted list, 重复元素都在一起 +- 运用 dummyHead: 如果要去掉所有重复元素, 就要有个dummyHead作为局外人在开头牵线 +- 只要发现一个 node.val == node.next.val, 就记下这个duplicated val, move forward, 过掉所有重复过的元素 +- 思想: +- 用第二个 inner while loop, 把所有的重复元素都处理干净, 然后再move forward +- 优点: outter while loop 不需要考虑太多case, 在inner loop 都把主要的business logic 解决了. + +##### 注意DummyHead 的使用 +- 当我们有了DummyHead 作为Linked List 的局外线头, 其实可以选择每次遇到duplicate, 就把更加后面的元素 强行assign 给 dummyHead.next +- 下面还尝试过一种做法: 但是需要考虑的edge case 太多了: 不断移动node, 知道不重复, assign prev.next = node. +- 这样的做法比较直白, 但是需要考虑很多edge case, 而且并没有很好利用到 dummy head, 注意规避. + +##### Previous Note +- 斩草除根。 +- 多个node,check node.next ?= node.next.next + + + + +--- + +**114. [QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)** Level: Medium Tags: [Quick Sort, Sort] + + +implement quick sort. + +#### Quick Sort +- 首先partition. 返还一个partition的那个中间点的位置: 这个时候, 所有小于nums[partitionIndex] 都应该在 partitionIndex左边 +- 然后劈开两半 +- 前后各自 quick sort, recursively +- 注意:在partition里面, 比较的时候nums[start] < pivot, nums[end]>pivot, 如果写成了 <= 会 stack overflow. +- Time O(nlogn), Space: O(1) + + + +--- + +**115. [MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/MergeSort.java)** Level: Medium Tags: [Merge Sort, Sort] + + +#### Merge Sort +- Divide and conquer, recursively +- 先从中间分段, merge sort 左边 (dfs), merge sort 右边 +- 最后merge起来 +- merge的时候因为是做int[], 所以没办法必须要O(n) space +- Time O(nlogn), Space O(n) + + + +--- + +**116. [Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**117. [Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)** Level: Medium Tags: [BFS, Tree] + + +如题, 但是output要倒序. + +#### BFS +- 跟Binary Tree Level Order Traversal一样,只不过存result一直存在存在0位. + + +#### DFS +- 根据level来append每个list +- rst里面add(0,...)每次都add在list开头 + + + +--- + +**118. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**119. [Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)** Level: Medium Tags: [Backtracking, Combination, DFS] + + +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +#### DFS, Backtracking +- for loop, recursive (dfs). +- 每个item用一次, 下一个level dfs(index + 1) +- Combination DFS. 画个图想想. 每次从1~n里面pick一个数字i +- 因为下一层不能重新回去 [0~i]选,所以下一层recursive要从i+1开始选。 + + + +--- + +**120. [Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)** Level: Medium Tags: [Array, Backpack DP, DP] + + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + +#### Backpack DP +- 计数问题, 可以想到DP. 其实就是Backpack VI. +- 从x个数字里面找candidate(可以重复用同一个数字), 来sum up to target. 找: # of ways to form the sequence. +- Backpack VI: 给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法 +- dp[i]: # of ways to build up to target i +- consider last step: 如果上一步取的是 candidate A, 那么就该加到dp[i]: +- dp[i] += dp[i - A] +- 要找overall dp[i], 就做一个for loop: dp[i] = sum{dp[i - num]}, where for (num: nums) +- Time: O(mn). m = size of nums, n = target +- If we optimize dp for loop, 需要Sort nums. O(mlogm). will efficient 如果m是constant或者relatively small. Overall: O(n) + +#### DFS, backtracking +- 尽管思考方式是对的, 但是 times out +- 可以重复使用数字的时候, 比如用1 来拼出 999, 这里用1就可以走999 dfs level, 不efficient + + + +--- + +**121. [Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右:即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list + +#### DFS +- Use Map 来存每一个level的结果 +- dfs function 里, 如果 input depth 不存在, 就add to map. +- dfs function 里面先: dfs(node.right), 然后 dfs(node.left) +- 由于always depth search on right side, 所以map会被right branch populate; 然后才是 leftChild.right + + + + +--- + +**122. [Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)** Level: Medium Tags: [DFS, Tree] + + +找到从max path sum from root. 条件: 至少有一个node. + +#### DFS +- 比Binary Tree Maximum Path Sum I 简单许多. 因为条件给的更多:at least 1 node + have to start from root +- root一定用到 +- 3种情况: curr node, curr+left, curr+right +- 因为一定包括root, 说以从 `dfs(root, sum=0)` 开始, 每个level先加root, sum += root.val + + + +--- + +**123. [Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +给一个single linked list, 右移k steps. k non-negative. + +#### Linked List basics +- 记得用dummy.next来存head. +- 特殊: 这里k可能大于list总长. 写一写linked node 移动的步数, 然后 k = k % n. +- 找到newTail, newHead, 然后利用dummy, 换位子 + + + +--- + +**124. [Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +找到binary tree 里的最长 consecutive sequence. + +#### DFS +- Divide and Conquer. dfs +- 分开 看左边/右边 +- 如果左边满足连续递增的规则, dfs (depth + 1), 如果不满足规则, dfs(depth = 1) +- 右边也是一样 +- 对结果跟max作比较, return + + + +--- + +**125. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**126. [Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)** Level: Medium Tags: [Basic Implementation, Enumeration, String] + + +给一个时间string"12:09", 用里面的4个integer组合成其他时间string, 目标找最小的next time. + +如果组合出的time string 在input time之前, 默认 + 24 hours. + +#### String +- enumerate all candidates and filter to keep the correct ones +- String.compareTo(string) -> gives lexicographical comparision + + + +--- + +**127. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**128. [Word Ladder.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder.java)** Level: Medium Tags: [BFS] + + +给一串string[], 需要找shortest distance to change from wordA -> wordB. (限制条件细节见原题) + +#### BFS +- 通常, 给一个graph(这道题可以把beginWord看成一个graph的起始node), 找shortest path用BFS +- 在start string基础上,string的每个字母都遍历所有26个字母 +- visited 过的 从wordList里去掉 +- time: word length m, there can be n candidates => O(mn) +- 但是总是exceed time limit on LeetCode. However, it passes LintCode: +- 原因是 LeetCode给的是list, list.contains(), list.remove() 都是 O(logn) time!!! +- convert to set first. + +#### Trie +- timeout, overkill + + + +--- + +**129. [Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)** Level: Medium Tags: [Design, Hash Table] + + + +给一个string[] dict, 和一个word. + +每个word都可以缩写成固定的abbreviation ``(详细看原题) + +检查input word是否满足unique + +#### HashMap +- 简单算出abbreviatioin +- 检查abbr是否存在; 如果存在, 是不是input word本身. + + + +--- + +**130. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**131. [Ugly Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number.java)** Level: Medium Tags: [Math] + + +LeetCode: 判断数字是否是ugly number. (definition: factor only have 2, 3, 5) + +#### Math +- 看是否可以整除. +- 看整除最终结果是否== 1 + +LintCode: 找kth ugly number, 应该与 Ugly Number II是一样的 + +- 方法1: PriorityQueue排序。用ArrayList check 新的ugly Number是否出现过。 +- 方法1-1:(解释不通,不可取)用PriorityQueue排序。神奇的3,5,7走位:按照题目答案的出发,定了3,5,7以什么规律出现。但是题目并没有特殊表明。 +- 方法2: DP . Not Done yet. + + + + +--- + +**132. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**133. [Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**134. [Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 + + + +--- + +**135. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**136. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**137. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**138. [ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)** Level: Medium Tags: [Design, Hash Table] + + +#### basic implementation +- 用HashMap, 理解题目规律,因为重复的计算可以被覆盖,所以是个优化题。没有什么太大的悬念和意义. +- 消灭重合点: +- 如果process当下col, 其实要减去过去所有加过的row的交接点。。。 +- 再分析,就是每次碰到row 取一个单点, sumRow += xxx。 +- 然后process当下col时候, sum += colValue * N - sumRow. 就等于把交叉所有row(曾经Process过的row)的点减去了。很方便。 +- 最后read in 是O(P), process也是O(P). + + + + +--- + +**139. [Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Two Pointers +- 木桶理论。盛水的最高取决于最低的那面墙。 +- 左右两墙,往中间跑动。 +- 另:若一面墙已经小于另外一面,就要移动,换掉矮墙(可能下一面更高,或更低) +- 但决不能换掉当下的高墙,因为低墙已经limit的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**140. [Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + +time: O(n) +space: O(1) + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List +- Basic Implementation of copy linked list: +- use node and dummy to hold new list, 遍历head.next .... null. +- Map 在这里用来: 1. avoid creating same node; 2. return the item if existing +- map 的 key全部是old object, 新的key全部是 newly created object +- 每一步都check map里面有没有head. 没有? 加上 +- 每一步都check map里面有没有head.random. 没有? 加上 + + + +--- + +**141. [Encode and Decode Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20Strings.java)** Level: Medium Tags: [String] + + +如题. + +#### String +- 'word.length()#word' 这样encode, 可以避免遇到# +- 基于我们自己定的规律, 在decode的里面不需要过多地去check error input, assume所有input都是规范的. +- decode就是找"#",然后用"#"前的数字截取后面的string. + + + + +--- + +**142. [Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)** Level: Medium Tags: [DFS, Divide and Conquer] + + +如题: Calculate the a^n % b where a, b and n are all 32bit integers. + +#### Divide and Conquer +- a^n可以被拆解成(a*a*a*a....*a), 是乘机形式,而%是可以把每一项都mod一下的。所以就拆开来take mod. +- 这里用个二分的方法,recursively二分下去,直到n/2为0或者1,然后分别对待. +- 注意1: 二分后要conquer,乘积可能大于Integer.MAX_VALUE, 所以用个long. +- 注意2: 要处理n%2==1的情况,二分时候自动省掉了一份 a,要乘一下。 + + + + +--- + +**143. [Find the Connected Component in the Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Connected%20Component%20in%20the%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS] + + +给一个undirected graph, return 所有的component. (这道题找不到了) + +#### BFS +- BFS遍历,把每个node的neighbor都加进来. +- 一定注意要把visit过的node Mark一下。因为curr node也会是别人的neighbor,会无限循环。 +- Component的定义:所有Component内的node必须被串联起来via path (反正这里是undirected, 只要链接上就好) +- 这道题:其实component在input里面都已经给好了,所有能一口气visit到的,全部加进queue里面,他们就是一个component里面的了。 +- 而我们这里不需要判断他们是不是Component + +#### DFS +- DFS 应该也可以 visit all nodes, mark visited. + + + +--- + +**144. [HashWithCustomizedClass(LinkedList).java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithCustomizedClass(LinkedList).java)** Level: Medium Tags: [Hash Table] + + +练习HashMap with customized class. functions: get(), put(), getRandom() + +#### Hash Table +- store map as array: `Entry[] table;` +- store entry as linked list: `public Entry(K key, V value, Entry next)` +- compute hashKey: `Math.abs(key.hashCode()) % this.capacity` +- Handle collision: +- 1. Check if duplicate (matching key), if so, replace and return +- 2. Check through the linked list, find find duplicate (matching key), replace and return. +- 3. If no duplicate, add the entry to the tail +- Find item: compute hashKey -> find linked list -> iterate over list to find a matching key. + + + +--- + +**145. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**146. [Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的sum. + +#### Segment Tree + Binary Search +- 其实是segment tree 每个node上面加个sum +- 记得Segment Tree methods: Build, Query +- Note: 存在SegmentTreeNode里面的是sum. 其他题目可能是min,max,count ... or something else. + + + +--- + +**147. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**148. [Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element%20II.java)** Level: Medium Tags: [Array] + + +#### Sort + count +- O(nlogN) + +#### Two counters +- O(n), count and track valueA, valueB +- count overall apperance at the end for the two items +- save to result +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +--- + +**149. [Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Linked List +- linked list 不能像partitioin array一样从两边遍历 +- 把小于value的加在前半段, 把 >= value的加在后半段 +- 做法很普通: 建造两个list, midTail pointer, post pointer +- 把满足条件(=x)的数字分别放到两个list里面 +- 记得用dummyNode track head. +- 最终midTail.next = post链接起来。 + + + +--- + +**150. [Peeking Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Peeking%20Iterator.java)** Level: Medium Tags: [Design] + + +#### Use concept pre cache +- 找一个cache来存next()的值, 也就是: next value的值提前存在cache里面 +- 因此peek()的时候, 就可以直接return cache, 而不用做 itt.next() +- 然后每次真的next()的时候, 里取下一个itt.next()维护这个cache + +#### Previous notes +- 再一次理解错题意. peek() 就是头顶,但是不一定是最大值啊。 +- 总是把PEEK想成了最大值,然后用2 STACK做了最大值的cache,练的一手好双stack,可惜错了。 + + + + +--- + +**151. [Rehashing.java](https://github.com/awangdev/LintCode/blob/master/Java/Rehashing.java)** Level: Medium Tags: [Hash Table] + + +给一个Hash Table, 是用 linked list 做的. 问题是: capacity太小, collision太多的情况下, 需要double capacity 然后rehash. + +#### Hash Table +- 明白hashCode() function的意义: 拿到hashKey的时候, 用hashKey%capacity 来做hash code +- hashcode就是hash map里面的index +- 明白collision handling 的方式, 和如何double capacity而rehashing +- 都是基本操作, 概念实现 + + + +--- + +**152. [Reorder List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reorder%20List.java)** Level: Medium Tags: [Linked List] + + +给一个Linked list, reorder: 从head/tail 两个方向 向中间进发, re-order like: one node at a time, + +#### Linked List 功能大集合 +- reverse list, find mid of list, merge two list +- 先find mid, 然后把 mid.next reverse了, 最后merge 两段. +- 注意, 用完mid.next之后, 一定要 mid.next = null, 不然merge会出问题 + + + +--- + +**153. [Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)** Level: Medium Tags: [Backtracking, DFS, String] + + +给一串数字, 检查是否是valid IP, 如果合理, 给出所有valid 的IP组合方式. + +#### Backtracking +- 递归的终点:list.zie() == 3, 解决最后一段 +- 递归在一个index上面, pass s.toCharArray() +- validate string要注意leading '0' +- 注意: 递归的时候可以用一个start/level/index来跑路 +- 但是尽量不要去改变Input source, 会变得非常confusing. +- note: code有点messy, 因为要考虑IP的valid情况 +- 那个'remainValid', 其实是一个对于remain substring的判断优化, 不成立的就不dfs了 + + + +--- + +**154. [Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String.java)** Level: Medium Tags: [String] + + +#### Break by space, then flip +- 结尾不能有空格 +- trim() output +- 如果Input是 ""的话,split以后就啥也没有了 +- 另个题目Reverse Words in String (char[]) 可以in-place, 条件是char[]里面是没有首尾空格. +- Time, Space: O(n) + +#### Other methods +- flip entire string, then flip each individual string (代码有点多, 这道题犯不着) + + + +--- + +**155. [Reverse Words in a String II.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20II.java)** Level: Medium Tags: [String] + + +#### In-place reverse +- reverse用两回. 全局reverse。局部:遇到空格reverse +- 注意ending index: `i == str.length - 1`, 结尾点即使没有' '也要给reverse一下最后一个词 + + + + +--- + +**156. [Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)** Level: Medium Tags: [Array, Binary Search] + + +给2D matrix, 每行sorted, 每行的首位都大于上一行的末尾. goal: find target from matrix + +#### 2D matrix 转1D array +- 一行一行是从小到大, sorted, 连续的, 可以看做1D sorted array +- Binary Search + + + +--- + +**157. [Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)** Level: Medium Tags: [Binary Search, Divide and Conquer] + + +给matrix, 每一行sorted, 每一列从上往下sorted, 找target是否存在 + +#### Binary Search +- 根据给定的性质, 其实点选的极端一点: x = 最下面的row, y = 当下一行里面最小的left position. +- (x,y)在左下角 +- 在此情况下, 只能往一个方向运行: 如果小于target, y++; 如果大于target, 那么只能x-- +- 每次操作, 都是删掉一行, 或者一列, 再也不需要回头看 +- `while (x >= 0 && y < col) {}` 确保不会跑脱 +- 同样的方式: 可以从右上角(0, col - 1) 开始, 代码稍微改一改 + +#### Divide and Conquer? +- TODO + + + +--- + +**158. [Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)** Level: Medium Tags: [Array, Binary Search] + + +给sorted array, 有重复数字, 找跟target重合所在的range. + +#### Binary Search +- 2个while loop +- 找first/last occurance +- TODO: Can the code be simplified? + + + + +--- + +**159. [Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)** Level: Medium Tags: [BST, Binary Tree] + + +给一个BST, integer range (k1, k2), 找range 里面所有的integer. + +#### BST +- 等于dfs遍历了所有k1<= x <= k2的x node。 +- dfs left, process root, then dfs right +- 这里, 把 left/right/match的情况全部cover了,然后把k1,k2的边框限制好,中间就全部遍历了。 + + + +--- + +**160. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**161. [Summary Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Summary%20Ranges.java)** Level: Medium Tags: [Array] + + +给一串sorted list, 中间有缺数字, return 所有数字的range string (example 看题目) + +#### Basic implementation +- 用一个list as the buffer to store candidates +- when: 1. end of nums; 2. not continuous integer => convert list to result + + + +--- + +**162. [Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Topological Sort] + + +#### Topological Sort BFS +- indegree tracking: Track all neighbors/childrens. 把所有的children都存在 inDegree里面 +- Process with a queue: 先把所有的root加一遍(indegree == 0),可能多个root。并且全部加到queue里面。 +- BFS with Queue: +- Only when map.get(label) == 0, add into queue && rst. (indegree剪完了, 就是root啦) +- inDegree在这里就 count down indegree, 确保在后面出现的node, 一定最后process. + + +#### Basics about graph +- 几个graph的condition: +- 1. 可能有多个root +- 2. directed node, 可以direct backwards. + +TODO: +- build`Map inDegree = new HashMap<>();` and include the root itself +- that is more traditional indegree building + + + +--- + +**163. [Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)** Level: Medium Tags: [Array, Enumeration] + + +从(0,0)坐标, 走完spiral matrix, 把结果存在list里. + +#### DX, DY +- Basic implementation, array, enumeration +- 写一下position前进的方向: RIGHT->DOWN->LEFT->UP +- 用一个direction status 确定方向 +- 写一个compute direction function 改变方向 `(direction + 1) % 4` +- `boolean[][] visited` 来track走过的地方 + + + +--- + +**164. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**165. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**166. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + +**167. [Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)** Level: Medium Tags: [Backtracking, DFS, DP] + + +String 只包含 + , - 两个符号. 两个人轮流把consecutive连续的`++`, 翻转成 `--`. + +如果其中一个人再无法翻转了, 另一个人就赢. 求: 给出string, 先手是否能赢. + +#### Backtracking +- curr player 每走一步, 就生成一种新的局面, dfs on this +- 等到dfs结束, 不论成功与否, 都要backtracking +- curr level: 把"++" 改成 "--"; backtrack的时候, 改回 '--' +- 换成boolean[] 比 string/stringBuilder要快很多, 因为不需要重新生成string. +- ++ 可以走 (n - 1)个位置: +- T(N) = (N - 2) * T(N - 2) = (N - 4) * (N - 2) * T(N - 4) ... = O(N!) + +##### iterate based on "++" +- 做一个String s的 replica: string or stringBuilder +- 每次dfs后, 然后更替里面的字符 "+" => "-" +- 目的只是Mark已经用过的index +- 真正的dfs 还是在 original input string s 身上展开 +- 每次都重新生成substring, 并不是很efficient + +##### Game theory +- 保证p1能胜利,就必须保持所有p2的move都不能赢 +- 或者说, 在知道棋的所有情况时, 只要p2有一种路子会输, p1就一定能走对路能赢. +- 同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +- p1: player1, p2: player2 + +#### O(N^2) 的 DP +- 需要Game Theory的功底, Nim game. https://www.jiuzhang.com/qa/941/ +- http://www.1point3acres.com/bbs/thread-137953-1-1.html +- TODO: https://leetcode.com/problems/flip-game-ii/discuss/73954/Theory-matters-from-Backtracking(128ms)-to-DP-(0ms) + + + +--- + +**168. [Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning.java)** Level: Medium Tags: [Backtracking, DFS] + + +给个string s, partition(分段)后, 要确保每个partition都是palindrome. + +求所有partition palindrome组合. `list>` + +#### DFS +- 可以top->bottom: 遍历str, validate substring(start, i); if valid, add as candidate, and dfs; backtrack by remove candidate. +- 也可以bottom->up: 遍历str, validate substring(start, i); if valid, dfs(remaining str), return list of suffix; cross match with curr candidate. + +#### DFS Top->Bottom +- 在遍历str的时候,考虑从每个curr spot 到 str 结尾,是能有多少种palindorme? +- 那就从curr spot当个字符开始算,开始backtracking. +- 如果所选不是palindrome, 那move on. +- 若所选的确是palindrome, 加到path里面,DFS去下个level,等遍历到了结尾,这就产生了一种分割成palindrome的串。 +- 每次DFS结尾,要把这一层加的所选palindrome删掉,backtracking嘛 + +#### Optimization +- 可以再每一个dfs level 算 isPalindrome(S), 但是可以先把 boolean[][] isPalin 算出来, 每次O(1) 来用 +- 注意: isPalin[i][j] 是 inclusive的, 所以用的时候要认准坐标 +- Calculate isPalin[i][j]: pick mid point [0 ~ n] +- expand and validate palindrome at these indexes: `[mid, mid+1]` or `[mid-1][mid+1]` + +#### Complexity +- Overall Space O(n^2): 存 isPlain[][] +- Time O(2^n), 每一层都在做 pick/not pick index i 的选择, 所以worst case 2^n. +- 因为我们的isPalin[][]优化了palindrome的判断O(1), 所以overall Time: O(2^n) + + + +--- + +**169. [Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)** Level: Medium Tags: [Array, Hash Table, PreSum] + + +给一个int[][] matrix, 找一个sub matrix, where the sum == 0. + +#### PreSum的思想 +- 算出一个右下角点(i,j)到(0,0)的大小: 上一块 + 左一块 + curr node - overlap area +- preSum[i][j]: sum from (0,0) to (i-1,j-1) +- same approach as `subarray sum`: use hashmap to store diff->index; if diff re-appears, that means sum of 0 has occurred +- sequence of calculation: 1. iterate over start row. 2. iterate over end row. 3. iterate over col number (this is where hashmap is stored based on) +- the iteration over col is like a screening: find previous sum and determine result +- Note: 其实并没有真的去找 `== 0` 的解答,而是根据特性来判断 `剩下的/后来加上的一定是0` + + + +--- + +**170. [Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Substring.java)** Level: Medium Tags: [DP, String] + + +给一个string, 找到最长的palindrome substring. + +Related: Longest Palindromic Subsequence, Palindrome Partioning II + +O(n^2) is not too hard to think of. How about O(n)? + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + +#### DP: isPalin[][] +- 穷举double for loop. O(n^2) +- boolean isPalin[i][j], 每次确认有palindrome就记录下来true / false +- 穷举的for loop计算顺序: end point j, and stat point i = [0, j] +- 在计算 isPalin[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- double for loop: O(n^2). slower, because it guarantees O(n^2) due to the for loop + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**171. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + +**172. [Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/Gas%20Station.java)** Level: Medium Tags: [Greedy] + + +给一串gas station array, 每个index里面有一定数量gas. + +给一串cost array, 每个index有一个值, 是reach下一个gas station的油耗. + +array的结尾地方, 再下一个点是开头, 形成一个circle route. + +找一个index, 作为starting point: 让车子从这个点, 拿上油, 开出去, 还能开回到这个starting point + +#### Greedy +- 不论从哪一个点开始, 都可以记录总油耗, `total = {gas[i] - cost[i]}`. 最后如果total < 0, 无论从哪开始, 必然都不能走回来 +- 可以记录每一步的油耗积累, `remain += gas[i] - cost[i]` +- 一旦 remain < 0, 说明之前的starting point 不合适, 也就是说, 初始点肯定在后面的index. 重设: start = i + 1 +- single for loop. Time: O(n) + +#### NOT DP +- 看似有点像 House Robber II, 但是问题要求的是: 一个起始点的index +- 而不是求: 最后点可否走完/最值/计数 + + + +--- + +**173. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**174. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + +**175. [H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)** Level: Medium Tags: [Bucket Sort, Hash Table, Sort] + + +找到h-index, 给的citation int[] 并不是sorted. h-index 的definition 具体看题目. + +#### Sort, find h from end +- 例子写出来,发现可以sort以后按照定义搜索一遍。 nlogn. +- 搜索一遍时候可以优化,用binary search. 但是没意义,因为array.sort已经用了nlogn +- 题目给的规则, 从小到大排序后: 剩下的 paper `n-h`, 全部要 <= h 个 citation. +- time O(nlogn), search O(n) + +##### 正向思考 +- 从i = 0 开始找第一个 `citations[i] >= h`, 就是第一个符合 h-index 规则的paper, return h + +##### 反向思考 +- 如果从 h = n, 每次h--; 那么 `x = n - h` 就是从 `[0 ~ n)` 开始找第一个 `dictations[x] >= h`, 就是结果 +- 同时, `dictations[x-1]` 就是最后一个(dictation最多的)其余的paper. + +#### Bucket count / Bucket Sort +- O(n) +- Bucket sort的思想(更像是counting sort?): 过一遍 input, 把dictation value 作为 index, 分布在bucket[index]上++ +- bucket[x] 是 count when # of citation == x. +- 如果 x 大于 n的时候, 就超出了index范围, 但是刚好这个问题可以包容, 把这样的情况记位在bucket[n]就可以 +- 巧妙: `sum += bucket[h]` where `h = [n ~ 0]` 利用了h-index的definition: +- #of papers (sum of bucket[n]...bucket[0]) has more than h cidations +- 这里运用到了bucket sort的思想, 但是并不是sorting, 而h-index的定义运用的很巧妙. +- Read more about actual bucket sort: https://en.wikipedia.org/wiki/Bucket_sort + + + +--- + +**176. [H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index%20II.java)** Level: Medium Tags: [Binary Search] + + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + + + +--- + +**177. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**178. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**179. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + +**180. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**181. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**182. [Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort] + + +#### PriorityQueue +- Use priority queue to sort by customized class interval{int dist; int x, y;}. +- Sort by larger distance and then sort by start index +- seat(): pq.poll() to find interval of largest distance. Split and add new intervals back to queue. +- leave(x): one seat will be in 2 intervals: remove both from pq, and merge to a new interval. +- 主方程写出来其实很好写, 就是 split + add interval, 然后 find + delete interval 而已. 最难的是构建data structure +- seat(): O(logn), leave(): O(n) + +##### Trick: 构建虚拟 boundary +- 如果是开头的seat, 或者是结尾的seat, 比较难handle: 一开始坐在seat=0的时候, 没有interval啊! +- Trick就是, 我们自己定义个虚拟的座位 `seat=-1`, `seat=N` +- 一开始有一个 interval[-1, N] 然后就建立了boundary. +- 从此以后, 每次split成小interval的时候: +- 遇到 `interval[-1, y]`, distance就是 `(y - 0)` +- 遇到 `interval[x, N]`, distance就是 `(N - 1 - x)` +- 当然正常的interval dist 就是 `(y - x) / 2` + +##### distance 中间点 +- Interval.dist 我们其实做的是 distance的中间点 `(y - x) / 2` +- 这里的dist是 `距离两边的距离` 而不是 x, y 之间的距离. 这里要特别注意. + +#### TreeSet +- https://leetcode.com/problems/exam-room/discuss/139885/Java-Solution-based-on-treeset/153588 + +#### Map +- how? +- TODO, not sure. + + + +--- + +**183. [Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Anagrams.java)** Level: Medium Tags: [Array, Hash Table] + + +把anagram找到并output + +#### HashMap +- 存在int[26], Arrays.toString(arr) 就是 string key: character frequency map +- anagram都有一样的key, 存进hashmap +- output anagrams + +#### HashMap + Sort +- HashMap 的做法. sort每个string, 存进HashMap, 重复的就是anagrams,最后输出。 +- toCharArray +- Arrays.sort +- Stirng.valueOf(char[]) +- 时间n*L*O(logL),L是最长string的长度。 + +#### Previous Notes +- Arrays.toString(arr)的做法。arr是int[26], assuming only have 26 lowercase letters. +- Count occurrance, 然后convert to String,作为map的key. +- Time complexity: nO(L) +- 另一种做法:http://www.jiuzhang.com/solutions/anagrams/ +- 1. take each string, count the occurrance of the 26 letters. save in int[]count. +- 2. hash the int[] count and output a unique hash value; hash = hash * a + num; a = a * b. +- 3. save to hashmap in the same way as we do. +- 这一步把for s: strs 里面的时间复杂度降到了O(L). L = s.length(). +- Need to work on the getHash() function. +- 时间变成n*O(L). Better. + + + + +--- + +**184. [Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)** Level: Medium Tags: [DFS, Hash Table, Tree] + + +给一串3-digit 的数组. 每个数字的表达一个TreeNode, 3 digit分别代表: depth.position.value + +这串数字已经从小到大排列. 求: 所有可能的 root->leaf path 的所有可能的 path sum 总和. + +#### DFS, Hash Table +- 因为`前两个digit可以uniquely identify`一个node, 所以可以把前两个digit作为key, 定位node. +- 特点: 比如考虑root, 有 n 个leaf, 就会加 n 遍root, 因为有 n 个 unique path嘛. +- 实现: 每个node, 上来先把curr value加进sum; 只要有child, 到这个node位置的以上path sum 就要被重加一次. +- format: depth.position.value. (on same level, position may not be continuous) +- approach: map each number into: , and dfs. +- Start from dfs(map, rootKey, sum): +- 1. add node value to sum +- 2. compute potential child. +- 3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +- 4. also, if child exist, dfs into next level +- Space, time O(n) + + + +--- + +**185. [Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)** Level: Medium Tags: [DP, Math] + + +具体看题目: count # of valid rectangles (four corner are 1) in a grid[][]. + +#### basic thinking + Math +- Fix two rows and count matching columns +- Calculate number rectangles with `combination` concept: +- total number of combinations of pick 2 points randomly: count * (count - 1) / 2 + +#### DP +- TODO. HOW? + +#### Brutle +- O(m^2 * n^2), times out + + + +--- + +**186. [Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)** Level: Medium Tags: [DP, String] + + +根据题意, count # of palindromic substring. (不同index截取出来的substring算不同的情况) + +#### isPalin[][] +- build boolean[][] to check isPalin[i][j] with DP concept +- check all candidates isPalin[][] +- O(n^2) + +#### odd/even split check +https://leetcode.com/problems/palindromic-substrings/discuss/105689/Java-solution-8-lines-extendPalindrome + + + +--- + +**187. [Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Multiply%20Strings.java)** Level: Medium Tags: [Math, String] + + +给两个integer String, 求乘积 + +#### String calculation, basic implementation +- let num1 = multipier, num2 = base. +- mutiply and save into int[m + n], without carry. Loop over num1, each row num1[x] * num2 +- move carry to the correct index and direclty save result +- calculate carry on rst[]: sb.insert(0, c) such that no need to reverse() later +- remove leading '0', but do not delete string "0" +- time,space O(mn) + +#### Previous notes. +- Bad solution: reversing makes it complicated, no need to reverse. +- 1. 数字‘123’, 在数组里面, index == 0 是 ‘1’。 但是我们平时习惯从最小位数开始乘积,就是末尾的'3'开始。 +- 所以!翻转两个数字先!我去。这个是个大坑。 +- 2. 乘积product,和移动Carrier都很普通。 +- 3. !!最后不能忘了再翻转。 +- 4. 最后一个看坑。要是乘积是0,就返回‘0’。 但是这个其实可以在开头catch到没必要做到结尾catch。 +- 用到几个StringBuffer的好东西: reverse(), sb.deleteCharAt(i) +- 找数字,或者26个字母,都可以: s.charAt(i) - '0'; s.charAt(i) - 'a'; + + + +--- + +**188. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**189. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**190. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**191. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**192. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + +**193. [Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + +time: O(n) +space: O(1) + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 看第二个解答, 进一步简化了代码 +- PreProduct, 跟preSum的感觉有点像, 就是差一位. + + + +--- + +**194. [Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)** Level: Medium Tags: [Bit Manipulation] + +time: O(n) +space: O(1), 32-bit array + +给出Hamming Distance定义(bit format时候有多少binary diff), 求一串数字的hamming distance总和. + +#### Bit Manipulation +- Bit题: 考验 bit >>, mask & 1, 还有对题目的理解能力 +- Put integers in binary, and compare each column: +- for each `1`, ask: how many are different from me? all the `0` +- `# of diffs at each bit-column = #ofZero * #ofOne ` +- 1. countZero[], countOne[]; 2. loop over nums and populate the two array + +##### 注意雷点 +- 问清楚: 10^9 < 2^31, we are okay with 32 bits +- `最终的hamming distance 要从 [1 ~ 32] 哪个bit开始算起`? 取决于 `最长`的那个binary format: 但不用先去找bit length +- 在做countZero, countOne时候, 都做32-bit; 最终做乘积的时候, 如果 `1` 或者 `0` 个数为零, 乘积自然为0. + + + + +--- + +**195. [Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + +time: O(n) +space: O(n) + +给一个tree, 按照题意找最一个node满足: +1. 这个node的subtree涵盖最深level的所有leaves. +2. 这个node必须是能找到的最deep那个 + +条件2的需求是因为: root本身就是满足条件1的node, 还有很多Higher-level node也是如此, 所以要找那个deepest. + + +#### DFS on tree +- 分析题目, 思想是: 看到tree里面所有的leaves, 找到他们最deep的 common ancestor +- Maintain a map +- Recursively dfs: return deepest node that has all leaves by these comparisons: +- 1. If left,right child same depth, return root: they need common ancestor +- 2. If not same depth, return the one with larger depth +- 被传送去上一个level的, 永远都是subtree里面符合题意的: the node containing all leaf nodes +- Visit all nodes once O(n), space O(n) + +#### BFS +- Find all leaves at deepest level +- Use map to track each node-parent +- Backtrack all nodes to find common ancestor + + + +--- + +**196. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**197. [Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + +time: O(n) +space: O(n) + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path 的情况, 不懂得要问: +- 1. `.` 代表current directory, 可以忽略. +- 2. `../` 表示previous level. +- 3. double slash 可以忽略. +- 4. empty string 要output `/` +- 最终就是用stack (`上一个加进去的item, 用来备选pop() out`), 遇到 `../` pop()掉上一个加上去的item, 其余加进stack +- 最终用 '/' 把所有item连接起来. + + + +--- + +**198. [Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)** Level: Medium Tags: [Linked List, Stack, Tree] + +time: O(n) +space: O(n) + +给一个BST, convert成 sorted doubly DoublyListNode. + +#### Inorder Traversal, Linked List +- 会iterative traverse Binary Search Tree(Stack && handle left-dig-down) +- create Doubly-ListNode, 注意用一个dNode作为tail node of the list + +##### Iterative inorder traversal +- 在check right node的事后, +- 不论right == null or != null, 每次都要强行move to right. +- 如果不node = node.right, +- 很可能发生窘境: +- node always = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 + + + +--- + +**199. [Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + +time: O(n) +space: O(n) + +#### Queue +- 简单的level traversal.根据level奇数偶数而add到不同位子. +- Option1: based on level % 2, insert to front/end of list +- Option2: based on level, insert right/left of node into queue + + + +--- + +**200. [Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- true/false problem, think about dp +- 子问题: 前i个字母, 是否可以有valid break +- 检查dp[j] && `if substring(j, i) valid`, for all j = [0 ~ i] +- dp = new boolean[n + 1]; dp[0] = true; +- goal: if there is a j, `dp[j] == true && word[j, n] in dict`. Need iterate over i = [0 ~ n], also j = [0, i] +- 注意, 用set代替list, 因为要用 contains(). + +#### Previous notes +##### 方法2(attempt4 code) +- 与Word BreakII用同样的DP。 +- valid[i]: 记录从i到valid array末尾是否valid. + +##### 方法1:(attempt3 code) +- state,rst[i]: 从[0~i] inclusive的string是否可以在dict中break开来找到? +- function: rst[i] = true if (rst[i - j] && set.contains(s.substring(i - j, i))); j in[0~i] +- 1. rst[i - j] 记录的是[0, i-j]这一段是否可以break后在dict找到。 +- 2. 若true,再加上剩下所有[i-j, i]都能在dict找到,那么rst[i] = rst[0, i - j] && rst[i-j, i] == true +- 优化:找dict里面最长string, 限制j的增大。 + + + + +--- + +**201. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**202. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**203. [Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Random%20Pick%20Index.java)** Level: Medium Tags: [Reservior Sampling] + +time: O(n) +space: O(n) for input int[], O(1) extra space used + +#### Reservior sampling +- Random choose: think about reservoir sampling. https://www.youtube.com/watch?v=A1iwzSew5QY +- Use random generator rd.nextInt(x) pick integer between [0, x) +- try all numbers, when target is met, we want to model reservoir sampling: +- item was chosen out of i samples, and all other samples are failed. +- where we can use 'count' to represent the denominator/base to choose. +- **HAVE TO finish all samples** to make sure equal opportunity +- we can pick that last matched item as result +- `rd.nextInt(count++) == 0` make sure we are always picking num == 0 to meet definition of reservoir sampling. + +#### Knowledge +- If multiply these probablities together to get the probability of one item being chosen with reservior sampling: +- probability = 1/i * (1 - 1/i+1) * (1 - 1/i+2) ....(1 - 1/n) = 1/n + + + + +--- + +**204. [Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Celebrity.java)** Level: Medium Tags: [Array, Greedy] + +time: O(n) +space: O(1) + +有n个人, 其中有个人是celebrity, 满足条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +#### Understand the property +- If brutly find celeb by comparing all possible pair: take complete O(n^2) handshakes. +- Instead, we can perform pruning, or like survival mode: +- 1. Assume a celeb = 0, and compare with all i = [1~ n-1] +- 2. If `celeb candidate know i, set celeb = i` as the next candidate (ex: prev canddiate invalid when he knows i) +- 3. For last standing celeb candidate: compare with all for validation +- Why performing the last run of validation? There could be someone dropped out before we execute `know(celeb, i)`. + +##### 思考逻辑 +- 先写出来[0 ~ n - 1], 最简单的方式 O(n^2) 检查, 记录每个人的状态. +- 逐渐发现, 因为 celeb 谁都不会认识, 那么当任何candidate knows anyone, 他自身就不是celeb. +- 我们可以greedy地, 一旦fail一个, 就立刻假设下一个是celeb candidate +- 最终还是要检查一遍, 避免错漏. +- 想一下happy case: 如果 celeb=0, 那么 know(celeb, i) 永远都是false, 然后 celeb一直保持0, 坚持到verify所有人. + + + +--- + +**205. [Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/Sparse%20Matrix%20Multiplication.java)** Level: Medium Tags: [Hash Table] + +time: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` +space: O(1) extra + +给两个matrics, 做乘积. 注意, 是sparse matrix (特点: 很多0). + +#### Hash Table +- Recall matric multiplication rules: result[i][j] = sum(A-row[i] * B-col[j]) +- `sparse matric: lots positions are zero` +- 平白地写matric multiplication 没有意义, 重点就是optimization: +- `optimization`: for A-zero-row, and B-zero-col, there is no need to calculate, just return 0. +- 1. Find A-zero-rows and store in setA, same for setB +- 2. during multiplication, reduce time complexity. +- Base: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` + +#### Matrices +- 乘法规则: result[i][j] = sum(A-row[i] * B-col[j]) +- A column size == B row size. 并且: 计算顺序是iterate over A column size + + + +--- + +**206. [Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)** Level: Medium Tags: [Hash Table] + +time: O(mn) +space: O(X), X = max wall width + +给一面墙, 每一行是一行bricks. 用一条vertical line 扫描, 会vertically割开brink. 找到割开最少brick的那条线的x index. + +#### Hash Table +- Find the vertical line (x-coordinate of the grid), where most gaps are found. +- Each gap has (x,y) coordinate +- Create `map`, and maintain a max occurance. +- 计算: x-coordinate: `x = 0; x += brick[i] width` +- Eventually: min-crossed bricks = wall.lenght - maxOccurrance + +##### 思想 +- 分析题意, 找到题目的目标 +- 虽然Map自己不能有sort的规律, 但是可以maintain global variable + + + +--- + +**207. [Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/Exclusive%20Time%20of%20Functions.java)** Level: Medium Tags: [Stack] + + +#### Stack +- 1. later function always appears after prior fn: 1 is called by 0 +- 2. `Not mentione in the question`: a function can be started multiple times +- 3. `Not mentione in the question`: a fn cannot start if children fn starts +- 4. Use stack to keep id +- TODO: what leads to the choice of stack? stacking fn id + + + +--- + +**208. [Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)** Level: Medium Tags: [Array, Math] + + +#### Array, Math +- 这个问题更在于问题本身的分析 (而且还有多余条件); 最终的for loop 也比较不standard. +- People younger than 15 cannot make requests due to the first rule. +- From the age of 15, people can make requests to the same age: a[i] * (a[i] - 1) requests. +- People can make requests to younger people older than 0.5 * i + 7: a[j] * a[i] requests. +- The third rule is redundant as the condition is already covered by the second rule. +- TODO: the approach. + + + +--- + +**209. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + + +// 如何想到从中间initialize + + + +--- + +**210. [Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)** Level: Medium Tags: [Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +#### Map +- use `Map` to store inline preSum and its index. +- 1. Build presum incline +- 2. Use map to cache current preSum value and its index: `Map` +- 3. Each iteration: calculate possible preSum candidate that prior target sequence. ex: `(preSum - k)` +- 4. Use the calculated preSum candidate to find index +- 5. Use found index to calculate for result. ex: calculate range. + + + +--- + +**211. [Contiguous Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Contiguous%20Array.java)** Level: Medium Tags: [Hash Table] + + +TODO: how aout without chaning the input nums? + + + +--- + +**212. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + +time: O(n) +space: O(n) + +给一串点, 找是否有一个所有点中间的, 跟y-axis平行的中线. + +#### Hash Table +- 1. store in `Map>`, 2. iterate over map, check head,tail against the mid point +- 很好的细节题目: +- 1. 除以2, 需要存double +- 2. (问面试官)可以有重复的点! 所以track `set` +- 3. 处理 left==right时候, 就当做两个点来处理. +- 4. 存进set里面没有sort, 但是最后做check的时候, 需要sort list +- 时间: visit all nodes 两遍, O(n) + + + +--- + +**213. [Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + +time: O(1) avg +space: O(n) + +#### Hash Table +- 用`map 来track value->index`, 用`list track index->value` +- map查看value是否存在 +- list maintain 用来 insert/remove/random operations. +- 特点: 一旦remove, 换到list结尾然后 `list.remove(list.size() - 1)`, 这样remove的cost更低. +- list.remove(object) 应该是要O(logn) 做一个search的. + + + +--- + +**214. [Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Coordinate DP, DP] + +time: O(n^2) +time: O(n) + +给一串 unsorted sequence, 找到长 increasing subsequence 的个数! + +#### Coordinate DP +- 需要能够判断综合题, 分清楚情况和套路: combination of `longest subsequence` and `ways to do`, as well as global variable. +- len[i] (我们平时的dp[i]): 在前i个元素中, 最长的 increasing subsequence length; +- count[i]: 在前i个元素中, 并且以 len[i]这个长度为准的 subsequence的 count. 或者: 在前i个元素中, ways to reach longest increasing subsequence. +- `len[i] == len[j] + 1`: same length, but different sequence, so add all `count[i] += count[j]` +- `len[i] < len[j] + 1`: 这就是更长的情况找到了, 那么有多少次 count[j] 有多少, count[i] 就有多少. 仔细想sequence: 长度增长了, 但是ways to reach i 没有增长. +- 同样的判断需要用在 maxLen 和 maxFreq上: +- 如果没有增长 maxLen 不变, maxFreq上面需要 +=count[i] (同一种长度, 多了更多的做法) +- 如果maxLen 变长, maxFreq 也就是采用了 count[i] = count[j] +- TODO: Is rolling array possible? + +#### 相关 +- 都是 Coordiate DP, DP的鼻祖家族: +- Longest Increasing Subsequence (跟这道题的一部分一模一样) +- Longest Continuous Increasing Subsequence (连续, 只check dp[i - 1]) +- Longest Increasing Continuous Subsequence I, II (Lintcode, II 是matrix) + + + +--- + +**215. [Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)** Level: Medium Tags: [Coordinate DP, DP, Status DP] + + + +#### DP +- 特点: 上一步可能是swaped也可能是fixed +- 考虑A,B之间的现状: `A[i] > A[i - 1] && B[i] > B[i - 1]` 或者 `A[i] > B[i - 1] && B[i] > A[i - 1]` +- 问题: 如何把这个状态变成合理的strick-increasing状态? +- `A[i] > A[i - 1] && B[i] > B[i - 1]`: 1. 已经合理, 也不动. 2. [i], [i-1] 全部都swap +- `A[i] > B[i - 1] && B[i] > A[i - 1]`, 交错开来, 所以调换[i], 或者[i-1]: 1. 换[i-1]. 2. 换[i] +- 注意因为求min, 所以init value应该是 Integer.MAX_VALUE; + + + +--- + +**216. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**217. [Populating Next Right Pointers in Each Node II.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node%20II.java)** Level: Medium Tags: [DFS, Tree] + +time: O(n) +space: O(1) + +给一个binary tree, 用constant space link 所有所有node.next to same level next node. + +#### DFS +- 用constant space 也就是不可以BFS, 但是mention了用dfs stack space没问题 (提示啊!) +- 1. link leftChild -> rightChild +- 2. resolve root.rightMost child -> first possible root.next.left/right child +- 3. dfs connect(rightChild), connect(leftChild) +- Each level should be fully linked from left side, so every reach to parent will have valid path or end. + +#### Trick +- 1. 处理 nextNode -> next -> next ...的case: 找到第一个有child的next node才可以罢休. 这个case很容易miss +- 2. 我们的假设是, 上一个level的所有node都应该是linked, 那么在dfs时候, 就应该先connect(root.right). 右孩子的全处理完毕, 那么trick1才可以施行. + + + +--- + +**218. [Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + +time: log(n) +space: O(1) + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 +- 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` +- `start mid`: start = mid; +- 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- `mid < target < end`: start = mid; +- `target < mid`: end = mid; + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**219. [Find the Weak Connected Component in the Directed Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Weak%20Connected%20Component%20in%20the%20Directed%20Graph.java)** Level: Medium Tags: [Union Find] + + +遍历 weak connected graph, 将结果存在 List>种. + +#### Union Find +- 跟传统的UnionFind有两点不同: +- 1. 用 Map 代替 int[], 因为没有给出 graph node label的 boundary. +- 2. find(x)时候, 没有去update `parent[x]/map.put(x, ..)`. 因为我们最终需要找到这个path. +- 无法用传统dfs: directed node 无法point到上一个点; 必须用`存parent的方式把所有node遍历掉` + +#### Identify这是个union-find问题 +- 看到了weak component的形式: 一个点指向所有,那么所有的点都有一个公共的parent,然后就是要找出这些点。 +- 为何不能从一个点出发,比如A,直接print它所有的neighbors呢: +- 如果轮到了B点,那因为是directed,它也不知道A的情况,也不知道改如何继续加,或者下手。 +- 所以,要把所有跟A有关系的点,或者接下去和A的neighbor有关系的点,都放进union-find里面,让这些点有Common parents. +- 最后output的想法: +- 做一个 map 。 +- 之前我们不是给每个num都存好了parent了嘛。 +- 每个num都有个parent, 然后不同的parent就创造一个不同的list。 +- 最后,把Map里面所有的list拿出来就好了。 + + + +--- + +**220. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**221. [Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字, array size = n. 给一串query: 每个query是一个数, 目的找 count# items smaller than query element. + +#### Segment Tree +- 和平时的segment tree问题不同。 [0 ~ n] 代表实际数字: based on real value的segment tree. +- Modify时,把array里面的value带进去,找到特定的位子, 然后count + 1. +- 最终在SegmentTree leaf上面全是array里面实际的数字。 +- node.count: 在node range里面的有多少个数字 + +##### right use of modify() +- build() 只是 empty segment tree, 没有property +- modify() 需要: 1. 找到left, update count+=1; 2. aggregate all parent when after returning +- 所以每一个modify 都是在整个path上所有的node上 + count + +##### query trick +- 在query前,给进去的start和end是: 0 ~ value-1. +- `value-1`: 找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. + +##### About other basic segment tree setup +- [那么其他做过的SegmentTree是怎么样呢?] +- 那些构成好的SegmentTree(找min,max,sum)也有一个Array。但是构成Tree时候,随Array的index而构架。 +- 也就是说,假如有Array[x,y,....]:在leaf,会有[0,0] with value = x. [1,1] with value = y. +- [但是这题] +- 构成时,是用actual value.也就是比如Array[x,y,....]会产生leaf:[x,x]with value = ..; [y,y]with value =... +- 其实很容易看穿: +- 若给出一个固定的array构成 SegmentTree,那估计很简单:按照index从0~array.lengh,leaf上就是[0,0] with value = x. +- 若题目让构造一个空心SegmentTree, `based on value 0 ~ n-1 (n <= 10000)`, 然后把一个Array的value modify 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**222. [My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)** Level: Medium Tags: [Array, TreeMap] + + +Given a list of interval as calendar items. Check if newly added calendar item is overlapping. + +Understand it is only checking time, but not requiring to insert into right spot. No need to overthink. + +#### Simply O(n) check on array +- number of test cases is small, like 1000, so less concern about the time complexity +- simply loop over the list of intervals, and check if any overlapping. +- where to insert does not really matter: every time we are just checking for overlaopping, not merging any range +- **IMPORTANT**: if interval over lapping, they will have this property `Math.max(s1, s2) < Math.min(e1, e2)`. This will help detect the overlapping very easily. +- O(n^2) runtime, with simple code. But somehow this approach is faster than the TreeMap solution: maybe the test cause causes avg O(n)? + +#### TreeMap +- One constraint from the simply array solution: it always cost O(n) to find the potential overlapping interval +- We can manually sort and always manually try to find the correct element via binary search, or we could store the interval in a treeMap, where the intervals are sorted by `start`. +- As result, all we need to do for book(start, end) is to find the next element ceiling(start), last element floor(start), and check for overlapping +- This approach also saves the custom data structure +- Overall cost O(nlogn) + +##### About TreeMap +- always with key sorted ascendingly +- more costly than regular HashMap because of the sorting. Building treemap of n items: O(nlogn) + +#### Sweep line +- use `Point{int start, end; boolean start}` to mark start/end of class. Add to pq. +- Adding new item to pq, sort, and check if overlapping occurs by counting started classes +- If started classes > 1, that means we overlapped. +- Every time it could consume all classes to find the overlap, O(n^2). +- Not quite need to sort or insert at correct point, and this solution requires longer code. Not quite worthy it for a simple problem. + + + + +--- + +**223. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + +**224. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**226. [Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)** Level: Medium Tags: [Heap, MinHeap, PriorityQueue] + + +Same as merge k sorted list, use priorityQueue + +#### Priority Queue +- 由Merge k sorted list启发。用PriorityQueue,存那k个首发element +- PriorityQueue需要存储单位: 自己建一个Class Node 存val, x, y index. +- 因为array里没有 'next' pointer,只能存x,y来推next element +- Not sure why `new PriorityQueue<>(Comparator.comparing(a -> a.val));` is slower + + + +--- + +**227. [Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/Heapify.java)** Level: Medium Tags: [Heap, MinHeap] + + +Turn unsorted array into a min-heap array, where for each A[i], + +A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i]. + +#### Heap +- Heap用的不多. 得用一下, 才好理解. 通常default 的PriorityQueue就是给了一个现成的min-heap: +- 所有后面的对应element都比curr element 小。 +- Heapify里面的**siftdown**的部分: +- 只能从for(i = n/2-1 ~ 0), 而不能从for(i = 0 ~ n/2 -1): 必须中间开花,向上跑的时候才能确保脚下是符合heap规则的 + +#### Heapify/SiftDown做了什么? +- 确保在heap datastructure里面curr node下面的两个孩子,以及下面所有的node都遵循一个规律 +- 比如在这里,若是min-heap,就是后面的两孩子都要比自己大。若不是,就要swap。 + +#### min-heap的判断规律: +- for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. +- siftdown时:在curr node和两个child里面小的比较。如果的确curr < child, 搞定,break while. +- 但若curr 并不比child小,那么就要换位子,而且继续从child的位子往下面盘查。 + + + +--- + +**228. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**229. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**230. [Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)** Level: Medium Tags: [BST, Tree] + + +找 Inorder traversal规则里的下一个. + +主要想法是考虑: + 1. 如果 node.right == null, 找上一个unprocessed node alone the inorder traversal path + 2. 如果 node.right != null, successor 一定在这个node.right那个subtree里面 +最后竟然可以简化成几行, 非常全面的BST问题: 有search, 有对inorder traversal的理解, 还有坑. + +#### Short Recursive and Iterative without Stack +- Previous solution, we use stack to hold previous cached/unprocessed items: but do we need use catch to hold them? +- If moving left: `p.val < root.val`, then root (parent of left child) is a successor candidate, so save `rst = root`. +- If moving right or equal: `p.val >= root.val`, the successor has nothing to do with curr node, so just directly dive into root.right. +- Both iterative and recursive solution can be simplified as such. + + +#### Previous Iterative + stack +- Iteratively search +- Still need stack to store previously unprocessed items along the path + +#### Previous Recursive + Stack +- 画inorder图,发现规律.每个node的后继node(successor)有几种情况: +- 1. node.right 是个leaf到底了。那么就return. +- 2. set rightNode = node.right, 但发现rightNode has a lot left children to leaf. +- 3. 比如, node.right == null, 也就是node自己是leaf,要回头看山顶找Inorder traversal规则里的下一个。 +- 发现:其实就是每层都把路过的curr node放在stack里,最上面的,就是当下改return的那个successor:) Done. + + + +--- + +**231. [Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)** Level: Medium Tags: [BFS, DFS] + + +给一个room 2D grid. 里面有墙-1, 门0, 还有empty space INF(Math.MAX_VALUE). + +对每个empty space而言, fill it with dist to nearest gate. + +#### DFS +- Form empty room: it can reach different gate, but each shortest length will be determined by the 4 directions. +- Option1(NOT applicable). DFS on INF, mark visited, summerize results of 4 directions. +- hard to resue: we do not know the direction in cached result dist[i][j] +- Option2. DFS on gate, and each step taken to each direction will +1 on the spot: distance from one '0'; +- Through dfs from all zeros, update each spot with shorter dist +- Worst time: O(mn), where entre rooms[][] are gates. It takes O(mn) to complete the iteration. Other gates be skipped by `if (rooms[x][y] <= dist) return;` + +#### BFS +- Exact same concept. Init with `Queue queue = new LinkedList()` + + + +--- + +**232. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + +**233. [String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- check sign, leading-0, overall size > 11, check max/min in Long format +- if passed all tests, parseInt() + +#### regular expression +- if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**234. [Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph] + + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes. +- 先能复制多少Node复制多少. 然后把neighbor 加上 +- Use `map` to mark visited + +#### DFS +- Given graph node obj `{val, list of neighbor}`: copy the node and all neighbors +- Mark visited using map +- for loop on the each one of the neighbors: map copy, record in map, and further dfs +- once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +- 主要思想是: 一旦复制过了, 不必要重新复制 + +#### BFS +- Copy the root node, then copy all the neighbors. +- Mark copied node in map. +- Use queue to contain the newly added neighbors. Need to work on them in the future. + + + +--- + +**235. [Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)** Level: Medium Tags: [Backtracking, DFS, Permutation] + + +#### Recursive: Backtracking +- Given a remaining list: 取, 或者不取 +- always iterate over full `nums[]`, use list.contains() to check if item has been added. +- Improvement: maintain list (add/remove elements) instead of 'list.contains' +- time O(n!): visit all possible outcome +- T(n) = n * T(n-1) + O(1) + +#### Iterative: Insertion +- 插入法: +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- 但是比dfs要快, 因该是因为 # of checks 少: 不需要check list.size(), 不需要maintain remaining list. + +#### Previous Notes +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- A bit slower, possibly because of the polling and saving the entire list every time + + + + +--- + +**236. [One Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/One%20Edit%20Distance.java)** Level: Medium Tags: [String] + + +如果S, T只用一个operation就能变成相等, return true. + +#### Edit: 删除,增加,和替换 +- 换完之后,理论上换成的String 就应该全等 +- for loop, 一旦找到不一样的char, 就判断那三种可能性: insert/delete/replace +- insert/delete 对于2个string来说, 效果是类似的 +- O(n) + + + +--- + +**237. [4Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/4Sum.java)** Level: Medium Tags: [Hash Table] + + +#### Based on 2sum +- 1. 利用2Sum的原理,把4Sum分为连个2Sum。左一个pair,右一个pair,每个pair里面放2个数字。 +- 2. 以一个点,i,作为分界口,也要列举出所有i之前的pair,作为基础。 +- 3. 再尝试从所有i+1后面,找合适的2nd pair。 +- Time: O(n^2 * x), where x = # of candidates, still slow +- 可以用HashSet, 可以直接比较list里面每一个元素, 保证set不重复. +- Previous Notes: 在造class Pair时候,要做@override的function: hashCode(), equals(Object d). 平时不太想得起来用。 +- 参见 http://lifexplorer.me/leetcode-3sum-4sum-and-k-sum/ + +#### Based on 3Sum +- 3Sum外面再加一层. 参考3Sum. 时间O(n^3)。 但此方法在k-sum时候,无疑过于费时间. O(n^k) + + + +--- + +**238. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**239. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**240. [The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)** Level: Medium Tags: [BFS, DFS] + + +#### BFS +- BFS on coordinates +- always attempt to move to end of border +- use boolean[][] visited to alingn with BFS solution in Maze II, III, where it uses Node[][] to store state on each item. + + + +--- + +**241. [The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)** Level: Medium Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- if already found a good/shorter route, skip +- `if (distMap[node.x][node.y] <= node.dist) continue;` +- This always terminates the possibility to go return to original route, because the dist will be double/higher + + + +--- + +**242. [Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)** Level: Medium Tags: [DP, MiniMax] + + +Detailed in `Coins in a Line III` + + + +--- + +**243. [Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)** Level: Medium Tags: [Hash Table, String] + + + +#### Convert to orginal string +- shit by offset. `int offset = s.charAt(0) - 'a';` +- increase if less than 'a': `if (newChar < 'a') newChar += 26;` + +#### Previous notes +- 相同shift规则的string, 能被推算到同一个零起始点,就是共同减去一个char,最后就相等。以此作为key,用HashMap。一目了然。 +- 记得根据题目意思,一开始要String[] sort一下。 + + + +--- + +**244. [Delete Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Digits.java)** Level: Medium Tags: [Greedy, Priority Queue] + + +#### Priority Queue +- TODO: parse into node(index, digitValue) +- find the top k, and remove from char array +- O(nlogn) time + +#### Greedy +- 数位靠前的,权值更大. 所以硬来把靠前的相对更大的(跟following digit相比)去掉。 + + + +--- + +**245. [Flatten 2D Vector.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%202D%20Vector.java)** Level: Medium Tags: [Design] + + +Implement an iterator to flatten a 2d vector. + +Just move pointers carefully with next(), hashNext() + +#### Basic Implementation using x, y corrdinate +- 就是把2D list里面的element全部遍历一遍。 +- 跟一个nxn的matrix遍历,是没区别的拉; 所有来个x,y,把2d list跑一变。 + +#### Always return item at index 0, and remove from list? +- list 方便remove, 考虑吧reduce input vector (就像给的是linked list 一样) + + + +--- + +**246. [The Spiral Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Spiral%20Matrix%20II.java)** Level: Medium Tags: [Array] + + +#### Move forward till end +- Similar concept as `The Maze`: keep walking until hit wall, turn back +- fix direction `dx[direction % 4]` + + + +--- + + + + + + + +## Hard (91) +**0. [Count of Smaller Number before itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number%20before%20itself.java)** Level: Hard Tags: [] + +与Count of Smaller Number非常类似。以实际的value来构成segment tree,leaf上存(count of smaller number)。 + +Trick: 先Query,再modify. +每次Query时候,A[i]都还没有加入到Segment Tree 里面,而A[i+1,...etc]自然也还没有加进去。 +那么就自然是coutning smaller number before itself. +刁钻啊! + +另外注意: +在modify里面:多Check了root.start <= index 和 index <= root.end。 过去都忽略了。以后可以把这个也写上。 +(其实是Make sense的,就是更加严格地check了index再 root.left 或者 root.right里面的站位) + + + +--- + +**1. [Kth Smallest Sum In Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Sum%20In%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [] + + +用priority queue. 每次把最小的展开,移位。分别x+1,或者y+1: +因为当下的Min里面x,y都是最小的。所以下一个最小的不是(x+1,y),就是(x,y+1)。 + +每次就poll()一个,放2个新candidate进去就好了。 +注意,这样的做法会用重复,比如例子(7,4)会出现两次。用一个HashSet挡一下。 + +注意,HashSet的唯一性,用一个"x,y"的string就可以代为解决。 + + + +--- + +**2. [LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)** Level: Hard Tags: [Design, Hash Table] + + +#### Hash Table +- 具体看thoughts, 几种不同的方式使用map +- `regular object map`: map of , where `item : {int val; int count}` +- Use a Map to track the frequency +- Track constant capacity, and minimum frequency +- Every get(): update all frequency map as well +- Every put(): update all frequency map as well, with optional removal (if over capacity) + +- Original post: http://www.cnblogs.com/grandyang/p/6258459.html +- TODO: one doubly linked list might be good enough to replace below: +- `frequency list map`: map of >, where the list preserves `recency` +- `item location in frequency map`: map of : +- index relative to the item in a particular list, not tracking which list here + + + +--- + +**3. [Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Prefix%20and%20Suffix%20Search.java)** Level: Hard Tags: [Trie] + + + + +--- + +**4. [Remove Node in Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Node%20in%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST] + + +方法1: Brutle一点。找到target和target的parent. +把target remove时,把target的children nodes 重新排列组成新的BST: inorder traversal, build tree based on inorder traversal list. + +方法2: 分析规律,先找到target和parent, 然后根据性质,把target remove时,移动children nodes, 保证还是BST。 + + + +--- + +**5. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + + +--- + +**6. [k Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/k%20Sum.java)** Level: Hard Tags: [DP] + + +DP. 公式如何想到, 还需要重新理解. + +dp[i][j][m]: # of possibilities such that from j elements, pick m elements and sum up to i. +i: [0~target] + +dp[i][j][m] = dp[i][j-1][m] + dp[i - A[j - 1]][j-1][m-1] + (i not included) (i included) + + + +--- + +**7. [Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)** Level: Hard Tags: [Binary Search, DP, Partition DP] + + +给一串书pages[i], k个人, pages[i] 代表每本书的页数. k个人从不同的点同时开始抄书. + +问, 最快什么时候可以抄完? + +#### Partition DP +- 第一步, 理解题目要求的问题: 前k个人copy完n本书, 找到最少的用时; 也可以翻译成: `n本书, 让k个人来copy, 也就是分割成k段`. +- 最后需要求出 dp[n][k]. 开: int[n+1][k+1]. +- 原理: +- 1. 考虑最后一步: 在[0 ~ n - 1]本书里, 最后一个人可以选择copy 1 本, 2 本....n本, 每一种切割的方法的结果都不一样 +- 2. 讨论第k个人的情况, 在 j = [0 ~ i] 循环. 而循环j时候最慢的情况决定 第k个人的结果(木桶原理): `Math.max(dp[j][k - 1], sum)`. +- 3. 其中: `dp[j][k-1]` 是 [k-1]个人读完j本书的结果, 也就是著名的`上一步`. 这里循环考虑的是第k个人不同的j种上一步 : ) +- 4. 循环的结果, 是要存在 dp[i][k] = Math.min(Math.max(dp[j][k - 1], sum[j, i]), loop over i, k, j = [i ~ 0]) +- Time: O(kn^2), space O(nk) + +##### Init +- Init: dp[0][0] = 0, 0个人0本书 +- Integer.MAX_VALUE的运用: +- 当 i = 1, k = 1, 表达式: dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); +- 唯一可行的情况就只有一种: i=0, k=0, 刚好 0 个人 copy 0 本书, dp[0][0] = 0. +- 其他情况, i = 1, k = 0, 0 个人读 1本书, 不可能发生: 所以用Integer.MAX_VALUE来冲破 Math.max, 维持荒谬值. +- 当 i=0, k=0 的情况被讨论时候, 上面的方程式才会按照实际情况计算出 dp[i][k] +- 这道题的init是非常重要而tricky的 + +##### 计算顺序 +- k个人, 需要一个for loop; +- k个人, 从copy1本书开始, 2, 3, ... n-1,所以 i=[1, n], 需要第二个for loop +- 在每一个i上, 切割的方式可以有[0 ~ i] 中, 我们要计算每一种的worst time + +##### 滚动数组 +- [k] 只有和 [k - 1] 相关 +- Space: O(n) + +#### Binary Search +- 根据: 每个人花的多少时间(time)来做binary search: 每个人花多久时间, 可以在K个人之内, 用最少的时间完成? +- time variable的范围不是index, 也不是page大小. 而是[minPage, pageSum] +- validation 的时候注意3种情况: 人够用 k>=0, 人不够所以结尾减成k<0, 还有一种是time(每个人最多花的时间)小于当下的页面, return -1 +- O(nLogM). n = pages.length; m = sum of pages. + + + + +--- + +**8. [Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)** Level: Hard Tags: [DP, Interval DP, String] + + +- 给两个string S, T. 检验他们是不是scramble string. +- scramble string 定义: string可以被分拆成binary tree的形式, 也就是切割成substring; +- 旋转了不是leaf的node之后, 形成新的substring, 这就是原来string的 scramble. + + +#### Interval DP 区间型 +- 降维打击, 分割, 按照长度来dp. +- dp[i][j][k]: 数组S从index i 开始, T从index j 开始, 长度为k的子串, 是否为scramble string + +##### Break down +- 一切两半以后, 看两种情况: , 或者不rotate这两半. 对于这些substring, 各自验证他们是否scramble. +- 不rotate分割的两半: S[part1] 对应 T[part1] && S[part2] 对应 T[part2]. +- rotate分割的两半: S[part1] 对应 T[part2] && S[part2] 对应 T[part1]. + +##### Initialization +- len == 1的时候, 其实无法旋转, 也就是看S,T的相对应的index是否字符相等. +- initialization非常非常重要. 很神奇, 这个initailization 打好了DP的基础, 后面一蹴而就, 用数学表达式就算出了结果. +- input s1, s2 在整个题目的主要内容里面, 几乎没有用到, 只是用在initialization时候. +- More details, 看解答 + + + +--- + +**9. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**10. [Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + +time: O(MN) +Space: O(N) + +两个字符串, A要变成B, 可以 insert/delete/replace, 找最小变化operation count + +#### Double Sequence +- 考虑两个字符串的末尾index s[i], t[j]: 如果需要让这两个字符一样, 可能使用题目给出的三种operation: insert/delete/replace? +- 先calculate最坏的情况, 3种operation count + 1; 然后在比较match的情况. +- 注意, 在i或者j为0的时候, 变成另外一个数字的steps只能是全变. +- 第一步, 空间时间都是O(MN), O(MN) +- 滚动数组优化, 空间O(N) + +##### Detail analysis +- insert: assume insert on s, `#ofOperation = (s[0 ~ i] to t[0 ~ j-1]) + 1;` +- delete: assume delete on t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j]) + 1;` +- replace: replace both s and t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j - 1]) + 1;` +- dp[i][j]代表了两个 sequence 互相之间的性质: s[0 ~ i] 转换成 s[0~j] 所需要的最少 operation count +- init: 当i==0, dp[0][j] = j; 每次都要 + j 个character; 同理, 当j==0, dp[i][0] = i; +- 而dp[i][j]有两种情况处理: `s[i] == t[j]` or `s[i] != t[j]` + +##### 何时initialize +- 这种判断取决于经验: 如果知道initialization可以再 double for loop 里面一起做, 那么可以留着那么做 +- 这样属于 `需要什么, initialize什么` +- 事后在做space optimization的时候, 可以轻易在 1st dimension 上做rolling array + +#### Search +- 可以做, 但是不建议:这道题需要找 min count, 而不是search/find all solutions, 所以search会写的比较复杂, 牛刀杀鸡. + + + +--- + +**11. [Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)** Level: Hard Tags: [DP, String] + + +Double Sequence DP: +0. DP size (n+1): 找前nth的结果, 那么dp array就需要开n+1, 因为结尾要return dp[n][m] +1. 在for loop 里面initialize dp[0][j] dp[i][0] +2. Rolling array 优化成O(N): 如果dp[i][j]在for loop里面, 就很好替换 curr/prev + + + +--- + +**12. [Ones and Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Ones%20and%20Zeroes.java)** Level: Hard Tags: [DP] + + +还是Double Sequence, 但是考虑第三种状态: 给的string array的用量. +所以开了3维数组. + +如果用滚动数组优化空间, 需要把要滚动的那个for loop放在最外面, 而不是最里面. +当然, 这个第三位define在 dp[][][]的哪个位置, 问题都不大. + +另外, 注意在外面calcualte zeros and ones, 节约时间复杂度. + + + +--- + +**13. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**14. [Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, String, Two Pointers] + + +基本思想: 用个char[]存string的frequency. 然后2pointer, end走到底, 不断validate. +符合的就process as result candidate. + +HashMap的做法比char[]写起来要复杂一点, 但是更generic + + + +--- + +**15. [Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String] + + +大清洗 O(nk) +map.size一旦>k,要把longest string最开头(marked by pointer:start)的那个char抹掉 +一旦某一个char要被清除,所以在这个char 的1st and last appearance之间的char都要被清洗from map + + + + +--- + +**16. [Find Minimum in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Hard Tags: [Array, Binary Search] + + +一个需要严谨思考的题目. 因为有duplicate, 会导致不断平移, 所以最终time complexity是O(n) +所以不如直接扫一遍, 给出答案. + +但是还是写一个Binary Search的样子, 只不过worst结果是O(n) + + + +--- + +**17. [Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands%20II.java)** Level: Hard Tags: [Union Find] + + +给一个island grid[][], and list of operations to fill a particualr (x,y) position. + +count # of remaining island after each operation. + +#### Union Find, model with int[] +- 把board转换成1D array, 就可以用union-find来判断了. +- 用int[] father 的unionFind, 需要转换2D position into 1D index. 这样比较clean +- 判断时,是在四个方向各走一步,判断是否是同一个Land. +- 每走一次operator,都会count++. 若发现是同一个island, count-- +- count的加减, 都放在了UnionFind自己的function里面, 方便tracking, 给几个helper function就对了. +- Time: O(k * log(mn)) + +#### Union Find, model with Hashmap +- 用HashMap的Union-find. + + +#### Note: +- Proof of UnionFind log(n) time: https://en.wikipedia.org/wiki/Proof_of_O(log*n)_time_complexity_of_union%E2%80%93find + + + +--- + +**18. [Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)** Level: Hard Tags: [Backtracking, DFS, Trie] + + +给一串words, 还有一个2D character matrix. 找到所有可以形成的words. 条件: 2D matrix 只可以相邻走位. + +#### Trie, DFS +- 相比之前的implementation, 有一些地方可以优化: +- 1. Backtracking时候, 在board[][] 上面mark就可以, 不需要开一个visited[][] +- 2. 不需要implement trie的所有方程, 用不到: 这里只需要insert. +- 普通的trie题目会让你search a word, 但是这里是用一个board, 看board的每一个字母能不能走出个Word. +- 也就是: 这里的search是自己手动写, 不是传统的trie search() funcombination +- 3. TrieNode里面存在 end的时候存string word, 表示到底. 用完了 word = null, 刚好截断重复查找的问题. + +##### 关于Trie +- Build Trie with target words: insert, search, startWith. Sometimes, just: `buildTree(words)` and return root. +- 依然要对board matrix做DFS。 +- no for loop on words. 直接对board DFS: +- 每一层,都会有个up-to-this-point的string. 在Trie里面check它是不是存在。以此判断。 +- 若不存在,就不必继续DFS下去了。 +- Trie solution time complexity, much better: +- build Trie: n * wordMaxLength +- search: boardWidth * boardHeight * (4^wordMaxLength + wordMaxLength[Trie Search]) + + +#### Regular DFS +- for loop on words: inside, do board DFS based on each word. +- Time cpmplexity: word[].length * boardWidth * boardHeight * (4^wordMaxLength) + +#### Previous Notes +- Big improvement: use boolean visited on TrieNode! +- 不要用rst.contains(...), 因为这个是O(n) 在leetcode还是会timeout(lintcode竟然可以pass)! +- 在Trie search() method 里面,凡是visit过的,mark一下。 + + + + +--- + +**19. [Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)** Level: Hard Tags: [Backtracking, Trie] + + +可以开Trie class, 里面用到TrieNode. 开Trie(words) 可以直接initalize with for loop +TrieNode 里面可以有一个 List startWith: 记录可以到达这个点的所有string: 有点像树形, ancestor形状的存储. + +神操作: +根据square的性质, 如果选中了list of words, 设定 int prefixIndex = list.size(). +取出list里面的所有word[prefixedIndex], 并且加在一起, 就是下一个word candidate的 prefix. + +形象一点: +list = ["ball", "area"]; +prefixIndex = list.size(); ball[prefixIndex] = 'l'; area[prefixIndex] = 'e'; +//then +candidatePrefix = ball[prefixIndex] + area[prefixIndex] = "le"; +这里就可以用到Trie的那个 findByPrefix function, 在每个点, 都存有所有这个点能产生的candidate. +这时, 试一试所有candidate: dfs + +能想到这种倒转的结构来存prefix candidates 在 Trie里面, 这个想法非常值得思考. + + + +--- + +**20. [Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. +#### 方法1 +Array, 维持一个左手最高墙array, 右手最高强array. +对于每个index而言, vertically 能存放的最大水柱, 就是靠左右最高墙决定的: +min(leftHighestWall, rightHighestWall) - currHeight. + +#### 方法2 +方法1上面的优化, two pointer, 还是找左边最高和右边最高. O(1) space. +利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +左边按照maxLeft来计算, 右边按照maxRight来计算. + +#### 方法3 +2 Pointers, 双面夹击: +1. 找中间最高bar的index +2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条。 +3. 每次还要减去block自身的height + +#### 方法4 +主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. + + + + +--- + +**21. [Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)** Level: Hard Tags: [Array, Monotonous Stack, Stack] + + +给n个bar,组成柱状图histogram. 求在这一排柱状图里面可以找到的面积最大的长方形. + +思考: 找长方形面积, 无非是找两个index, 然后底边长度 * height. + +#### Monotonous Stack +- 重点是根据找Histogram里面rectangle的性质, 维持一个单调递增的Stack +- 在loop over indexes的时候: +- 如果高度>= previous peek(), 那么对于那个peek, 就意味着, 往下走, 一直走高嘛, 之前的peek总可以继续抄底 +- 什么时候不能抄底了呢? 就是有一个下降趋势的时候 +- 这时候并不是calculate所有前面的peek, 而是考虑 大于 current height的之前所有的peek. +- 把这些peek到 current height 前一格的rectangle全部找出来: stack.pop() +- 这个stack.pop()的过程里面, 其实没有算上 current height, 因为需要留到下一轮, 把current index加进stack 再说 +- 为什么用stack? 因为需要知道连续递增的peek, stack.peek() O(1), 好用 + 而其实不用stack, 也可以用其他方式记录所有height, 只不过要 O(n)去找peek不方便 + +#### 知识点 +- 理解monotonous stack 是如何被维护的 +- 维护monotonous stack 是题目需要, 而不是stack本身性质, 是一种借助 stack.peek() O(1)的巧妙用法. + + + + +--- + +**22. [Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)** Level: Hard Tags: [Binary Search, DFS, Divide and Conquer] + + +2Dmatrix, 里面的value有一些递增, 递减的特点(细节比较长, 看原题). 目标是找到peak element + +peak: 比周围4个方向的点value大 + +#### DFS + +##### 基本原理 +- 我们不可能一口气准确定位(x,y), 但是我们可以再一个row/col里面, 找到1D array的 peak. +- 根据这个点, 再往剩下两个方向移动 +- 1. 在中间的一行i=midX, 找到peak所在的y. +- 2. 在中间的一列j=midY, 找到peak所在的x. (有可能强势override之前找到的y, 也就是放弃那一行的peak, 在midY上找peak) +- 3. 根据 (x,y) 的4个neighbor check (x,y)是不是 peak, 如果不是, 像更高的位置移动一格 +- 4. 根据之前算的 midX, midY 把board分成4个象限, 在每一份里面再继续找 +- 这个题目LintCode不给做了, 所以思路对的, 但是解答还没有再次验证. + +##### 剪枝/切分象限 +- 每次只是找到一个row/col里面的peak而已! +- 找到这个点, 就等于把board切成了两半. +- 然后, 再跟剩下的相邻的两个位置比较, 就知道了哪里更大, 就去哪里找peak, 也就是又切了第二刀. +- 切第二刀的时候, 也要把(x, y) 移到需要取的象限. 进行DFS +- 根据mid row 切割: +- http://www.jiuzhang.com/solution/find-peak-element-ii/#tag-highlight-lang-java +- http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf + +##### 时间复杂度 +- 每一个level都减一半 +- T(n) = n + T(n/2) = n + n/2 + n/4 + ... + 1 = n(1 + 1/2 + .... + 1/n) = 2n = O(n) + +#### Binary Search +- TODO +- O(nLogN) + + + +--- + +**23. [Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)** Level: Hard Tags: [Hash Table, String, Trie] + + +Obvious的做法是全部试一遍, 判断, 变成 O(n^2) * O(m) = O(mn^2). O(m): isPalindrome() time. + +当然不行了, 那就看是O(nlogN), 还是O(n)? + +#### 方法1: Hash Table + Palindrome的性质. 复合型. +O(mn) + +##### 思路 +- 每一个word, 都可以拆分成 front + mid + end. 如果这个word + 其他word可以组成palindrome,那就是说 +- 砍掉 (mid+end), front.reverse() 应该存在 words[] 里面. +- 砍掉 (front+mid), end.reverse() 应该存在 words[] 里面. +- 我们用HashMap存所有的, 然后reverse, 找配对就好. + +##### Corner case +- 如果有 empty string "", 那么它跟任何一个palindrome word, 都可以配对, 并且根据位置前后变换, 凑成2份 distinct indexes. +- 这样就有了那个 `if (reverseEnd.equals("")) {...}` 的logic. +- 注意: 虽然在处理砍头/砍尾的两个 for loop里面都在根据 empty string 重复记录, + 但因为 "" 自己本身不能作为起点, 所以overall只会在被其他palindrome配对时记录一次. + + +#### 方法2: Trie +还要做一下那. + + + +--- + +**24. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**25. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**26. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**27. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**28. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**29. [Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + +time: O(NK^2): +space: (NK) + +一排n个房子, 每个房子可涂成k种颜色, 涂每个房子的价钱不一样, 用costs[][]表示. + +costs[0][1]表示涂了index是0的房子, 用了color 1. + +规则: 相邻的两个房子不能使同一种颜色 + +求: 最少的cost + +#### DP +- 跟Paint House I 几乎一模一样, 只不过paint color更多了: k colors. +- 先考虑单纯地用dp[i]表示涂前 i 个房子的最小cost +- 但是 dp[i] 和 dp[i-1] 两个index选什么颜色会互相影响, 难讨论, 于是加状态: 序列DP被加了状态变成2D. +- 考虑最后位, 而前一位i-1又被i位的颜色限制, 于是在考虑 min dp[i] 时候, 又多了一层iteration. +- 做dp[i][j]: # cost for 前 i 个房子, 所以要先pick (i-1) 房子的cost, 然后在找出 (i-2)房子的cost +- K种颜色 => O(NK^2) +- 如果不优化, 跟Paint House I 几乎是一模一样的代码 +- Time O(NK^2), space(NK) +- Rolling array: reduce space to O(K) + +#### 注意 +- 序列型dp[i]表示'前i-1个'的结果. 所以dp最好设定为 int[n + 1] size. +- 然而, 颜色在这里是状态, 所以保留在 j: [ 0~k) +- [[8]] 这样的edge case. 跑不进for loop, 所以特殊handle. + +#### Optimization Solution +- Time: O(NK) +- 如果已知每次都要从cost里面选两个不同的最小cost,那么先把最小两个挑出来, 就不必有第三个for loop 找 min +- 每次在数列里面找: 除去自己之外的最小值, 利用最小值/次小值的思想 +- 维持2个最值: 最小值/次小值. +- 计算的时候, 如果除掉的不是最小值的index, 就给出最小值; 如果除掉的是最小值的index, 就给出次小值. +- Every loop: 1. calculate the two min vlaues for each i; 2. calcualte dp[i][j] +- 如何想到优化: 把表达式写出来, 然后看哪里可以优化 +- 另外, 还是可以rolling array, reduce space complexity to O(K) + + + +--- + +**30. [Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)** Level: Hard Tags: [Array, DP, Sequence DP] + + +比stock II 多了一个限制:只有2次卖出机会. + +#### DP加状态 +- 只卖2次, 把买卖分割成5个状态模块. +- 在状态index 0, 2, 4: 没有持有股票. 1. 一直在此状态, max profit不变; 2. 刚卖掉, dp[i][前状态] + profit +- 在状态index 1, 3: 持有股票. 1. 一直在此状态, daily profit. 2. 刚刚买进, 状态改变, 但是没有profit yet: dp[i][前状态] + +##### Partial profit +- 把每天的partial profit (diff)加在一起, 最终的overall profit是一样的. 唯一更好的是, 不需要记录中间买入的时间点. +- 什么时候会积累profit呢? +- 1. 原本就持有股票的, 如果毫无动作, 那么状态不变, 积累profit diff. +- 2. 卖出了股票, 状态改变, 积累profit diff. +- 注意: 只有在状态index: 0, 2, 4, 也就是卖掉股票的时候, 才可以积累profit + +##### Rolling Array +- [i] 只有和 [i-1] 打交道, reduce space +- O(1) space, O(n) time + +#### 找峰头 +- 找峰头;然后往下再找一个峰头。 +- 怎么样在才能Optimize两次巅峰呢?从两边同时开始找Max!(棒棒的想法) +- leftProfit是从左往右,每个i点上的最大Profit。 +- rightProfit是从i点开始到结尾,每个点上的最大profit. +- 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 +- 三个O(n),还是O(n) + + + +--- + +**31. [Best Time to Buy and Sell Stock IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20IV.java)** Level: Hard Tags: [DP, Sequence DP] + + +有int[] price of stock, 最多做 k transactions. 求最大profit. + +#### DP +- 根据StockIII, 不难发现StockIV就是把状态划分为2k+1份. 那么同样的代码, 移植. + +##### 注意1: +- 如果k很大, k>n/2, 那么长度为n的数组里面, 最多也只能n/2个transaction +- 那么题目简化为stockII, 给n数组, 无限次transaction. +- 注意, status的数量是 2k+1 +- Time O(NK), Space O(2k+1) to store the status + +##### 注意2: +- 最后状态是'没有stock'的都该考虑, 做一个 for 循环比较max. +- 当然, 来一个profit variable, 不断比较, 也是可以的. + +#### 方法2 +- (previous notes, 熟练第一种方法的思考就可以) +- 记得要理解:为什么 i-1天的卖了又买,可以和第 i 天的卖合成一次交易? +- 因为每天交易的price是定的。所以卖了又买,等于没卖!这就是可以合并的原因。要对价格敏感啊少年。 +- Inspired from here: http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html + +##### 局部最优解 vs. 全局最优解: +- local[i][j] = max(global[i – 1][j – 1] + diff, local[i – 1][j] + diff) +- global[i][j] = max(global[i – 1][j], local[i][j]) +- local[i][j]: 第i天,当天一定进行第j次交易的profit +- global[i][j]: 第i天,总共进行了j次交易的profit. + +- local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生。 +- 当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff; +- 当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。 +- (Note:在我下面这个solution里面没有省去 +diff) + +- global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况: +- 如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j]; +- 如果第i天有交易(卖出),那么global[i][j]=local[i][j]。 + + + + + +--- + +**32. [Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)** Level: Hard Tags: [Binary Search, Coordinate DP, DP] + + +俄罗斯套娃, 这里用envelope来表现. 给一串array, 每一个[x, y] 是envelope 长宽. [[5,4],[6,4],[6,7],[2,3]]. + +看用这些套娃, 可以最多套几个. + +#### DP: 1D Coordinate +- envelopes没有顺序, 先排序 (主要根据第一个index排序) +- 然后观察: 排序过后, 就变成了1D的坐标动态规划. +- max number 取决于上一个成功Russian doll的 max value + 1 +- 上一个index不知道, 所以遍历找上一个index. +- 当下index i 的状态, 取决于前面index j 的状态, 所以遍历两个index. +- O(n^2)的DP, n = envelopes.length; + +#### DP: 2D Coordinate +- 这个方法是自己想出来的, 但是时间复杂度太大, timeout +- 把envelop标记在2D grid上面, 然后像走机器人一样, 求到最右下角的最大 count max. +- count 当下能存在多少Russian doll +- 两种情况: 当下coordinate 没有target, 当下coordinate有target +- 当下coordinate 没有target: 如同机器人走法, Math.max(dp[i - 1][j], dp[i][j - 1]) +- 当下coordinate 有target: dp[i - 1][j - 1] + dp[i][j] +- timeout: O(n^2), n = largest coordinate. + + + + +--- + +**33. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**34. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**35. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**36. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**37. [Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)** Level: Hard Tags: [DP, Enumeration, Partition DP] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +其中字符可能是 "*", 可以代表 [1 - 9] + +#### DP +- 乘法原理 +- 跟decode way I 一样, 加法原理, 切割点时: 当下是取了 1 digit 还是 2 digits 来decode +- 定义dp[i] = 前i个digits最多有多少种decode的方法. new dp[n + 1]. +- 不同的情况是: 每一个partition里面, 如果有"*", 就会在自身延伸出很多不同的可能 +- 那么: dp[i] = dp[i - 1] * (#variations of ss[i]) + dp[i - 2] * (#variations of ss[i,i+1]) + +##### 特点 +- 枚举的能力: 具体分析 '*' 出现的位置, 枚举出数字, 基本功. +- 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) +- 理解取MOD的原因: 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. +- 枚举好以后, 其实这个题目的写法和思考过程都不难 + + + + +--- + +**38. [Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)** Level: Hard Tags: [DP, Partition DP] + + +给一个String s, 找出最少用多少cut, 使致 切割出的每一个substring, 都是palindrome + +#### Partition DP +- Find minimum cut: 分割型DP +- dp[i]: 最少cut多少刀, 使得前 i 长度的string, 割出来都是palindrome +- 最终要得到 dp[n], 所以 int[n + 1] +- 移动切刀, 看在哪里切, index j in [0 ~ i] +- 考虑[j, i - 1] 是否是回文串, 如果是, 那么: dp[i]= min(dp[i], d[j] + 1). +- note: 估计遍历 j 的时候, 反过来遍历也可以. + +#### 计算Palindrome的优化 +- 利用palindrome的性质, 可以算出 boolean palindrome[i, j]的情况. +- 找一个任意mid point: +- 1. 假设palindrome是奇数长度, 那么 mid 是单独的字符, 而两边的字符 [mid-1], [mid+1] 应该完全相等. +- 2. 假设palindrome是偶数长度, 那么 [mid] 和 [mid + 1] 这样位置的字符应该相等. +- 这样做出来 palindrome[i, j]: 从字符 i 到 字符 j 的 substring 是否是 palindrome +- 这样就给我们的问题合理降维, 目前是time: O(n^2). +- 不然求一次palindrome, 就是n, 会变成O(n^3) + +#### Previous Notes +- Double for loop 检查每种substring string (i~j). 若i,j相邻或者同点,那么肯定isPal;否则,i,j之间的(i+1, j-1)一定得isPal。 +- 看上去,在检查i,j的时候,中间按的(i+1, j-1)怎么可能先知道? 其实不然..在j慢慢长大的时候,所有的0~j的substring都检查过。所以isPal[i+1][j-1]一定是已经知道结果的。 +- okay.那么假如以上任意一种情况成立,也就是说isPal[i][j] == true。那就要判断,切到第一层循环参数j的末尾点时,有多少种切法? +- 想法很顺:我们naturally会想到,把i之前的cut加上i~j之间发生的不就好了。 +- 反正现在j不变,现在就看吧i定在哪里,cut[i - 1]是否更小/最小; 再在cut[i-1]基础上+1就完了。 +- 当然,如果i==0, 而 i~j又是isPal,那没啥好谈的,不必切,0刀。 +- 最终,刷到cut[s.length() - 1] 也就是最后一点。 return的理所应当。 + + + + +--- + +**39. [Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)** Level: Hard Tags: [Backpack DP, DP] + + +给n种不同的物品, int[] A weight, int[] V value, 每种物品可以用无限次 + +问最大多少value可以装进size是 m 的包? + +#### DP +- 可以无限使用物品, 就失去了last i, last unique item的意义: 因为可以重复使用. +- 所以可以转换一个角度: +- 1. 用i **种** 物品, 拼出w, 并且满足题目条件(max value). 这里因为item i可以无限次使用, 所以考虑使用了多少次K. +- 2. K虽然可以无限, 但是也被 k*A[i]所限制: 最大不能超过背包大小. +- dp[i][w]: 前i种物品, fill weight w 的背包, 最大价值是多少. +- dp[i][w] = max {dp[i - 1][w - k*A[i-1]] + kV[i-1]}, k >= 0 +- Time O(nmk) +- 如果k = 0 或者 1, 其实就是 Backpack II: 拿或者不拿 + +#### 优化 +- 优化时间复杂度, 画图发现: +- 所计算的 (dp[i - 1][j - k*A[i - 1]] + k * V[i - 1]) +- 其实跟同一行的 dp[i][j-A[i-1]] 那个格子, 就多出了 V[i-1] +- 所以没必要每次都 loop over k times +- 简化: dp[i][j] 其中一个可能就是: dp[i][j - A[i - 1]] + V[i - 1] +- Time O(mn) + +#### 空间优化到1维数组 +- 根据上一个优化的情况, 画出 2 rows 网格 +- 发现 dp[i][j] 取决于: 1. dp[i - 1][j], 2. dp[i][j - A[i - 1]] +- 其中: dp[i - 1][j] 是上一轮 (i-1) 的结算结果, 一定是已经算好, ready to be used 的 +- 然而, 当我们 i++,j++ 之后, 在之前 row = i - 1, col < j的格子, 全部不需要. +- 降维简化: 只需要留着 weigth 这个 dimension, 而i这个dimension 可以省略: +- (i - 1) row 不过是需要用到之前算出的旧value: 每一轮, j = [0 ~ m], 那么dp[j]本身就有记录旧值的功能. +- 变成1个一位数组 +- 降维优化的重点: 看双行的左右计算方向 +- Time(mn). Space(m) + + + +--- + +**40. [First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Missing%20Positive.java)** Level: Hard Tags: [Array] + + +给一串无序数字, 有负数: 找这个array里面第一个 missing的 positive integer + +missing positive integer 其实是以 [1, n] 来做比较的. + +#### Array分析, index 技巧 +- 用while loop, 不断地尝试把 number 送到该放的地方 +- 如果 index = nums[i] 超过了nums.length, 当然就不移动了 +- 注意: 检查 val != nums[val], avoid infinitely loop +- 检验: nums[i] 是否等于 i, 如果不对, 就找到了结果 + +#### Edge Case +- 如果nums==null, 其实missing positive integer 自然而然是 1 +- validation时, 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) +- 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +- 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + +**41. [N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens.java)** Level: Hard Tags: [Backtracking] + + +N-Queen 问题, 给数字n, 和 nxn board, 找到所有N-queens的答案. + +#### Backtracking +- 用dfs找所有情况, 每一个iteration, 从找一行里挑合适的点, dfs +- 选中的点加进candidate list 里面, 记得要backtracking. +- 每一个candidate都需要validation, 检查 row, col, 2 diagnal 有没有queen + +#### validate n queue at certain (x, y) +- 1. array 里面不能有 target row# +- 2. diagnal. 记得公式: +- row1 - row2 == col1 - col2. Diagnal elelment.fail +- row1 - row2 == - (col1 - col2). Diagnal element. fail +- Draw a 3x3 board to test the 2 scanarios: +- (0,0) and (3,3) are diagnal +- (0,2) and (2,0) are diagnal + + + + +--- + +**42. [N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens%20II.java)** Level: Hard Tags: [Backtracking] + + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + + + +--- + +**43. [LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)** Level: Hard Tags: [Design, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度。 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) + +##### 巧妙点 +- 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +- 2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +- 直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法: +- moveToHead() +- insertHead() +- remove() + +#### O(n) 检查重复 +- timeout method, 天真的来了一个O(n) 的解法,结果果然timeout. +- 一个map存数值。一个queue来存排位。 +- 每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + + + + +--- + +**44. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + +**45. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + +**46. [Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)** Level: Hard Tags: [Array, Hash Table, Union Find] + + +给一串数字, unsorted, 找这串数字里面的连续元素序列长度 (consecutive序列, 是数字连续, 并不是说要按照原order) + +#### HashSet +- 要想看连续元素, 必须要num++, num--这样搜索 +- 1. 需要O(1)找到元素 +- 2. 需要简单快速找到 num - 1, num + 1. +- 如果用min,max开array, 耗费空间 +- 用HashSet来存, 用set.contains() 来查找 num - 1, num + 1 存在与否 +- for loop. O(n) +- 里面的while loop 一般不会有O(n); 一旦O(n), 也意味着set 清零, for loop也不会有更多 inner while 的衍生. +- overall O(n) 时间复杂度 + + +#### Union Find +- 最终是要把相连的元素算一下总长, 其实也就是把元素group起来, 相连的group在一起, 于是想到UnionFind +- 这里用到了一个`int[] size` 来帮助处理 `合并的时候parent是哪个`的问题: 永远往group大的union里去 +- main function 里面, 有一个map来track, 每个元素, 只处理1遍. +- union的内容: current number - 1, current number + 1 +- https://www.jianshu.com/p/e6b955ca208f + +##### 特点 +- Union Find 在index上做好像更加容易 +- 其他union find function: `boolean connected(a,b){return find(a) == find(b)}` + + + +--- + +**47. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**48. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**49. [Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)** Level: Hard Tags: [Greedy, Hash Table, Stack] + + +#### Hash Table, Greedy +- count[] = int[256], 不需要 `c-'a'` +- boolean visited[]: 一旦一个字母固定了位置后, 再次遇到时候, 直接跳过用过的character +- 如果tail字母可以变小, 那就delete掉tail, 重新接上新字母 (前提条件: 去掉的字母后面还会再出现, set visited[tail] = false) +- Space: O(1) count[], visited[]. +- Time: Go through all letters O(n) + +#### Stack +- Use stack instead of stringBuffer: keep append/remove last added item +- However, stringBuffer appears to be faster than stack. + + + +--- + +**50. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**51. [Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort] + + +#### Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn) + + +#### Basic Implementation +- 这里已经给了 **sorted** intervals by start point. +- 直接找到可以insert newInterval的位子. Insert +- 然后loop to merge entire interval array +- 因为给的是个list, 所以方便`intervals.remove(i)` +- remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture +- O(n) + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**52. [Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)** Level: Hard Tags: [KMP, String] + + +#### Divide by mid point, Brutle +- check (mid, mid+1), or (mid-1, mid+1). +- If the two position matches, that is a palindrome candidate +- 比较front string 是否是 end string 的substring +- O(n^2) +- timeout on last case: ["aaaaaa....aaaacdaaa...aaaaaa"] + +#### KMP +- TODO + + + +--- + +**53. [K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)** Level: Hard Tags: [Array, BST, TreeSet] + + +题目解析后: find 2 number, that: 1. k slots between the 2 number, 2. no slots taken between the two number. + +#### BST +- BST structure not given, use TreeSet to build BST with each node +- Every time find last/next inorder element +- `treeSet.lower(x)`, `treeSet.higher(x)` +- 一旦位置相隔(k + 1), 就满足题目条件 +- O(nlogn), good enough + +#### Track slots of days +- Reverse the array, save days index into days[], where the new index is slot. +- days[i]: at slot i, which day a flower will be planted +- O(n) +- Needs to understand: http://www.cnblogs.com/grandyang/p/8415880.html + + + +--- + +**54. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**55. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**56. [Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)** Level: Hard Tags: [Design, Geometry, Hash Table] + + +看的list of coordinates 是否能组成perfect rectangle, 并且不允许overlap area. + +#### 画图发现特点 +- 特点1: 所有给出的点(再找出没有specify的对角线点), 如果最后组成perfect rectangle, 都应该互相消除, 最后剩下4个corner +- 特点2: 找到所有点里面的min/max (x,y), 最后组成的 maxArea, 应该跟过程中accumulate的area相等 +- 特点1确保中间没有空心的部分, 保证所有的重合点都会互相消除, 最后剩下4个顶点 +- 特点2确保没有重合: 重合的area会最终超出maxArea + + + +--- + +**57. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + +**58. [Number of Digit One.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Digit%20One.java)** Level: Hard Tags: [Math] + + +Pure math problem, not quite representative + +Explanation +https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython + + + +--- + +**59. [Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)** Level: Hard Tags: [Bit Manipulation, String] + + +#### String +- 首先要分两半解决,断点是'.': str.split("\\."); +- Integer那一半好弄,whie loop里: num%2, num/2. 做一个 `parseInteger()` function +- Decimal那边复杂点. 做一个 `parseDecimal()` function: +- bit == 1的数学条件: 当下num * 2 >= 1。 更新: num = num * 2 - 1; +- bit == 0的数学条件: num * 2 < 1. 更新: num = num * 2 + +#### 注意 +- num是 double, 小数在 `num = num * 2 - 1` 的公式下可能无限循环 +- 因此check: num重复性,以及binary code < 32 bit. +- 所以题目也才有了32BIT的要求! + + + +--- + +**60. [Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST, DFS, Tree] + + +BST里面有2个node misplace, 要归为. 要求: O(1) extra space + +#### Observation +- BST inorder traversal should give small -> large sequence +- misplaced means: a **large**->small item would occur, and later a large>**small** would occur. +- The first large && second small item are the 2 candidates. Example +- [1, 5, 7, 10, 12, 15, 18] +- [1, 5, `15, 10`, `12, 7`, 18] + +#### dfs, O(1) extra space +- traverse, and take note of the candidate +- at the end, swap value of the 2 candidates + +#### O(n) space +- inorder traversal the nodes and save in array, find the 2 items misplanced and swap them +- But O(n) space should not be allowed + + + + +--- + +**61. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**62. [Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)** Level: Hard Tags: [Coordinate DP, Stack, String] + + +给一串string, 里面只有`(`, `)`. 找最长valid parentheses 的长度. + +#### 1D Coordinate DP +- use dp[i] track local max, maintain global max +- int[] dp. dp[i]: longest valid string that ends on i. +- 结尾是 ')', 2种情况: 1. 刚好s[i-1]是'('; 2. s[i]的')'更前面的一个起始'(' 对应 +- 注意, 结尾如果是'('属于不合理情况, 忽略. +- init: dp[0] = 0, 单个char不可能成型. +- 计算顺序: 从左到右, 找local max, maintain global max +- O(n) space, O(n) runtime + +#### Stack +- Stack 里面存所有的open/close parentheses. +- 如果遇到stack.top()刚好开合结掉, 就stack.pop(). +- 剩下的都是不合理的elements. +- 有点像negatively找 solution: `endIndex - 最后一个failedIndex(stack.pop()) - 1`, 应该就是最后一个succeeded string的长度 +- 每次更新 endIndex 为stack.top(), 然后从stack继续找下一个failedIndex +- 所有的length作比较, 就可以找出最长length +- O(n) stack space, O(n) runtime. 应该比dp慢一点, 因为做了2遍O(n) + + + + +--- + +**63. [Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)** Level: Hard Tags: [Greedy, Hash Table, Heap] + + +给一个string, 全是lowercase letter, 要求重新排列: 然后每个unique的character要有k distance apart. + +跟Task Scheduler有点像, 只不过Task那道题里面还可以用其他方法求count, 这道题要求出排列结果 + +#### PriorityQueue + HashTable +- PriorityQueue排序+分布排列的一个经典用法. +- Count frequency and store in pq. +- Consume element of pq for k rounds, each time pick one element from queue. +- Exception: if k still has content but queue is consumed: cannot complete valid string, return ""; +- space, O(n) extra space in sb, O(26) constant space with pq. +- time: O(n) to add all items + + + +--- + +**64. [Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + +time: O(n) + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**65. [Bricks Falling When Hit.java](https://github.com/awangdev/LintCode/blob/master/Java/Bricks%20Falling%20When%20Hit.java)** Level: Hard Tags: [Union Find] + + +给一个matrix of 1 and 0, `1` 代表brick. 连着ceiling的brick就不会drop. 给一串coordinate hits[][], 记录每次take down 1 brick 后, 会drop多少个. + +#### UnionFind +- 1. 我们知道大部分的brick可能都是连着ceiling, 所以每次正向检查都traverse all and timeout +- 2. 能否用union, 把connect都装在一起, 然后drop brick的时候把连着的都drop掉? 难: 因为还是要check所有brick当下的status. +- 受其他人的解答启发, 由于是计算count,我们可以`反向考虑`: +- 把hit-brick全部mark=2 (就当舍弃不算), 观察整个局面的最后一步, 先把所有还连着ceiling的brick算一下总数, 统计在unionFind的 全部统计在count[0] 里面. +- 剩下的不连着ceiling的也就是一个个isolated island +- 做法: 把hit-brick 一个个加回去, 然后再做一次union, 看看最终连到ceiling的有多少个. 增加的count, 就是正向思考时 dropped brick 数量! + +##### Union Find 变种 +- 还是用数字index做union find, 但是把每一个index都+1, 右移一位, 而[0]留下来做特殊用途: +- 用union at 0来 统计总共的remain count of ceiling-connected bricks, where `x = 0`. +- 如果在其他其他题目种, 条件可能就不是`x=0`, 但也可以用这个 union index = 0 来做一个root的统计 +- 关键: 把最后一个hit brick加回去, 然后再重新union一下这个hit-brick周围: count增加的变化, 不就是缺少hit-brick时候掉下去的数量. + + +#### DFS (timeout) +- 考虑每个hit的四周, 全部traverse, 没有连着ceiling就全部: +- 比如是 200 x 200 的 全部是1的matrix, 任何一次traverse都要到顶; 重复计算, 所以timeout +- 算法是没错, 但是不efficient. +- 想要减少重复计算, 但是又不能提前计算: grid在不断变化. 所以看能不能把连着ceiling的都group起来, 可以O(1)快速check? + + + + +--- + +**66. [Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)** Level: Hard Tags: [Binary Search, Lint, Segment Tree] + + +SegmentTree大集合. Methods: `build, query, modify`. 不难。只是要都记得不犯错. + +#### Segment Tree +- build: recursively build children based on index-mid and link to curr node +- query: recursively try to find `node.start == targetStart && node.end == targetEnd`. Compare with node.mid +- modify: recursively try to find `node.start == targetStart && node.end == targetEnd`; modify and recursively assign upper interval with updated interval property. + + + +--- + +**67. [HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/HashHeap.java)** Level: Hard Tags: [HashHeap, Heap] + + +非题.是从九章找来的HashHeap implementation. + + + +--- + +**68. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**69. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**70. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**71. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + +**72. [Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)** Level: Hard Tags: [Enumeration, Math, String] + + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 + +#### 注意 +- StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 +- 注意加 " " 的时候, 如果多余, 要`trim()` +- 注意, 小于20的数字, 有自己的特殊写法, 需要额外handle +- 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 + + + + +--- + +**73. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**74. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**75. [Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- Summing space = `width + (size-1)`. maintain: 1. list of candidates, 2. width of actual words +- calculate space in between: `remain/(size - 1)` +- overall for loop; clean up list: 1. over size; 2. last item +- 一点也不难, 但是要小心: deal with list of string的时候, 注意处理干净sum size of list, 就行了. +- `干净处理space`: 只处理 (n-1) items, 然后最后一个拿到for loop 外面, 特殊处理. + +#### Notes +- Clarification, observation: +- can start with greedy approach to stack as many words as possible +- once exceed the length, pop the top, and justify the added words (untouched words tracked by index) +- left justify: given list/stack of words with size t, overall remaining space length m, +- deal with last line with special care: just fill one space, and fill the rest of the row with space +- Does not seem very complicated, but need additional care of calculating the amount of space needed. +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**76. [Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)** Level: Hard Tags: [Enumeration, String] + + +Read N Character using `Read4(char[] buf)` 的加强版: 可以不断读 read(buf, n) + +#### String +- 注意String的index handle, 慢慢写edge case +- 理解题目意思: `read4(char[] buf)` 这样的 `populate input object` 的function稍微少一点. +- 遇到时候, 仔细理解function用法, 不要慌乱. 其实思考方式很简单, 仔细handle string 还有 edge case就好了. + + + +--- + +**77. [Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)** Level: Hard Tags: [DP, Hash Table] + + +Frog jump 的题目稍微需要理解: 每个格子可以 jump k-1, k, k+1 steps, 而k取决于上一步所跳的步数. 默认 0->1 一定是跳了1步. + +注意: int[] stones 里面是stone所在的unit (不是可以跳的步数, 不要理解错). + +#### DP +- 原本想按照corrdiante dp 来做, 但是发现很多问题, 需要track 不同的 possible previous starting spot. +- 根据jiuzhang答案: 按照定义, 用一个 map of > +- 每次在处理一个stone的时候, 都根据他自己的 set of , 来走下三步: k-1, k, or k+1 steps. +- 每次走一步, 查看 stone + step 是否存在; 如果存在, 就加进 next position: `stone+step`的 hash set 里面 + +##### 注意init +- `dp.put(stone, new HashSet<>())` mark 每个stone的存在 +- `dp.get(0).add(0)` init condition, 用来做 dp.put(1, 1) + +##### 思想 +- 最终做下来思考模式, 更像是BFS的模式: starting from (0,0), add all possible ways +- 然后again, try next stone with all possible future ways ... etc + + + +--- + +**78. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**79. [Shortest Distance from All Buildings.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Distance%20from%20All%20Buildings.java)** Level: Hard Tags: [BFS] + + +给Walls and Gates很像, 不同的是, 这道题要选一个 coordinate, having shortest sum distance to all buildings (marked as 1). + +#### BFS +- BFS 可以 mark shortest distance from bulding -> any possible spot. +- Try each building (marked as 1) -> BFS cover all 0. +- time: O(n^2) * # of building; use new visited[][] to mark visited for each building. +- O(n^2) find smallest point/aggregation value. +- 注意, 这道题我们update grid[][] sum up with shortest path value from building. +- 最后找个min value 就好了, 甚至不用return coordinate. +- 分析过, 还没有写. + + + +--- + +**80. [Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Deque, Monotonous queue +- 维持monotonuous queue: one end is always at max and the other end is min. Always need to return the max end of queue. +- when adding new elements x: start from small-end of the queue, drop all smaller elements and append to first element larger than x. +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`. +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! + + + +--- + +**81. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + +**82. [Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)** Level: Hard Tags: [BFS] + + + + +--- + +**83. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + + +--- + +**84. [Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)** Level: Hard Tags: [DFS, Greedy, Math] + + +#### Greedy, Iterative +- For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +- We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +- Track the newly formed string; if new, add the new char to overall result +- Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] +- Same concept as dfs + +#### DFS +- Same concept: use window to cut out tail, and append with new candidate +- do this for k^n = Math.pow(k, n) times + + + +--- + +**85. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + +**86. [The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)** Level: Hard Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- 跟 Maze I, II 类似, 用一个 Node[][] 来存每一个(x,y)的state. +- Different from traditional BFS(shortest path): `it terminates BFS when good solution exists (distance), but will finish all possible routes` +- 1. `Termination condition`: if we already have a good/better solution on nodeMap[x][y], no need to add a new one +- 2. Always cache the node if passed the test in step1 +- 3. Always offer the moved position as a new node to queue (as long as it fits condition) +- 4. Finally the item at nodeMap[target.x][target.y] will have the best solution. + + + +--- + +**87. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**88. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + +**89. [Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)** Level: Hard Tags: [Backtracking, DFS] + + +#### DFS +- Different from regular dfs to visit all, the robot move() function need to be called, backtrack needs to move() manually and backtracking path shold not be blocked by visited positions +- IMPORTANT: Mark on the way in using set, but `backtrack directly without re-check against set` +- Mark coordinate 'x@y' +- Backtrack: turn 2 times to revert, move 1 step, and turn 2 times to revert back. +- Direction has to be up, right, down, left. +- `int [] dx = {-1, 0, 1, 0};`, `int[] dy = {0, 1, 0, -1};` + + + +--- + +**90. [Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)** Level: Hard Tags: [DP] + + + + +--- + + + + + + + +## Review (5) +**0. [Maximum Subarray III.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20III.java)** Level: Review Tags: [] + + + +--- + +**1. [Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Perfect%20Square.java)** Level: Review Tags: [Binary Search, Math] + + +Binary找sqrt. 基本 mid+1, mid-1 template. +注意: define index as long. + + + +--- + +**2. [Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)** Level: Review Tags: [Array, Binary Search, PreSum] + + +给int[] nums 和 window min size k. window size可以大于K. 找最大的连续数列average value. + +- Binary Search的思想, 用在所要找的这个 average sum 上面. 大小是在[min, max]之中 +- 找k的时候, 是>=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + + + +--- + +**3. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**4. [Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)** Level: Review Tags: [BFS, DFS, DP] + + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- in dfs: remove the incorrect parentheses one at a time +- detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` +- once detected, remove the char from middle of s, and dfs on the rest of the s that has not been tested yet. + +##### Core concept: reverse test +- `if a parenthese string is valid, the reverse of it should also be valid` +- Test s with open='(', close=')' first; **reverse s**, and test it with open=')', close='(' + +##### Minor details +- only procceed to remove invalid parenthese when `count<0`, and also break && return dfs after the recursive calls. +- The above 2 facts eliminates all the redundant results. +- Reverse string before alternating open and close parentheses, so when returning final result, it will return the correct order. +- Open questions: how does it guarantee minimum removals? + +##### Backtracking +- 如果用stringbuffer, 那么久不会每次create new string, 但是需要maintain这个string buffer, 就会backtracking + +##### Complexity +- Seems to be O(n), but need to derive + +#### BFS +TODO + +#### DP + + + +--- + + + + diff --git a/Others/CheckClock.java b/Others/CheckClock.java new file mode 100644 index 0000000..cda236b --- /dev/null +++ b/Others/CheckClock.java @@ -0,0 +1,87 @@ +import java.io.*; +import java.util.*; + +/* + R.L. + 12 spots to fill 4P, 4N, 4D. make sure they are not jumping onto each other. + P: move 1 step. + N: 5. + D: 10 + */ + +// +class Solution { + + // Generate all valid 12-length strings + public static void generateAll(ArrayList solutions, String current, int p, int n, int d) { + if (current.length() == 12) { + solutions.add(current); + return; + } + + if (p > 0) generateAll(solutions, current + "P", p - 1, n, d); + if (n > 0) generateAll(solutions, current + "N", p, n - 1, d); + if (d > 0) generateAll(solutions, current + "D", p, n, d - 1); + } + + + // Return true whether the string solution is valid, or false otehrwise + public static boolean isValid(String solution) { + if (solution == null || solution.length() != 12) + return false; + + boolean[] clock = new boolean[12]; + int pointer = 0; + + for (int c = 0; c < 12; c++) { + if (clock[pointer]) + return false; + + int advance = 0; + switch (solution.charAt(c)) { + case 'P': advance = 1; break; + case 'N': advance = 5; break; + case 'D': advance = 10; break; + } + + clock[pointer] = true; + pointer = (pointer + advance) % 12; + } + + for (int c = 0; c < 12; c++) { + if (!clock[c]) + return false; + } + return true; + } + + + // Generate all valid strings, then filter them out and only print the valid ones + public static void printSolution() { + ArrayList solutions = new ArrayList(); + generateAll(solutions, "", 4, 4, 4); + + for (String s : solutions) { + if (isValid(s)) + System.out.println(s); + } + } + + + + public static void main(String[] args) { + printSolution(); + + /* + ArrayList rst = validateClock(); + System.out.println(rst.size()); + for (String string : rst) { + System.out.println(string); + } + */ + } +} + + + +///Generate all possible solutions, then validate them all. \ No newline at end of file diff --git a/Others/HackerrankNote.md b/Others/HackerrankNote.md new file mode 100755 index 0000000..6381dc3 --- /dev/null +++ b/Others/HackerrankNote.md @@ -0,0 +1,9 @@ +Notes: +System ouput: What out for format with space: " " +Don't println() when print() is needed. + Or: can choose to build a String rst, and print at the end. + +Be careful with root node : ) always + +Safe way to do LCA on Binary Search Tree: traditional way of 2 lists + diff --git a/Others/Insert a node into a sorted doubly linked list.java b/Others/Insert a node into a sorted doubly linked list.java new file mode 100755 index 0000000..d8434ce --- /dev/null +++ b/Others/Insert a node into a sorted doubly linked list.java @@ -0,0 +1,70 @@ +E + +Hackerrank. 注意处理null case. 找第一个比curr node 大的 next node. + +``` + +/* +You’re given the pointer to the head node of a sorted doubly linked list and an integer to insert into the list. Create a node and insert it into the appropriate position in the list. The head node might be NULL to indicate that the list is empty. + +Input Format +You have to complete the Node* SortedInsert(Node* head, int data) method which takes two arguments - the head of the sorted, doubly linked list and the value to insert. You should NOT read any input from stdin/console. + +Output Format +Create a node with the given data and insert it into the given list, making sure that the new list is also sorted. Then return the head node of the updated list. Do NOT print anything to stdout/console. + +Sample Input + +NULL , data = 2 +NULL <-- 2 <--> 4 <--> 6 --> NULL , data = 5 + +Sample Output + +NULL <-- 2 --> NULL +NULL <-- 2 <--> 4 <--> 5 <--> 6 --> NULL +Explanation +1. We have an empty list, 2 is inserted. +2. Data 5 is inserted such as list remains sorted. +*/ + +/* + Insert Node at the end of a linked list + head pointer input could be NULL as well for empty list + Node is defined as + class Node { + int data; + Node next; + Node prev; + } +*/ +//If head and tail both null, just insert to head.next +//Else, insert based on data +Node SortedInsert(Node head,int data) { + Node node = new Node(); + node.data = data; + if (head == null && head.next == null) { + node.prev = head; + node.next = head.next; + head.next = node; + return head; + } + + Node fast = head; + while (fast.next != null && node.data > fast.next.data) { + fast = fast.next; + } + if (fast.next == null) { + fast.next = node; + node.prev = fast; + node.next = null; + } else { + Node next = fast.next; + fast.next = node; + node.prev = fast; + node.next = next; + next.prev = node; + } + return head; +} + +``` \ No newline at end of file diff --git a/Others/Matching Same Text Again & Again.java b/Others/Matching Same Text Again & Again.java new file mode 100644 index 0000000..e63f4ce --- /dev/null +++ b/Others/Matching Same Text Again & Again.java @@ -0,0 +1,88 @@ +E + +后面需要重复的pattern,用括号包起来。 + +``` +/* +Hackerrank +https://www.hackerrank.com/contests/regular-expresso/challenges/matching-same-text-again-again +Matching Same Text Again & Again + +\𝗀𝗋𝗈𝗎𝗉_𝗇𝗎𝗆𝖻𝖾𝗋\group_number + +This tool (\1 references the first capturing group) matches the same text as previously matched by the capturing group. + +ach18.png +In the above image, the Regex Pattern is matched with the Test String. +In the above image, the Regex Pattern is matched with the Test String. + +For Example: + +(\d)\1: It can match 00, 11, 22, 33, 44, 55, 66, 77, 88 or 99. + +Task + +You have a test string SS. +Your task is to write a regex that will match SS with the following conditions: + +SS must be of length: 20 +1st1st character: lowercase letter. +2nd2nd character: word character. +3rd3rd character: whitespace character. +4th4th character: non word character. +5th5th character: digit. +6th6th character: non digit. +7th7th character: uppercase letter. +8th8th character: letter (either lowercase or uppercase). +9th9th character: vowel (a, e, i , o , u, A, E, I, O or U). +10th10th character: non whitespace character. +11th11th character: should be same as 1st character. +12th12th character: should be same as 2nd character. +13th13th character: should be same as 3rd character. +14th14th character: should be same as 4th character. +15th15th character: should be same as 5th character. +16th16th character: should be same as 6th character. +17th17th character: should be same as 7th character. +18th18th character: should be same as 8th character. +19th19th character: should be same as 9th character. +20th20th character: should be same as 10th character. +Note + +This is a regex only challenge. You are not required to write code. +You have to fill the regex pattern in the blank (_________). + +*/ + +import java.io.*; +import java.util.*; +import java.text.*; +import java.math.*; +import java.util.regex.*; + + +public class Solution { + + public class Solution { + + public static void main(String[] args) { + + Regex_Test tester = new Regex_Test(); + tester.checker("([a-z])(\\w)(\\s)(\\W)(\\d)(\\D)([A-Z])([a-zA-Z])([a|e|i|o|u|A|E|I|O|U])(\\S)\\1\\2\\3\\4\\5\\6\\7\\8\\9\\10"); // Use \\ instead of using \ + + } + } +} + +class Regex_Test { + + public void checker(String Regex_Pattern){ + + Scanner Input = new Scanner(System.in); + String Test_String = Input.nextLine(); + Pattern p = Pattern.compile(Regex_Pattern); + Matcher m = p.matcher(Test_String); + System.out.println(m.find()); + } + +} +``` \ No newline at end of file diff --git a/Others/MorseCode.java b/Others/MorseCode.java new file mode 100644 index 0000000..00b2649 --- /dev/null +++ b/Others/MorseCode.java @@ -0,0 +1,122 @@ +想法: +1. 只关注正数,找一个分割点,开始查看能不能有valid的解。 +2. 过程中,把正数可能有的所有正数的解都找出来,可能存为一个ArrayList letters in morse format: +String 是 "... --- ..." 每个字母之间有空格分开。 +这里可能生成所有possibilities。 其中有很多possibility在结合了负数后,可能就会不成立。 + +3. loop through arraylist: letters +根据每个字母,以及每个字母后面的停顿,来尝试跟所有的负数,分段比较。 +比如第一个字母:[. -x . -y . -z] z 肯定比x 和y都大; 而SOS后面的空出来的负数时间,应该比这个词里面的所有负数绝对值都大。 + +这样就把ArrayList Letters 里面的很多可能性都灭掉。出一个或者多个结果~ + + + +当然:这里负数只做了最终判断的作用,而一开始正数出的结果可能会非常耗时。 +而且假设了分段判断的规律,而不假设全局的规律。(好像更切合实际)。 + + +``` +//Trie + + +import java.io.*; +import java.util.*; + +/* + * To execute Java, please define "static void main" on a class + * named Solution. + * + * If you need more classes, simply define them inline. + */ + + +/* +Double[] = {0.15, -0.12, 0.16, -0.1, 0.17, -0.3} +pos: . - time +neg: empty time + + morseCodeDict["A"] = ".-" + morseCodeDict["B"] = "-..." + morseCodeDict["C"] = "-.-." + morseCodeDict["D"] = "-.." + morseCodeDict["E"] = "." + morseCodeDict["F"] = "..-." + morseCodeDict["G"] = "--." + morseCodeDict["H"] = "...." + morseCodeDict["I"] = ".." + morseCodeDict["J"] = ".---" + morseCodeDict["K"] = "-.-" + morseCodeDict["L"] = ".-.." + morseCodeDict["M"] = "--" + morseCodeDict["N"] = "-." + morseCodeDict["O"] = "---" + morseCodeDict["P"] = ".--." + morseCodeDict["Q"] = "--.-" + morseCodeDict["R"] = ".-." + morseCodeDict["S"] = "..." + morseCodeDict["T"] = "-" + morseCodeDict["U"] = "..-" + morseCodeDict["V"] = "...-" + morseCodeDict["W"] = ".--" + morseCodeDict["X"] = "-..-" + morseCodeDict["Y"] = "-.--" + morseCodeDict["Z"] = "--.." + + morseCodeDict["1"] = "-----" + morseCodeDict["2"] = ".----" + morseCodeDict["3"] = "..---" + morseCodeDict["4"] = "...--" + morseCodeDict["5"] = "....-" + morseCodeDict["6"] = "....." + morseCodeDict["7"] = "-...." + morseCodeDict["8"] = "--..." + morseCodeDict["9"] = "---.." + morseCodeDict["0"] = "----." + +Example: +[0.0450931929901705, -0.0901581814005595, 0.0678896922281069, -0.121032138856854, 0.152995205350165, -0.276777733213167, 0.275236261113044, -0.0919186682910514, 0.347645293572589, -0.125459771265131, 0.342321600880377, -0.363701690087054, 0.102393076997638, -0.117040938252157, 0.114223775510942, -0.0895479217582997, 0.0973858877985071] = SOS ...---... + +Thoughts: +Pos: +Two sets: dot or dash. +Negative: +Three sets: time between morse code, time between letter, time between words. + +Pick a pos time for '.', pick a negtive time for time between morse code, then start DFS. + +For initial judgement: +Pos: +Sort all postive number, let the smallest be '.' and the rest to be '-'. try it out. If not working, move the threshold. + +Negative: +Sort by absolute value. +Assume all letters are the shortest one morse code. So the set for 'time between word' will have most of number. + The set for 'time between letter' will be empty + The set for 'time between morse code' will be empty +If not working, move number around the tree sets, and do dfs. + +The possible solution is to try all posibility with dfs, and cut it off when it seems not valid. + + + +Thought2: +Ignore negative for now. Try to use same method for positive number to try out all posibilities with postiive numbers. +Use the results to match negative numbers, see if the time pulse is valid. +If match, return all posibilities. + +*/ +class Solution { + public static void main(String[] args) { + ArrayList strings = new ArrayList(); + strings.add("Hello, World!"); + strings.add("Welcome to CoderPad."); + strings.add("This pad is running Java 8."); + + for (String string : strings) { + System.out.println(string); + } + } +} + +``` \ No newline at end of file diff --git a/Others/README.md b/Others/README.md new file mode 100755 index 0000000..43da067 --- /dev/null +++ b/Others/README.md @@ -0,0 +1,5 @@ +# Other Problems + +This section captures other programs that does not appear in LintCode. There might not be a clear solution to problems here, but here we'll try to capture some 'how to' or good thoughts. + +And, due to the non-consistency, there won't be auto-generated list view : ). \ No newline at end of file diff --git a/Others/RemoveNodeFromLinkedList.java b/Others/RemoveNodeFromLinkedList.java new file mode 100644 index 0000000..ebd0c86 --- /dev/null +++ b/Others/RemoveNodeFromLinkedList.java @@ -0,0 +1,22 @@ +//Remove node from linkedlist + //H L L L 0 + Node current = head;//L + Node node = current; + Node prev = new Node('0'); + prev.next = node; + while (current != null) {//1L + + while (node.next != null) {//21L + if (current.value == node.next.value ) { + prev.next = prev.next.next; + } + prev = prev.next; + node = node.next; + + } + prev = current; + current = current.next; + node = current; + } + return head; + \ No newline at end of file diff --git a/Others/Solution$TreeNode.class b/Others/Solution$TreeNode.class new file mode 100644 index 0000000..d9e0891 Binary files /dev/null and b/Others/Solution$TreeNode.class differ diff --git a/Others/Solution.class b/Others/Solution.class new file mode 100644 index 0000000..7aa4049 Binary files /dev/null and b/Others/Solution.class differ diff --git a/Others/Solution.java b/Others/Solution.java new file mode 100644 index 0000000..5d5224b --- /dev/null +++ b/Others/Solution.java @@ -0,0 +1,124 @@ +import java.io.*; +import java.util.*; +import java.text.*; +import java.math.*; +import java.util.regex.*; +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + + //BFS. store as string, separated by ',' +class Solution { + class TreeNode { + public int val; + public TreeNode left, right; + public TreeNode(int val) { + this.val = val; + this.left = this.right = null; + } + } + + /** + * This method will be invoked first, you should design your own algorithm + * to serialize a binary tree which denote by a root node to a string which + * can be easily deserialized by your own "deserialize" method later. + */ + public String serialize(TreeNode root) { + String rst = ""; + if (root == null) { + return rst; + } + Queue queue = new LinkedList(); + queue.offer(root); + int size = 0; + while (!queue.isEmpty()) { + size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + rst += node.val + ","; + if (node.left == null) { + rst += "#,"; + } else { + queue.offer(node.left); + } + if (node.right == null) { + rst += "#,"; + } else { + queue.offer(node.right); + } + + } + }//end while + System.out.println("here rst: " + rst); + return rst; + } + + /** + * This method will be invoked second, the argument data is what exactly + * you serialized at method "serialize", that means the data is not given by + * system, it's given by your own serialize method. So the format of data is + * designed by yourself, and deserialize it here as you serialize it in + * "serialize" method. + */ + public TreeNode deserialize(String data) { + if (data == null) { + return null; + } + TreeNode root = new TreeNode(0); + root.val = Integer.parseInt(data.substring(0, data.indexOf(","))); + data = data.substring(data.indexOf(",") + 1); + + Queue queue = new LinkedList(); + queue.offer(root); + int size = 0; + while (!queue.isEmpty()) { + size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + String temp = data.substring(0, data.indexOf(",")); + if (!temp.equals("#")) { + node.left = new TreeNode(Integer.parseInt(temp)); + } + data = data.substring(data.indexOf(",") + 1); + + temp = data.substring(0, data.indexOf(",")); + if (!temp.equals("#")) { + node.right = new TreeNode(Integer.parseInt(temp)); + } + data = data.substring(data.indexOf(",") + 1); + } + } + + return root; + } + + public TreeNode init() { + TreeNode head = new TreeNode(1); + head.left = new TreeNode(2); + head.right = new TreeNode(3); + return head; + } + public static void main(String[] args) { + System.out.println("test"); + Solution test = new Solution(); + + + + String str = test.serialize(test.init()); + System.out.println(); + } + +} + + + + + diff --git a/Others/Sort Anagram.java b/Others/Sort Anagram.java new file mode 100755 index 0000000..2679a35 --- /dev/null +++ b/Others/Sort Anagram.java @@ -0,0 +1,12 @@ +/* + Sort Anagram + My guess this problem is: Sort a list of strings and make sure all the anagrams are grouped together. + + The clarification needed would be: + 1. Does the output need to be in alphabetical order and how to define the rules, since anagram will not be in perfect order comparing with other non-same-group strings. + + 2. If no need of alphabetical order, but just group anagram words together, this would turn to a regular anagram problem. + Use HashMap to store sorted string as KEY, and a list of strings as value. + Then output all contents from map. + +*/ \ No newline at end of file diff --git a/Others/TopTenURL.java b/Others/TopTenURL.java new file mode 100755 index 0000000..af56bfd --- /dev/null +++ b/Others/TopTenURL.java @@ -0,0 +1,35 @@ +/* + Given a list of URLs, find top 10 most visited URLs + + Some points this problem might cover: + + +*/ + +/* + 1. MapReduce related problem + http://stackoverflow.com/questions/17928158/find-top-10-most-frequent-visited-url-data-is-stored-accross-network# + To mimic the process of MapReduce. + + One most voted solution: + It says you can't use map-reduce directly which is a hint the author of the question wants you to think how map reduce works, so we will just mimic the actions of map-reduce: + + pre-processing: let R be the number of servers in cluster, give each server unique id from 0,1,2,...,R-1 + (map) For each (string,id) - send the tuple to the server which has the id hash(string) % R. + (reduce) Once step 2 is done (simple control communication), produce the (string,count) of the top 10 strings per server. Note that the tuples where those sent in step2 to this particular server. + (map) Each server will send all his top 10 to 1 server (let it be server 0). It should be fine, there are only 10*R of those records. + (reduce) Server 0 will yield the top 10 across the network. + + Notes: + + The problem with the algorithm, like most big-data algorithms that don't use frameworks is handling failing servers. MapReduce takes care of it for you. + The above algorithm can be translated to a 2 phases map-reduce algorithm pretty straight forward. +*/ + + +/* + 2. Top K words in a document: + This is an actaul problem on LintCode + http://www.zrzahid.com/top-k-or-k-most-frequent-words-in-a-document/ + http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ +*/ \ No newline at end of file diff --git a/Others/Tree-Huffman Decoding.java b/Others/Tree-Huffman Decoding.java new file mode 100755 index 0000000..91b33a9 --- /dev/null +++ b/Others/Tree-Huffman Decoding.java @@ -0,0 +1,129 @@ +M + +读.很长的题目。最后不过是:根据input char array, 找到leaf, print罢了。 + +这里frequency没有用到。可能构建这个Huffman Tree会更难 + +``` + + +/* +Huffman coding assigns variable length codewords to fixed length input characters based on their frequencies. More frequent characters are assigned shorter codewords and less frequent characters are assigned longer codewords. A huffman tree is made for the input string and characters are decoded based on their position in the tree. We add a '0' to the codeword when we move left in the binary tree and a '1' when we move right in the binary tree. We assign codes to the leaf nodes which represent the input characters. + +For example : + + {ϕ,5} + 0 / \ 1 + {ϕ,2} {A,3} + 0/ \1 +{B,1} {C,1} +Input characters are only present on the leaves. Internal nodes have a character value of ϕ. Codewords: + +A - 1 +B - 00 +C - 01 +No codeword appears as a prefix of any other codeword. Huffman encoding is a prefix free encoding technique. + +Encoded String "1001011" represents the string "ABACA" + +You have to decode an encoded string using the huffman tree. + +You are given pointer to the root of the huffman tree and a binary coded string. You need to print the actual string. + +Input Format + +You are given a function, + +void decode_huff(node * root, string s) +{ + +} +The structure for node is defined as : + +struct node +{ + int freq; + char data; + node * left; + node * right; + +}node; +Note: +Internal nodes have data='\0'(ϕ ) + +Output Format + +Output the decoded string on a single line. + +Sample Input + + {ϕ,5} + 0 / \ 1 + {ϕ,2} {A,3} + 0/ \1 +{B,1} {C,1} + +S="1001011" +Sample Output + +ABACA +Explanation + +S="1001011" +Processing the string from left to right. +S[0]='1' : we move to the right child of the root. We encounter a leaf node with value 'A'. We add 'A' to the decoded string. +We move back to the root. + +S[1]='0' : we move to the left child. +S[2]='0' : we move to the left child. We encounter a leaf node with value 'B'. We add 'B' to the decoded string. +We move back to the root. + +S[3] = '1' : we move to the right child of the root. We encounter a leaf node with value 'A'. We add 'A' to the decoded string. +We move back to the root. + +S[4]='0' : we move to the left child. +S[5]='1' : we move to the right child. We encounter a leaf node with value C'. We add 'C' to the decoded string. +We move back to the root. + + S[6] = '1' : we move to the right child of the root. We encounter a leaf node with value 'A'. We add 'A' to the decoded string. +We move back to the root. + +Decoded String = "ABACA" + +*/ + +/* + class Node + public int frequency; // the frequency of this tree + public char data; + public Node left, right; + +*/ +//Based on the input S.toCharArray(), and use the char to find leaf. Print leaf +void decode(String S ,Node root) { + if (root == null) { + return; + } + char[] arr = S.toCharArray(); + int index = 0; + String rst = ""; + while (index < arr.length) { + Node node = root; + while (node != null) { + if (node.left == null && node.right == null) { + rst += node.data; + break;//break inner while + } else { + char c = arr[index]; + if (c == '0') { + node = node.left; + } else { + node = node.right; + } + index++; + } + } + } + System.out.println(rst); +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/GenerateCodeTable.class b/Others/old records/LeetCode-Merged/GenerateCodeTable.class new file mode 100644 index 0000000..598affa Binary files /dev/null and b/Others/old records/LeetCode-Merged/GenerateCodeTable.class differ diff --git a/Others/old records/LeetCode-Merged/GenerateCodeTable.java b/Others/old records/LeetCode-Merged/GenerateCodeTable.java new file mode 100644 index 0000000..8da91b3 --- /dev/null +++ b/Others/old records/LeetCode-Merged/GenerateCodeTable.java @@ -0,0 +1,123 @@ +import java.io.*; +/* +Used to generate table of contents. +1. No args: generate both tables +2. args == 'word', generate WordPress table. +3. args == 'git', genereate GitHub table. +*/ +public class GenerateCodeTable { + public static void main(String[] args) { + //Read Java Solution Folder + File folder = new File("./Java");//"." = current path + if (!folder.exists() || !folder.isDirectory()) { + System.out.println("Check Directory:1"); + return; + } + File[] listOfFiles = folder.listFiles(); + if (listOfFiles == null) { + System.out.println("Check Directory:2"); + return; + } + + String outputContent = ""; + File outFile; + + if (args.length == 0){ + outputContent = generateREADME(listOfFiles); + printPage("README.md", outputContent); + outputContent = generateWordPressPage(listOfFiles); + printPage("WordPress.txt", outputContent); + } else if (args != null && args[0].contains("word")) {//Wordpress + outputContent = generateWordPressPage(listOfFiles); + printPage("WordPress.txt", outputContent); + } else if (args != null && args[0].contains("git")) { + outputContent = generateREADME(listOfFiles); + printPage("README.md", outputContent); + } else { + return; + } + + + } + /* + Generate Wordpress Table + */ + public static String generateWordPressPage(File[] listOfFiles) { + //Assemble output + String outputContent = "Java Solutions to problems from LeetCode(https://leetcode.com/problemset/algorithms/).\n" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + "" + + ""; + + int count = 0; + for (File file : listOfFiles) { + if (file.getName().contains(".java")) { + //outputContent += "|" + count + "|[" + file.getName() + "](https://github.com/awangdev/LintCode/blob/master/Java/"+ file.getName() +")| |" + "Java|\n"; + outputContent+= + "" + + "" + + "" + + "" + + "" + + ""; + count++; + } + } + + outputContent += "
#Problem      Level  Language
" + count + "" + file.getName() + "Java
"; + return outputContent; + } + + + /* + Generate GitHub ReadMe file + */ + public static String generateREADME(File[] listOfFiles) { + //Assemble output + String outputContent = "# LeetCode\n\n" + + "To host Java Solutions to problems from LeetCode(https://leetcode.com/problemset/algorithms/).\n" + + "I Will try to revise the solutions once new problem or new testing case occurs.\n" + + "Since I do not run .java files, they are formatted with markdowns to help compressing code in blog format.\n\n"+ + "| Squence | Problem | Level | Language |\n" + + "|:-------:|:--------------|:---------------|:---------:|\n"; + int count = 0; + for (File file : listOfFiles) { + if (file.getName().contains(".java")) { + outputContent += "|" + count + "|[" + file.getName() + "](https://github.com/awangdev/LeetCode/blob/master/Java/"+ file.getName() +")| |" + "Java|\n"; + count++; + } + } + return outputContent; + } + /* + Generate a combined post of all files, with proper markdown + */ + public static String generateCombinedPost() { + return ""; + } + /* + Write the outputContent to specific file + */ + public static void printPage(String fileName, String outputContent) { + System.out.println(outputContent); + try { + File outFile = new File(fileName); + FileOutputStream fop = new FileOutputStream(outFile); + byte[] contentInBytes = outputContent.getBytes(); + fop.write(contentInBytes); + fop.flush(); + fop.close(); + System.out.println("Mission Accomplished. Now go ahead and commit"); + } catch (IOException e) { + e.printStackTrace(); + } + } + +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/3Sum Smaller.java b/Others/old records/LeetCode-Merged/Java/3Sum Smaller.java new file mode 100644 index 0000000..55642b1 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/3Sum Smaller.java @@ -0,0 +1,58 @@ +一般的O(n3)肯定不行。在此基础上优化。 +发现j,k满足条件时候,(k - j)就是所有 sum target, 又因为j不能后退,只能k--,那么问题就被锁定了. 这样可以做到O(n2) +``` +/* +Given an array of n integers nums and a target, find the number of index triplets i, j, k with 0 <= i < j < k < n that satisfy the condition nums[i] + nums[j] + nums[k] < target. + +For example, given nums = [-2, 0, 1, 3], and target = 2. + +Return 2. Because there are two triplets which sums are less than 2: + +[-2, 0, 1] +[-2, 0, 3] + +Follow up: +Could you solve it in O(n2) runtime? + +Tags: Array Two Pointers +Similar Problems:(M) 3Sum, (M) 3Sum Closest + +*/ + + +/* +Thoughts: +Similar to 3 sum, but ofcourse, this one check on '<' so we can not use HashMap anymore. +Basic concept is to fix first number, then check for the rest two numbers, see if they addup < target. +When checking j and k, realize something nice: + if nums[j] + nums[k] < target - nums[i], that means for all index <= k will work, so directly add (k - j) to result (that's: index = j+1, j+2, ....,k) + also, move j forward for next round. +OR, if three-add-up >= target, since j can only increase, we do k-- to make the three-add-up smaller + +Note: +Don't forget to sort, otherwise the sequence/order is unpredictable +*/ +public class Solution { + public int threeSumSmaller(int[] nums, int target) { + if (nums == null || nums.length <= 2) { + return 0; + } + Arrays.sort(nums); + int rst = 0; + for (int i = 0; i < nums.length - 2; i++) { + int j = i + 1; + int k = nums.length - 1; + while (j < k) { + if (nums[i] + nums[j] + nums[k] >= target) { + k--; + } else { + rst += (k - j); + j++; + } + } + }//END for + return rst; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/3Sum.java b/Others/old records/LeetCode-Merged/Java/3Sum.java new file mode 100644 index 0000000..6d77930 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/3Sum.java @@ -0,0 +1,78 @@ +/* +Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? +Find all unique triplets in the array which gives the sum of zero. + +Note: +Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c) +The solution set must not contain duplicate triplets. + For example, given array S = {-1 0 1 2 -1 -4}, + + A solution set is: + (-1, 0, 1) + (-1, -1, 2) +Hide Company Tags Facebook +Hide Tags Array Two Pointers +Hide Similar Problems (M) Two Sum (M) 3Sum Closest (M) 4Sum (M) 3Sum Smaller + +*/ +/* + Thoughts: + sort list. O(nLogn) + end: n^2 + for (i = 0 ~ n) { + int target = 0 - nums[i]; + while (start + 1 < end) { + start + end == target { + rst.add(i, star, end); + keep looking: start ++, end-- + } + else start + end < target? + start++ + else + end--; + } + } + } + +Note: + Check duplicates. Compute a unique string to savei set +*/ + +public class Solution { + public List> threeSum(int[] nums) { + List> rst = new ArrayList>(); + if (nums == null || nums.length == 0) { + return rst; + } + + Arrays.sort(nums); + HashSet set = new HashSet(); + //use old target to check duplicates. instead of set. + for (int i = 0; i < nums.length - 2; i++) { + int target = 0 - nums[i]; + int start = i + 1; + int end = nums.length - 1; + + ArrayList list = new ArrayList(); + while (start < end) { + if (nums[start] + nums[end] == target && + !set.contains(nums[i] + "," + nums[start] + "," + nums[end])) { + list.add(nums[i]); + list.add(nums[start]); + list.add(nums[end]); + rst.add(list); + set.add(nums[i] + "," + nums[start] + "," + nums[end]); + list = new ArrayList(); + start++; + end--; + } else if (nums[start] + nums[end] < target) { + start++; + } else { + end--; + } + }//end while + } + + return rst; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Alien Dictionary.java b/Others/old records/LeetCode-Merged/Java/Alien Dictionary.java new file mode 100644 index 0000000..da58127 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Alien Dictionary.java @@ -0,0 +1,56 @@ +/* +There is a new alien language which uses the latin alphabet. +However, the order among letters are unknown to you. +You receive a list of words from the dictionary, where words are sorted lexicographically by the rules of this new language. +Derive the order of letters in this language. + +For example, +Given the following words in dictionary, + +[ + "wrt", + "wrf", + "er", + "ett", + "rftt" +] +The correct order is: "wertf". + +Note: +You may assume all letters are in lowercase. +If the order is invalid, return an empty string. +There may be multiple valid order of letters, return any one of them is fine. +Hide Company Tags Google Facebook +Hide Tags Graph Topological Sort +Hide Similar Problems (M) Course Schedule II + +*/ + +/* + NOT DONE + Thoughts: + They have sink node. They form a valid tree, without sycle. + A char can visit another node, does not mean they have order. + A char appear in a lower row means they have different order. + For 1st column, w appears before e, e appears before r. + For 2nd column:r appears before t, t appears before f + For 3rd col: t appears before f. + For 4th col, nothing to compare. + So make in[][]: [w,e] [e,r][r,t][t,f] based on the possible order. + + Then do topological sort on the sequence and mark the sequence like in course schedule II +*/ + +public class Solution { + public String alienOrder(String[] words) { + + } +} + + + + + + + + diff --git a/Others/old records/LeetCode-Merged/Java/Binary Search Tree Iterator.java b/Others/old records/LeetCode-Merged/Java/Binary Search Tree Iterator.java new file mode 100644 index 0000000..56f3585 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Binary Search Tree Iterator.java @@ -0,0 +1,86 @@ +李特这的这个题目不错。写一遍example就能看出来inorder traversal。当然啦,不能直接全部traverse了,因为题目说有空间限制。 + +那么就traversal on the fly, 先左手DFS, 然后每次加上一个右手node,都再来一遍左手DFS。 + +存到一个后进先出的数据结构里,stack呗,然后头顶就是最小的了。 + +``` +/* +Implement an iterator over a binary search tree (BST). Your iterator will be initialized with the root node of a BST. + +Calling next() will return the next smallest number in the BST. + +Note: next() and hasNext() should run in average O(1) time and uses O(h) memory, where h is the height of the tree. + +Tags: Tree Stack Design +Similar Problems: (M) Binary Tree Inorder Traversal, (M) Flatten 2D Vector, (M) Zigzag Iterator, (M) Peeking Iterator, (M) Inorder Successor in BST + +*/ + +/* +Attempt, Thoughts: +Test + 5 + 3 9 + 1 4 6 10 +return: 1,3,4,5,6,9,10. Looks like in-order taversal style, though don't traversal all at once because we can only store O(h) elements. +However, we can do inorder traversal on the fly, by mantaining a stack. +How about Priority queue of all left-most elements. + Do a run-through on left elements, add them all. + When pop one element: + (it cannot have left, because we've initially added them already) + if has right: + add right node + check right's node's left-most(DFS), added all left nodes and left nodes' left-child +Well.. the way I did it, does not need priority queue. Just use a stack will be fine. +*/ +/** + * Definition for binary tree + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +public class BSTIterator { + private Stack stack = new Stack(); + public BSTIterator(TreeNode root) { + if (root == null) { + return; + } + stack.push(root); + while(root.left != null) { + root = root.left; + stack.push(root); + } + } + + /** @return whether we have a next smallest number */ + public boolean hasNext() { + return !stack.isEmpty(); + } + + /** @return the next smallest number */ + public int next() { + TreeNode node = stack.pop(); + int rst = node.val; + if (node.right != null) { + node = node.right; + stack.push(node); + while(node.left != null) { + node = node.left; + stack.push(node); + } + } + return rst; + } +} + +/** + * Your BSTIterator will be called like this: + * BSTIterator i = new BSTIterator(root); + * while (i.hasNext()) v[f()] = i.next(); + */ +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Binary Tree Inorder Traversal.java b/Others/old records/LeetCode-Merged/Java/Binary Tree Inorder Traversal.java new file mode 100644 index 0000000..9edeb06 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Binary Tree Inorder Traversal.java @@ -0,0 +1,81 @@ +/* +Given a binary tree, return the inorder traversal of its nodes' values. + +For example: +Given binary tree {1,#,2,3}, + 1 + \ + 2 + / + 3 +return [1,3,2]. + +Note: Recursive solution is trivial, could you do it iteratively? + +confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ. + + +OJ's Binary Tree Serialization: +The serialization of a binary tree follows a level order traversal, where '#' signifies a path terminator where no node exists below. + +Here's an example: + 1 + / \ + 2 3 + / + 4 + \ + 5 +The above binary tree is serialized as "{1,2,3,#,#,4,#,#,5}". +*/ + +/* +Thoughts: Need to it iteratively +Steps: +1. added root + all left most (whichever added last, process that first, so STACK) +2. Process the stack, for each node, if right !=null, then add right, called it rightNode +3. Add all rightNode's left child and grandchild ... etc. +Test + 5 + 3 9 + 1 4 6 10 +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public List inorderTraversal(TreeNode root) { + List rst = new ArrayList(); + Stack stack = new Stack(); + if (root == null) { + return rst; + } + //Add left children + dfsOnLeft(stack, root); + //Process + while(!stack.isEmpty()) { + TreeNode node = stack.pop(); + rst.add(node.val); + if (node.right != null) { + node = node.right; + dfsOnLeft(stack, node); + } + }//end while + return rst; + } + + public void dfsOnLeft(Stack stack, TreeNode node) { + stack.push(node); + while(node.left != null) { + node = node.left; + stack.push(node); + } + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Binary Tree Level Order Traversal.java b/Others/old records/LeetCode-Merged/Java/Binary Tree Level Order Traversal.java new file mode 100644 index 0000000..0ad0278 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Binary Tree Level Order Traversal.java @@ -0,0 +1,80 @@ +普通的BFS. +唯一小心就是:每次要把每一Level 的放在一个list里面。也就是说,一旦queue里有东西,那一定就全部是这一个level的。先把size固定一下,把queue里面的东西全部加到这个level的list里面(同时还要继续添加element 进 queue)。没跑完一圈i ~ size, 然后就add那个level list。 +``` +/* +Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). + +For example: +Given binary tree {3,9,20,#,#,15,7}, + 3 + / \\ + 9 20 + / \\ + 15 7 +return its level order traversal as: +[ + [3], + [9,20], + [15,7] +] +confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ. + +Tags: Tree Breadth-first Search +Similar Problems: (M) Binary Tree Zigzag Level Order Traversal, (E) Binary Tree Level Order Traversal II, (E) Minimum Depth of Binary Tree + +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Thoughts: +Looks like BFS. put root in a queue, then put left, right in the queue. +Process the queue until it runs out. + +Note: +Queue: offer() + + +*/ +public class Solution { + public List> levelOrder(TreeNode root) { + List> rst = new ArrayList>(); + if (root == null) { + return rst; + } + Queue queue = new LinkedList(); + queue.offer(root); + + while (!queue.isEmpty()) { + int size = queue.size();//Becareful with fixed size + List list = new ArrayList(); + for (int i = 0; i < size; i++) { + TreeNode node = queue.poll(); + list.add(node.val); + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + rst.add(list); + } + return rst; + } +} + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Binary Tree Longest Consecutive Sequence.java b/Others/old records/LeetCode-Merged/Java/Binary Tree Longest Consecutive Sequence.java new file mode 100644 index 0000000..05d54db --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Binary Tree Longest Consecutive Sequence.java @@ -0,0 +1,125 @@ +屌炸天的4行代码。 +我洋洋洒洒那么多行,最后还不work.看了solution, 如此精简。 + +主要想法: +Recursive用好。首先在这个level比一比,可否成。 +不成的话,另立门户。 +然后左右开弓。再把结果拿过来比较一下就好了。简单明了。 +``` +/* +Given a binary tree, find the length of the longest consecutive sequence path. + +The path refers to any sequence of nodes from some starting node to any node in the tree along the parent-child connections. The longest consecutive path need to be from parent to child (cannot be the reverse). + +For example, + 1 + \\ + 3 + / \\ + 2 4 + \\ + 5 +Longest consecutive sequence path is 3-4-5, so return 3. + 2 + \\ + 3 + / + 2 + / + 1 +Longest consecutive sequence path is 2-3,not3-2-1, so return 2. + +Tags:Tree +Similar Problems: (H) Longest Consecutive Sequence + +*/ + +/* +Attemp2: http://www.cnblogs.com/jcliBlogger/p/4923745.html. +The original solution has just 4 lines of C++ code. That hurts. +The concept is very much similar as my attempt1, though the code is more clear with recursive call +1. pass alone a depth. +2. if consecutive, depth++; else, start from depth 1 +3. Go deeper on both left, and right; both with new depth: currDepth; +4. Compare the Max of currDept, left's return, right's return. +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public int longestConsecutive(TreeNode root) { + return recursiveHelper(root, null, 0); + } + + public int recursiveHelper(TreeNode curr, TreeNode parent, int depth) { + if (curr == null) { + return 0; + } + int currDepth = 0; + if (parent != null && parent.val + 1 == curr.val) { + currDepth = depth + 1; + } else { + currDepth = 1; + } + return Math.max(currDepth, Math.max(recursiveHelper(curr.left, curr, currDepth), recursiveHelper(curr.right, curr, currDepth))); + } +} + + +/* +Attemp1, failed. +Thoughts: +Seems like backtracking: we find the lowest level, then return depth, and add it on parent's record if consectutive. +always record a max value. +If node.left != null || node.right != null, if consecutive, node.depth = node.child.depth +1 +If node.left == null && node.right == null, return 1; + + +public class Solution { + private int max = Integer.MIN_VALUE; + public int longestConsecutive(TreeNode root) { + dfsOnNode(root); + return max; + } + + public int dfsOnNode(TreeNode root) { + int depth = 0; + int localMax = Integer.MIN_VALUE; + if (root == null) { + return depth; + } + if (root.left == null && root.right == null) {//ON:right4, + depth++; + return depth; + } + if (root.right != null) {//root.right = 3, right4, + int childDepth = longestConsecutive(root.right);//ON: 3, right4 + if (root.val + 1 == root.right.val) {//3+1=4 + depth = childDepth + 1;//1+1=2 + } else { + depth = 1; + } + } + localMax = Math.max(localMax, depth); + if (root.left != null) { + int childDepth = longestConsecutive(root.left); + if (root.val + 1 == root.left.val) { + depth = childDepth + 1; + } else { + depth = 1; + } + } + localMax = Math.max(localMax, depth); + max = Math.max(max, localMax); + return localMax; + } +} +*/ +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Binary Tree Paths.java b/Others/old records/LeetCode-Merged/Java/Binary Tree Paths.java new file mode 100644 index 0000000..bc99f12 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Binary Tree Paths.java @@ -0,0 +1,71 @@ +一幕了然. DFS把tree给过了. +用ArrayList存item比较方便,记得每次backtrack的时候把末尾的item去掉 +``` +list.remove(list.size() - 1); +``` + +``` +/* +Given a binary tree, return all root-to-leaf paths. + +For example, given the following binary tree: + + 1 + / \\ +2 3 + \\ + 5 +All root-to-leaf paths are: + +["1->2->5", "1->3"] +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public List binaryTreePaths(TreeNode root) { + List rst = new ArrayList(); + if (root == null) { + return rst; + } + ArrayList list = new ArrayList(); + DFS(root, list, rst); + return rst; + } + public void DFS(TreeNode node, ArrayList list, List rst) { + list.add(node.val+""); + if(node.left == null && node.right == null) { + String str = ""; + for (String s : list) { + str += s + "->"; + } + rst.add(str.substring(0, str.length() - 2)); + return; + } + if (node.left != null) { + DFS(node.left, list, rst); + list.remove(list.size() - 1); + } + if (node.right != null) { + DFS(node.right, list, rst); + list.remove(list.size() - 1); + } + } +} + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Binary Tree Right Side View.java b/Others/old records/LeetCode-Merged/Java/Binary Tree Right Side View.java new file mode 100644 index 0000000..5c7e923 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Binary Tree Right Side View.java @@ -0,0 +1,86 @@ +自己想了这个方法,有可能不是特别efficient. +一个queue放普通的BFS。 +一个queue放level。 +同时维护一个parent value;维护一个跟着BFS跑的level。 +每个node都有一个lv。一旦lv和正在跑的level不一样,证明lv>level,那么也就是说,刚刚换行拉。parent的值,就是上一行最右边的值。DONE. +``` +/* +Given a binary tree, imagine yourself standing on the right side of it, +return the values of the nodes you can see ordered from top to bottom. + +For example: +Given the following binary tree, + 1 <--- + / \\ +2 3 <--- + \\ \\ + 5 4 <--- +You should return [1, 3, 4]. + +Tags: Tree, Depth-first Search, Breadth-first Search +Similar Problems: (M) Populating Next Right Pointers in Each Node + +*/ + +/* +Thoughts: +Use 2 queue: one for BFS, one for level. Each node in queue has a corresponding level +Track level. +WHen level != levelQ.poll(), that means we are moving to next level, and we should record the previous(parent) node's value. +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public List rightSideView(TreeNode root) { + List rst = new ArrayList(); + if (root == null) { + return rst; + } + Queue q = new LinkedList(); + Queue levelQ = new LinkedList(); + q.offer(root); + levelQ.offer(1); + int level = 1; + int parent = root.val; + TreeNode node = null; + + while (!q.isEmpty()) { + node = q.poll(); + int lv = levelQ.poll(); + if (level != lv) { + level++; + rst.add(parent); + } + parent = node.val; + if (node.left != null) { + q.offer(node.left); + levelQ.offer(lv + 1); + } + if (node.right != null) { + q.offer(node.right); + levelQ.offer(lv + 1); + } + }//END while + rst.add(parent); + return rst; + } +} + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Building Outline.java b/Others/old records/LeetCode-Merged/Java/Building Outline.java new file mode 100644 index 0000000..8610681 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Building Outline.java @@ -0,0 +1,62 @@ +/* +Given N buildings in a x-axis,each building is a rectangle and can be represented by a triple (start, end, height),where start is the start position on x-axis, end is the end position on x-axis and height is the height of the building. Buildings may overlap if you see them from far away,find the outline of them。 + +An outline can be represented by a triple, (start, end, height), where start is the start position on x-axis of the outline, end is the end position on x-axis and height is the height of the outline. + +Example +Given 3 buildings: + +[ + [1, 3, 3], + [2, 4, 4], + [5, 6, 1] +] +The outlines are: + +[ + [1, 2, 3], + [2, 4, 4], + [5, 6, 1] +] +Note +Please merge the adjacent outlines if they have the same height and make sure different outlines cant overlap on x-axis. + +Tags Expand +LintCode Copyright Heap +*/ + + + +public class Solution { + /** + * @param buildings: A list of lists of integers + * @return: Find the outline of those buildings + */ + public ArrayList> buildingOutline(int[][] buildings) { + // write your code here + } +} + + +/* +Attempt1, may not be correct. +Thoughts: +PriorityQueue, sort by start. +1. Keep track of max height. +2. Find max height. +3. Poll() queue + a. on left of highest building, When height increate, record this point(x, height) + b. on right of highest building, when height decreses, record this point. + +*/ + + + + + + + + + + + diff --git a/Others/old records/LeetCode-Merged/Java/Burst Balloons.java b/Others/old records/LeetCode-Merged/Java/Burst Balloons.java new file mode 100644 index 0000000..ec05ba9 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Burst Balloons.java @@ -0,0 +1,121 @@ +其实会做之后挺好想的一个DP。 +dp[i][j] = balloons i~j 之间的sum. 然后找哪个点开始burst? 设为x。 +For loop 所有的点作为x, 去burst。 +每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 + + +这个是momorization, 而不纯是DP +因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing +``` +/* +Given n balloons, indexed from 0 to n-1. Each balloon is painted with a number on it represented by array nums. +You are asked to burst all the balloons. If the you burst balloon i you will get nums[left] * nums[i] * nums[right] coins. +Here left and right are adjacent indices of i. After the burst, the left and right then becomes adjacent. + +Find the maximum coins you can collect by bursting the balloons wisely. + +Note: +(1) You may imagine nums[-1] = nums[n] = 1. They are not real therefore you can not burst them. +(2) 0 ≤ n ≤ 500, 0 ≤ nums[i] ≤ 100 + +Example: + +Given [3, 1, 5, 8] + +Return 167 + + nums = [3,1,5,8] --> [3,5,8] --> [3,8] --> [8] --> [] + coins = 3*1*5 + 3*5*8 + 1*3*8 + 1*8*1 = 167 +Credits: +Special thanks to @peisi for adding this problem and creating all test cases. + +Hide Company Tags Google +Show Tags +Divide and Conquer Dynamic Programming + + +*/ + +/* + Thoughts: as seen in dicussion. Build DP. + State: + dp[i][j]: the number of max coins can collect between i and j. + For a position x in [i,j], where to burst it? So this goes into a divide and conquer method. + Burst at x, track the sum, and record the max into dp[i][j] + Function: + dp[i][j] = Math.max(dp[i][j], DP(i, x-1) + nums[x-1]*nums[x]*nums[x+1] + DP(x+1, j)) + Init: + create dp[n+2][n+2]. (from 0 to n+1) + dp[0][1] = 1; + dp[n][n+1] = 1; + Return: + dp[1][n] + + DP(int i, int j, int[][] dp) + + Need to redo that nums. +*/ + + +public class Solution { + int[][] dp; + int[] values; + public int maxCoins(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + dp = new int[n + 2][n + 2]; + + //Initialize new array + values = new int[n + 2]; + values[0] = values[n + 1] = 1; + for (int i = 1; i < n + 1; i++) { + values[i] = nums[i - 1]; + } + + return DP(1, n); + } + + public int DP(int i, int j){ + if (dp[i][j] > 0) {//momorization + return dp[i][j]; + } + for (int x = i; x <= j; x++) { + dp[i][j] = Math.max(dp[i][j], DP(i, x - 1) + values[i-1]*values[x]*values[j+1] + DP(x + 1, j)); + } + return dp[i][j]; + } +} + +/* + 用了recursive + memorization, 但是也可以用传统的DP,比如: + for (int length = 1; length < n; length++) [ + for (int = 0; i < n-1; i++) { + j = i + length; + if length == 1: + dp[i][j] = A[i] * A[j] + A[i] + else: + dp[i][j] = max {} + } + } + +*/ + + + +/* + My Thought: TOO COMPLEX. Should go with the easy DP approach. Also, using a hashMap to trach all the patterns, + this might not be applicable: because as the integer array's length goes up, there will be too many possible + combinations to store in hashamp. + Burst each balloon, and DFS into each branch, calcualte the sum + each balloon-burst's product. + Also, use a HahsMap<"Value combination", max value>. to reduce the # of re-calculation. + convert nums into string, and in DFS, we don't even need bakc-tracking + helper(list, sum) + + + Thoughts:http://www.cnblogs.com/grandyang/p/5006441.html + dp[i,j]: burst range [i~j]'s max coins. + +*/ +``` diff --git a/Others/old records/LeetCode-Merged/Java/Closest Binary Search Tree Value.java b/Others/old records/LeetCode-Merged/Java/Closest Binary Search Tree Value.java new file mode 100644 index 0000000..865f7b0 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Closest Binary Search Tree Value.java @@ -0,0 +1,50 @@ +/* +Given a non-empty binary search tree and a target value, find the value in the BST that is closest to the target. + +Note: +Given target value is a floating point. +You are guaranteed to have only one unique value in the BST that is closest to the target. + +Tags: Tree Binary Search +Similar Problems: (M) Count Complete Tree Nodes, (H) Closest Binary Search Tree Value II + +*/ + + +/* +Thoughts: +Binary search, maintain a closest value. +Note: initial closest in real case is just the root, since we start from the root +*/ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public int closestValue(TreeNode root, double target) { + if (root == null) { + return 0; + } + double closest = root.val; + while (root != null) { + if (root.val == target) { + return root.val; + } else { + if (Math.abs(target - closest) >= Math.abs(target - root.val)) { + closest = root.val; + } + if (root.val > target) { + root = root.left; + } else { + root = root.right; + } + } + }//END while + return (int)closest; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Count Primes.java b/Others/old records/LeetCode-Merged/Java/Count Primes.java new file mode 100644 index 0000000..7120d81 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Count Primes.java @@ -0,0 +1,83 @@ +什么是prime number: >=2的没有除自己和1以外公约数的数。 +还有另外一个中定义方法: +这个n,有没有小于n的一个i,而达到: i*i + # of i = n. 如果有,那就不是 prime。 + +方法很牛逼也很数学。没做的时候可能想不到。做了之后就觉得,哎,我去,有道理啊。 +简而言之:简历一个boolean长条,存isPrime[]。 然后从i=2, 全部变true. +然后利用这个因子的性质,非prime满足条件: self*self, self * self + self ... etc. +所以就check每一个j, j+i, j+i+i, 然后把所有non-prime全部mark成false. +最后,数一遍还剩下的true个数就好了 +``` +/* +Description: +Count the number of prime numbers less than a non-negative number, n. +Tags: Hash Table, Math +Similar Problems: (E) Ugly Number, (M) Ugly Number II, (M) Perfect Squares +*/ + +/* +Attempt2: https://leetcode.com/problems/count-primes/ explains it well +1. Ignore 1 and n. Don't count 1 and the number itself in. +2. Assume all numbers are prime in a boolean[]. Check off those are certainly not prime, the remaining will be prime. +3. For any n, only need to check up to i * i < n; more than that, for example 2 x 6 is same as checking 6x2, but 6x2 is not necessary to check. +4. How to mark things off: + The first non-prime is always i^2: self * self. + Then more non-primes:self * self, self * (self + 1), self * (self + 2) ... etc. + So, mark all of these index of in the boolean[] + +*/ +public class Solution { + public int countPrimes(int n) { + if (n <= 1) { + return 0; + } + boolean[] primes = new boolean[n]; + for (int i = 2; i < primes.length; i++) { + primes[i] = true; + } + + for (int i = 2; i * i< n; i++) { + if (!primes[i]) { + continue; + } + for (int j = i * i; j < n; j += i) { + primes[j] = false; + } + } + int count = 0; + for (int i = 2; i < primes.length; i++) { + count += primes[i] ? 1 : 0; + } + return count; + } +} + + +/*Timeout version*/ +//prime is a number n that cannot be divided by any number < n. +//In fact, only need to check sqrt(n) numbers from 1 + +public class Solution { + public int countPrimes(int n) { + int count = 0; + for (int i = 1; i < n; i++) { + if (isPrime(i)) { + count++; + } + } + return count; + } + + public boolean isPrime(int num) { + if (num <= 1) return false; + for (int i = 2; i * i <= num; i++) { + if (num % i == 0) { + return false; + } + } + return true; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Course Schedule II.java b/Others/old records/LeetCode-Merged/Java/Course Schedule II.java new file mode 100644 index 0000000..262c275 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Course Schedule II.java @@ -0,0 +1,101 @@ +详细的中文分析,看Course Schedule I +``` +/* +There are a total of n courses you have to take, labeled from 0 to n - 1. + +Some courses may have prerequisites, for example to take course 0 you have to first take course 1, which is expressed as a pair: [0,1] + +Given the total number of courses and a list of prerequisite pairs, return the ordering of courses you should take to finish all courses. + +There may be multiple correct orders, you just need to return one of them. If it is impossible to finish all courses, return an empty array. + +For example: + +2, [[1,0]] +There are a total of 2 courses to take. To take course 1 you should have finished course 0. So the correct course order is [0,1] + +4, [[1,0],[2,0],[3,1],[3,2]] +There are a total of 4 courses to take. To take course 3 you should have finished both courses 1 and 2. +Both courses 1 and 2 should be taken after you finished course 0. +So one correct course order is [0,1,2,3]. Another correct ordering is[0,2,1,3]. + +Note: +The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented. + +click to show more hints. + +Hints: +This problem is equivalent to finding the topological order in a directed graph. If a cycle exists, no topological ordering exists and therefore it will be impossible to take all courses. +Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining the basic concepts of Topological Sort. +Topological sort could also be done via BFS. +Hide Company Tags Facebook Zenefits +Hide Tags Depth-first Search Breadth-first Search Graph Topological Sort +Hide Similar Problems (M) Course Schedule (H) Alien Dictionary (M) Minimum Height Trees + +*/ + +/* + http://blog.csdn.net/ljiabin/article/details/45847019 + + Based on Course Schedule I, now we need to return all nodes with by the seq number. + + Note: + The map is built based on +*/ +public class Solution { + HashMap> map; + int[] visited; + int seq; + int[] order; + public int[] findOrder(int numCourses, int[][] prerequisites) { + order = new int[numCourses]; + seq = numCourses - 1; + visited = new int[numCourses]; + map = new HashMap>(); + //Put all start node into map. + for (int i = 0; i < prerequisites.length; i++) { + if (!map.containsKey(prerequisites[i][1])) { + map.put(prerequisites[i][1], new ArrayList()); + } + map.get(prerequisites[i][1]).add(prerequisites[i][0]); + } + //dfs on each start node in the pair + for (int i = 0; i < numCourses; i++) { + if (!dfs(i)) { + return new int[]{}; + } + } + return order; + } + + public boolean dfs (int node) { + if (visited[node] == 1) {//has been through this path, true. + return true; + } + if (visited[node] == -1) {//visiting a visited node from a deper level node, cycle + return false; + } + //mark it -1 then after dfs mark it 1. Marking and backtracking skills + visited[node] = -1; + + //Visit each child and make sure there is no cycle. + if (map.containsKey(node)) { + for (int nextNode : map.get(node)) { + if (!dfs(nextNode)) { + return false; + } + } + } + visited[node] = 1; + order[seq--] = node; + return true; + } + +} + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Course Schedule.java b/Others/old records/LeetCode-Merged/Java/Course Schedule.java new file mode 100644 index 0000000..b722c58 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Course Schedule.java @@ -0,0 +1,222 @@ +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +``` +/* +There are a total of n courses you have to take, labeled from 0 to n - 1. + +Some courses may have prerequisites, for example to take course 0 you have to first take course 1, +which is expressed as a pair: [0,1] + +Given the total number of courses and a list of prerequisite pairs, is it possible for you to finish all courses? + +For example: + +2, [[1,0]] +There are a total of 2 courses to take. To take course 1 you should have finished course 0. So it is possible. + +2, [[1,0],[0,1]] +There are a total of 2 courses to take. To take course 1 you should have finished course 0, and to take course 0 you should also have finished course 1. +So it is impossible. + +Note: +The input prerequisites is a graph represented by a list of edges, not adjacency matrices. Read more about how a graph is represented. + +click to show more hints. + +Hints: +1. This problem is equivalent to finding if a cycle exists in a directed graph. + If a cycle exists, no topological ordering exists and therefore it will be impossible to take all courses. +2. Topological Sort via DFS - A great video tutorial (21 minutes) on Coursera explaining the basic concepts of Topological Sort. +3. Topological sort could also be done via BFS. + +Hide Company Tags Zenefits +Hide Tags Depth-first Search Breadth-first Search Graph Topological Sort +Hide Similar Problems (M) Course Schedule II (M) Graph Valid Tree (M) Minimum Height Trees + +*/ + +/* + Thoughts: Try some help. make shorter version. + This version does not use a class, but does the exact same algorithm: mark visited and check cycle with dfs. + The idea is almost exaclty same (http://www.jyuan92.com/blog/leetcode-course-schedule/) + Instead of using a seq,visited to check mark seq, and visted, we can do a 'mark' backtracking. + This means: we can mark the visited = -1 before the dfs, and if anything in the dfs sees a -1, + it returns false; + After the dfs of children nodes in the map, wwe mark a node.visited = 1. That means the spot has been visited : ) + + Note: + SHould build he map: map +*/ + +public class Solution { + HashMap> map; + int[] visited; + public boolean canFinish(int numCourses, int[][] prerequisites) { + if (numCourses <= 0 || prerequisites == null || prerequisites.length == 0 || + prerequisites[0] == null || prerequisites[0].length == 0) { + return true; + } + visited = new int[numCourses]; + map = new HashMap>(); + //Put all start node into map. + for (int i = 0; i < prerequisites.length; i++) { + if (!map.containsKey(prerequisites[i][1])) { + map.put(prerequisites[i][1], new ArrayList()); + } + map.get(prerequisites[i][1]).add(prerequisites[i][0]); + } + //dfs on each start node in the pair + for (int i = 0; i < prerequisites.length; i++) { + if (!dfs(prerequisites[i][0])) { + return false; + } + } + + return true; + } + + public boolean dfs (int node) { + if (visited[node] == 1) {//has been through this path, true. + return true; + } + if (visited[node] == -1) {//visiting a visited node from a deper level node, cycle + return false; + } + //mark it -1 then after dfs mark it 1. Marking and backtracking skills + visited[node] = -1; + + //Visit each child and make sure there is no cycle. + if (map.containsKey(node)) { + for (int nextNode : map.get(node)) { + if (!dfs(nextNode)) { + return false; + } + } + } + + visited[node] = 1; + return true; + } + +} + + + + + + +/* + Thoughts: TO LONG. Should try shorter version. + ALSO, built the map based map: this will cause trouble and hard to work if we want to return + the sequence of course. + + Though, no need to find the correct order of course, but we can do Topological Sort via DFS, + where in the process we check if there is cycle. + 0. Create node {val, visited, list of child node}. OR: just a map + 1. Put all prerequisites in map + 2. For loop on all nodes in the map. + Each node, DFS on it, add all outgoing child on stack if that node is not visited + Check on cycle: after DFS on all child, we need to mark the parent node itself to be visited. + At this moment, if this node has been visited once, that means a cycle has happended, then return false; + + Implementation: + Use a node, track value, sequence number, and if visited, and its child nodes. + In DFS: first mark curr node visited, then DFS on child, then mark curr node with a sequence number. + Cycle: if a node has been visited but does not have a sequence number yet , + (that means some nodes at the earlier level, which has no been back-tracking to yet) + then there must be a cycle backwards. +*/ +public class Solution { + public class Node { + int val; + int seq; + boolean visited; + ArrayList children; + public Node(int val){ + this.val = val; + this.visited = false; + this.children = new ArrayList(); + this.seq = -1; + } + } + public int n; + public boolean canFinish(int numCourses, int[][] prerequisites) { + if (numCourses <= 0 || prerequisites == null || prerequisites.length == 0 || + prerequisites[0] == null || prerequisites[0].length == 0) { + return true; + } + HashMap map = new HashMap(); + for (int i = 0; i < prerequisites.length; i++) { + Node node; + //Add curr nodes + if (map.containsKey(prerequisites[i][0])) { + node = map.get(prerequisites[i][0]); + } else { + node = new Node(prerequisites[i][0]); + } + node.children.add(prerequisites[i][1]); + map.put(node.val, node); + //Add Child nodes + if (!map.containsKey(prerequisites[i][1])) { + map.put(prerequisites[i][1], new Node(prerequisites[i][1])); + } + } + n = prerequisites.length; + for (int i = 0; i < prerequisites.length; i++) { + if (!DFS(prerequisites[i][0], map)) { + return false; + } + } + + return true; + } + + public boolean DFS(int val, HashMap map) { + Node node = map.get(val); + node.visited = true; + map.put(val, node); + + for (int child : node.children) { + if (map.get(child).visited && map.get(child).seq == -1) {//Check cycle + return false; + } else if (!map.get(child).visited) { + if(!DFS(child, map)){ + return false; + } + } + } + + node.seq = n--; + map.put(val, node); + return true; + } +} + + + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Encode and Decode Strings.java b/Others/old records/LeetCode-Merged/Java/Encode and Decode Strings.java new file mode 100644 index 0000000..e445034 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Encode and Decode Strings.java @@ -0,0 +1,104 @@ + +不难,但是要考虑好如何handle ""。 +因为平时都把“” 当做Null对待,这里就犯浑了。 +这题,要把Null特别mark一下为‘NULL’,而特别处理 “” empty string. +``` +/* +Design an algorithm to encode a list of strings to a string. The encoded string is then sent over the network and is decoded back to the original list of strings. + +Machine 1 (sender) has the function: + +string encode(vector strs) { + // ... your code + return encoded_string; +} +Machine 2 (receiver) has the function: +vector decode(string s) { + //... your code + return strs; +} +So Machine 1 does: + +string encoded_string = encode(strs); +and Machine 2 does: + +vector strs2 = decode(encoded_string); +strs2 in Machine 2 should be the same as strs in Machine 1. + +Implement the encode and decode methods. + +Note: +The string may contain any possible characters out of 256 valid ascii characters. Your algorithm should be generalized enough to work on any possible characters. +Do not use class member/global/static variables to store states. Your encode and decode algorithms should be stateless. +Do not rely on any library method such as eval or serialize methods. You should implement your own encode/decode algorithm. + + +Tags: String +Similar Problems: (E) Count and Say, (M) Serialize and Deserialize Binary Tree + +*/ + +/* +Thoughts: +Break into integers +Use some special words to: 1. break line. 2. record null condition. +Note: "" empty string is also a string case, so don't treat that as null. Call null, "NULL" +Note2: As long as the list is not empty, though some string might be just "", make sure to encode it as 'LINE' just to remind in decoder: treat it as a "" +*/ +public class Codec { + // Encodes a list of strings to a single string. + public static String encode(List strs) { + if (strs == null || strs.size() == 0) { + return "NULL"; + } + StringBuffer sb = new StringBuffer(); + for (String str : strs) { + char[] arr = str.toCharArray(); + for (int i = 0; i < arr.length; i++) { + if (arr[i] >= 100) { + sb.append("" + (int)arr[i]); + } else if (arr[i] >= 10) { + sb.append("0" + (int)arr[i]); + } else { + sb.append("00" + (int)arr[i]); + } + } + sb.append("LINE"); + }//END for + if (sb.length() == 0) { + sb.append("LINE"); + } + return sb.toString(); + } + + // Decodes a single string to a list of strings. + public static List decode(String s) { + List rst = new ArrayList(); + if (s.equals("NULL")) { + return rst; + } + int index = s.indexOf("LINE"); + while (index != -1) { + String str = s.substring(0, index); + + StringBuffer sb = new StringBuffer(); + int i = 0; + while (i + 3 <= str.length()) { + int letter = Integer.parseInt(str.substring(i, i + 3)); + sb.append((char)letter); + i+=3; + } + rst.add(sb.toString()); + + s = s.substring(index + 4); + index = s.indexOf("LINE"); + } + + return rst; + } +} +// Your Codec object will be instantiated and called as such: +// Codec codec = new Codec(); +// codec.decode(codec.encode(strs)); + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Excel Sheet Column Number.java b/Others/old records/LeetCode-Merged/Java/Excel Sheet Column Number.java new file mode 100644 index 0000000..fa6385a --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Excel Sheet Column Number.java @@ -0,0 +1,46 @@ +好久以前做的一个题了。 +记得:'A' - 'A' = 0 呀。所以 any char - 'A' + 1 = 这个char在我们这个题目里面所代表的数字。 +其次,26个字母代表着26位运算。和10位一样嘛,那第一位x26就好了。 +``` +/* + +Given a column title as appear in an Excel sheet, return its corresponding column number. + +For example: + + A -> 1 + + B -> 2 + + C -> 3 + + ... + + Z -> 26 + + AA -> 27 + + AB -> 28 + +*/ + +public class Solution {//ABC -> 'A', 'B', 'C' + +public int titleToNumber(String s) {//S = AA + +int rst = 0; + +char[] arr = s.toCharArray(); + +for (int i = 0; i < arr.length; i++) {//i = 0,1,2 // (char c : arr) + +rst = rst * 26 + arr[i] - 'A' + 1;//rst =1, 26 + 1 = 27, + +} + +return rst; + +} + +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/ExcelSheetColumnNumber .java b/Others/old records/LeetCode-Merged/Java/ExcelSheetColumnNumber .java new file mode 100644 index 0000000..c5db099 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/ExcelSheetColumnNumber .java @@ -0,0 +1,25 @@ +/* +Given a column title as appear in an Excel sheet, return its corresponding column number. + +For example: + + A -> 1 + B -> 2 + C -> 3 + ... + Z -> 26 + AA -> 27 + AB -> 28 +*/ + + +public class Solution {//ABC -> 'A', 'B', 'C' + public int titleToNumber(String s) {//S = AA + int rst = 0; + char[] arr = s.toCharArray(); + for (int i = 0; i < arr.length; i++) {//i = 0,1,2 // (char c : arr) + rst = rst * 26 + arr[i] - 'A' + 1;//rst =1, 26 + 1 = 27, + } + return rst; + } +} diff --git a/Others/old records/LeetCode-Merged/Java/Find Peak Element.java b/Others/old records/LeetCode-Merged/Java/Find Peak Element.java new file mode 100644 index 0000000..5ca799b --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Find Peak Element.java @@ -0,0 +1,52 @@ +/* +A peak element is an element that is greater than its neighbors. + +Given an input array where num[i] ≠ num[i+1], find a peak element and return its index. + +The array may contain multiple peaks, in that case return the index to any one of the peaks is fine. + +You may imagine that num[-1] = num[n] = -∞. + +For example, in array [1, 2, 3, 1], 3 is a peak element and your function should return the index number 2. + +Note: +Your solution should be in logarithmic complexity. + +Tags: Array, Binary Search + +*/ + +/* +Thoughts: +Easy brutle force: pick any element, see it's increasing or decresing, then move forward the increasing directin. O(n) +Here, try binary search method +Note: Special case of 3 element, which could run into infinite loop, so check start + 1 < end. (That is, when start, mid, end are 3 adjacent increasing/decresing point, they will run into infinite loop) + +Note2: because of missing case, 1,0,1 case when mid is at '0'. It needs either direction. +*/ +public class Solution { + public int findPeakElement(int[] nums) { + if (nums == null || nums.length <= 1) { + return 0; + } + int start = 0; // 0 + int end = nums.length - 1;// 2 + int mid; + while (start + 1 < end) { + mid = start + (end - start)/2;//1 + if (mid <= 0 || mid >= nums.length - 1) {//start and end are adjacent, jump to end + break; + } + if (nums[mid - 1] < nums[mid] && nums[mid] > nums[mid + 1]) { + return mid; + } else if (nums[mid - 1] < nums[mid] && nums[mid] < nums[mid + 1]) { + start = mid + 1; + } else if (nums[mid - 1] > nums[mid] && nums[mid] > nums[mid + 1]) { + end = mid - 1; + } else {//1,0,1 case + end = mid - 1; + } + }//end while + return nums[start] > nums[end] ? start : end; + } +} diff --git a/Others/old records/LeetCode-Merged/Java/First Bad Version.java b/Others/old records/LeetCode-Merged/Java/First Bad Version.java new file mode 100644 index 0000000..e9e2e62 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/First Bad Version.java @@ -0,0 +1,43 @@ +这个挺直接的,也被李特标了EASY. +就是在1~n里面找第一个出错点。其实就是搜一个数字i(同时用isBadVersion(i))检查这个数字是否报错。那搜index的活,直接binary search就好了。 +注意特别条件: +``` +!isBadVersion(i) && isBadVersion(i+1) +``` +一定要找出这个i+1,就是第一个出错点。 + +还有就是:很可能start == end了呀,如果还没有完结,那么最终的那个情况必然是start==end==mid, 然后既然mid之前没找到错,那么try 一try isBadVersion(mid) 就好了。 +``` +/* +You are a product manager and currently leading a team to develop a new product. Unfortunately, the latest version of your product fails the quality check. Since each version is developed based on the previous version, all the versions after a bad version are also bad. + +Suppose you have n versions [1, 2, ..., n] and you want to find out the first bad one, which causes all the following ones to be bad. + +You are given an API bool isBadVersion(version) which will return whether version is bad. Implement a function to find the first bad version. You should minimize the number of calls to the API. +*/ + +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ + +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + if (n <= 1) { + return isBadVersion(n) ? n : -1; + } + int start = 1; + int end = n; + int mid = start + (end - start) / 2; + while(start < end) { + mid = start + (end - start) / 2; + if (!isBadVersion(mid) && isBadVersion(mid + 1)) { + return mid + 1; + } else if (!isBadVersion(mid)){ + start = mid; + } else { + end = mid; + } + } + return isBadVersion(mid) ? mid : -1; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Flatten 2D Vector.java b/Others/old records/LeetCode-Merged/Java/Flatten 2D Vector.java new file mode 100644 index 0000000..10cfaee --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Flatten 2D Vector.java @@ -0,0 +1,82 @@ +/* +Implement an iterator to flatten a 2d vector. + +For example, +Given 2d vector = + +[ + [1,2], + [3], + [4,5,6] +] +By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,2,3,4,5,6]. + +Hint: + +How many variables do you need to keep track? +Two variables is all you need. Try with x and y. +Beware of empty rows. It could be the first few rows. +To write correct code, think about the invariant to maintain. What is it? +The invariant is x and y must always point to a valid point in the 2d vector. Should you maintain your invariant ahead of time or right when you need it? +Not sure? Think about how you would implement hasNext(). Which is more complex? +Common logic in two different places should be refactored into a common method. + + +Tags: Design +Similar Problems: (M) Binary Search Tree Iterator, (M) Zigzag Iterator, (M) Peeking Iterator + +*/ + +/* +Thoughts: +As hint indicates: use 2 pointers to hold position. +Use hasNext to validate (x,y) and move x. +Use next() to return (x,y) and move it(regardless of correctness, which is determined by hasNext()) +*/ +public class Vector2D { + private int x; + private int y; + private List> list; + public Vector2D(List> vec2d) { + if (vec2d == null) { + return; + } + this.x = 0; + this.y = 0; + this.list = vec2d; + } + + public int next() { + int rst = list.get(x).get(y); + if (y + 1 >= list.get(x).size()) { + y = 0; + x++; + } else { + y++; + } + return rst; + } + + public boolean hasNext() { + if (list == null) { + return false; + } + while (x < list.size() && list.get(x).size() == 0) { + x++; + y = 0; + } + if (x >= list.size()) { + return false; + } + if (y >= list.get(x).size()) { + return false; + } + return true; + } +} + +/** + * Your Vector2D object will be instantiated and called as such: + * Vector2D i = new Vector2D(vec2d); + * while (i.hasNext()) v[f()] = i.next(); + */ \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Flattern 2D Vector.java b/Others/old records/LeetCode-Merged/Java/Flattern 2D Vector.java new file mode 100644 index 0000000..cc01237 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Flattern 2D Vector.java @@ -0,0 +1,88 @@ +大概意思就是把2D list里面的element全部遍历一遍。 +注意啊,一开始理解题意搞错:我以为是必须要排序正确,所以上来就PriorityQueue+HashMap搞得无比复杂。其实,这个跟一个nxn的matrix遍历,是没区别的拉。 +所有来个x,y,把2d list跑一变。 + +``` +/* +Implement an iterator to flatten a 2d vector. + +For example, +Given 2d vector = + +[ + [1,2], + [3], + [4,5,6] +] +By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1,2,3,4,5,6]. + +Hint: + +How many variables do you need to keep track? +Two variables is all you need. Try with x and y. +Beware of empty rows. It could be the first few rows. +To write correct code, think about the invariant to maintain. What is it? +The invariant is x and y must always point to a valid point in the 2d vector. Should you maintain your invariant ahead of time or right when you need it? +Not sure? Think about how you would implement hasNext(). Which is more complex? +Common logic in two different places should be refactored into a common method. + + +Tags: Design +Similar Problems: (M) Binary Search Tree Iterator, (M) Zigzag Iterator, (M) Peeking Iterator + +*/ + +/* +Thoughts: +As hint indicates: use 2 pointers to hold position. +Use hasNext to validate (x,y) and move x. +Use next() to return (x,y) and move it(regardless of correctness, which is determined by hasNext()) +*/ +public class Vector2D { + private int x; + private int y; + private List> list; + public Vector2D(List> vec2d) { + if (vec2d == null) { + return; + } + this.x = 0; + this.y = 0; + this.list = vec2d; + } + + public int next() { + int rst = list.get(x).get(y); + if (y + 1 >= list.get(x).size()) { + y = 0; + x++; + } else { + y++; + } + return rst; + } + + public boolean hasNext() { + if (list == null) { + return false; + } + while (x < list.size() && list.get(x).size() == 0) { + x++; + y = 0; + } + if (x >= list.size()) { + return false; + } + if (y >= list.get(x).size()) { + return false; + } + return true; + } +} + +/** + * Your Vector2D object will be instantiated and called as such: + * Vector2D i = new Vector2D(vec2d); + * while (i.hasNext()) v[f()] = i.next(); + */ +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Flip Game II.java b/Others/old records/LeetCode-Merged/Java/Flip Game II.java new file mode 100644 index 0000000..3fb9bff --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Flip Game II.java @@ -0,0 +1,171 @@ +12.06.2015 recap: +注意:不要乱改input s. recursive call 需要用原始的input s. + +这个题目李特是屌炸天的。 +我飞了九牛二虎之力(路子对),但是代码写的七荤八素,好长好长好长好长的。 +结果正解,三四行就搞定了。真是心有不甘啊。 +想法如下: +保证p1能胜利,就必须保持所有p2的move都不能赢。 +同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +(题目里面用一个for loop + 只要 满足条件就return true来表达 OR的意思:p1不同的路子,赢一种就行了) +p1: player1 +p2: player2 + +``` +/* +You are playing the following Flip Game with your friend: +Given a string that contains only these two characters: + and -, +you and your friend take turns to flip two consecutive "++" into "--". +The game ends when a person can no longer make a move and therefore the other person will be the winner. + +Write a function to determine if the starting player can guarantee a win. + +For example, given s = "++++", return true. The starting player can guarantee a win by flipping the middle "++" to become "+--+". + +Follow up: +Derive your algorithm's runtime complexity. + +Tags: Backtracking +Similar Problems: (E) Nim Game, (E) Flip Game +*/ + +/* + recap: 12.06.2015 + Make sure to use a new string, and do not alter the original input s when calling recursively on canWin. +*/ + +public class Solution { + public static boolean canWin(String s) { + if (s == null || s.length() <= 1) { + return false; + } + String str = new String(s); + while (str.indexOf("++") != -1) { + int index = str.indexOf("++"); + if(!canWin(s.substring(0, index) + "--" + s.substring(index + 2))) { + return true; + } + str = str.substring(0, index) + "-" + str.substring(index + 1); + } + return false; + } + +} + + + +/* +Attemp2, from:http://www.cnblogs.com/jcliBlogger/p/4886741.html +Similar to my idea, but much more clear: no need of the isP1 flag. +Iterative idea:p1 can win, and p2 must not win at all. +Therefore, if p2's move can't win, we return true on p1's state. +For loop and the if statement works as 'OR': just need one of the p1's movement win. +*/ + +public class Solution { + public static boolean canWin(String s) { + if (s == null || s.length() <= 1) { + return false; + } + String str = new String(s); + for (int i = str.indexOf("++"); i >= 0 && i < str.length() - 1; i = str.indexOf("++")) { + if (!canWin( s.substring(0, i) + "--" + s.substring(i + 2))) {//Just pick one way of p1's move + return true; + } + str = str.substring(0, i) + "-" + str.substring(i + 1);//Help to move certain spot. + } + return false; + } + +} +//let k = n/2 +//O(k * (k - 1) * (k - 2) ... k) = O(k!) = O((n/2)!) = O(n!) + +/* +Attempt1, Failed. +Thoughts: +method checkP1Win(), inside of it: +OR all p1's win state, if one of the move wins, return true; +However, a bit of code redundancy, does not feel good about this. +Fails on "+++++++++" +*/ +public class Solution { + public static boolean canWin(String s) { + if (s == null || s.length() <= 1) { + return false; + } + boolean rst = false; + String str = new String(s); + for (int i = str.indexOf("++"); i >= 0 && i < str.length() - 1; i = str.indexOf("++")) { + if (checkP1Win(s, i, true)) { + rst = true; + break; + } + str = str.substring(0, i) + "-" + str.substring(i + 1); + } + return rst; + + } + public static boolean checkP1Win(String str, int x, boolean isP1) { + String s = str.substring(0,x) + "--" + str.substring(x + 2); + if (s.indexOf("++") == -1) { + return isP1; + } + for (int i = s.indexOf("++"); i >= 0 && i < s.length() - 1; i = s.indexOf("++")) { + if (checkP1Win(s, i, !isP1)) { + return true; + } + s = s.substring(0, i) + "-" + s.substring(i + 1); + } + return false; + } + + +} + + + +public class Solution { + public static boolean canWin(String s) { + if (s == null || s.length() <= 1) { + return false; + } + boolean rst = false; + String str = new String(s); + for (int i = str.indexOf("++"); i >= 0 && i < str.length() - 1; i = str.indexOf("++")) { + if (checkP1Win(s, i, true)) { + rst = true; + break; + } + str = str.substring(0, i) + "-" + str.substring(i + 1); + } + return rst; + + } + public static boolean checkP1Win(String str, int x, boolean isP1) { + String s = str.substring(0,x) + "--" + str.substring(x + 2); + if (s.indexOf("++") == -1) { + return isP1; + } + if (isP1) { + String temp = s; + for (int i = temp.indexOf("++"); i >= 0 && i < temp.length() - 1; i = temp.indexOf("++")) { + if (checkP1Win(s, i, !isP1)) { + return true; + } + temp = temp.substring(0, i) + "-" + temp.substring(i + 1); + } + return false; + } else { + String temp = s; + for (int i = temp.indexOf("++"); i >= 0 && i < temp.length() - 1; i = temp.indexOf("++")) { + if (!checkP1Win(s, i, !isP1)) { + return false; + } + temp = temp.substring(0, i) + "-" + temp.substring(i + 1); + } + return true; + } + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Flip Game.java b/Others/old records/LeetCode-Merged/Java/Flip Game.java new file mode 100644 index 0000000..2afa5c1 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Flip Game.java @@ -0,0 +1,78 @@ +这个题目是很寂寞的. 2 pointer可以做, 在网上又搜了一下,貌似可以有很多牛逼的优化,我暂时还没去看。 +很郁闷的就是条件不明,原来只需要从'++'转到'--'的情况,反过来没必要关注...搞了我半天啊 +``` +/* +You are playing the following Flip Game with your friend: Given a string that contains only these two characters: + and -, you and your friend take turns to flip two consecutive "++" into "--". The game ends when a person can no longer make a move and therefore the other person will be the winner. + +Write a function to compute all possible states of the string after one valid move. + +For example, given s = "++++", after one move, it may become one of the following states: + +[ + "--++", + "+--+", + "++--" +] +*/ + +// 12.06.2015, slower than the previous one +public class Solution { + public List generatePossibleNextMoves(String s) { + List rst = new ArrayList(); + if (s == null || s.length() == 0) { + return rst; + } + ArrayList list = new ArrayList(); + StringBuffer sb = new StringBuffer(s); + while (sb.indexOf("++") != -1) { + int index = sb.indexOf("++"); + list.add(index); + sb.replace(index, index + 1, "*"); + } + for (int index : list) { + rst.add(s.substring(0, index) + "--" + s.substring(index + 2)); + } + return rst; + } +} + + +/* +Thoughts: +Two pointers to check if p1 and p2 match target patern. If so, add. + +Need to ask: are we only looking to change to '--' from '++'? +*/ +public class Solution { + public static List generatePossibleNextMoves(String s) { + List rst = new ArrayList(); + if (s == null || s.length() < 1) { + return rst; + } + char[] arr = s.toCharArray(); + search('+','-',arr,rst); + return rst; + } + + public static void search(char target, char replace, char[] arr, List rst) { + int p1 = 0; + int p2 = 1; + while (p2 <= arr.length - 1) { + if (arr[p1] == target && arr[p2] == target) { + arr[p1] = replace; + arr[p2] = replace; + rst.add(new String(arr)); + arr[p1] = target; + arr[p2] = target; + } + p1++; + p2++; + } + } +} + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Fraction to Recurring Decimal.java b/Others/old records/LeetCode-Merged/Java/Fraction to Recurring Decimal.java new file mode 100644 index 0000000..d482da0 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Fraction to Recurring Decimal.java @@ -0,0 +1,121 @@ +不难想到处理除法:考虑正负,考虑小数点前后。主要是小数点以后的要着重考虑。 +很容易忽略的是integer的益处。 +``` +/* +Given two integers representing the numerator and denominator of a fraction, return the fraction in string format. + +If the fractional part is repeating, enclose the repeating part in parentheses. + +For example, + +Given numerator = 1, denominator = 2, return "0.5". +Given numerator = 2, denominator = 1, return "2". +Given numerator = 2, denominator = 3, return "0.(6)". + +Hide Company Tags Google +Show Tags +Hash Table Math + + +*/ + +/* + Thoughts: + Divide it into small pieces: + 1. d = 0, return null; + 2. n = 0 -> 0 + 3. mark negative. let n = abs(numerator), d = abs(denominator) + 4. consider front and end: + front = (int)sharp divide + end: build hashmap to track if the numerator*10 occurs. Once occurs again, return the remianing. + 5. based on sign, return results. + +Note: +Have to take int overflow INT_MAX, INT_MIN.... +*/ +//Optimized code: +public class Solution { + public String fractionToDecimal(int numerator, int denominator) { + long nume = Math.abs((long)numerator); + long deno = Math.abs((long)denominator); + String sign = (numerator < 0) ^ (denominator < 0) ? "-" : ""; + if (deno == 0) { + return ""; + } else if (nume == 0) { + return "0"; + } else if (nume % deno == 0) { + return sign + nume/deno + ""; + } + + HashMap map = new HashMap(); + StringBuffer rst = new StringBuffer(sign + nume/deno + "."); + long end = nume%deno * 10;//The decimal portion of the value, after decimal point + int i = 0; + while (end != 0){ + if (map.containsKey(end)) { + rst.insert(rst.indexOf(".") + map.get(end) + 1, "("); + rst.append(")"); + return rst.toString(); + } + rst.append(end/deno); + map.put(end, i++); + end = (end % deno) * 10; + } + return rst.toString(); + } +} + + + +//Original working version +public class Solution { + public String fractionToDecimal(int numerator, int denominator) { + long nume = Math.abs((long)numerator); + long deno = Math.abs((long)denominator); + String sign = (numerator < 0) ^ (denominator < 0) ? "-" : ""; + if (deno == 0) { + return ""; + } else if (nume == 0) { + return "0"; + } else if (nume % deno == 0) { + return sign + nume/deno + ""; + } + + String rst = sign + nume/deno + "."; + long end = nume%deno * 10; + + HashMap map = new HashMap(); + boolean repeat = false; + String endRepeat = ""; + int n = 0; + while (true){ + if (end == 0) { + break; + } else if (map.containsValue(end)) { + repeat = true; + break; + } + map.put(n++, end); + end = (end % deno) * 10; + } + + for (int i = 0; i < n; i++) { + if (repeat && map.get(i) == end) { + rst += "(" + map.get(i)/deno; + endRepeat = ")"; + repeat = false; + } else { + rst += map.get(i)/deno; + } + } + + return rst + endRepeat; + } +} + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Game of Life.java b/Others/old records/LeetCode-Merged/Java/Game of Life.java new file mode 100644 index 0000000..2799713 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Game of Life.java @@ -0,0 +1,42 @@ +/* +According to the Wikipedia's article: +"The Game of Life, also known simply as Life, is a cellular automaton devised by the British mathematician John Horton Conway in 1970." + +Given a board with m by n cells, each cell has an initial state live (1) or dead (0). +Each cell interacts with its eight neighbors (horizontal, vertical, diagonal) using the following four rules +(taken from the above Wikipedia article): + +Any live cell with fewer than two live neighbors dies, as if caused by under-population. +Any live cell with two or three live neighbors lives on to the next generation. +Any live cell with more than three live neighbors dies, as if by over-population.. +Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction. +Write a function to compute the next state (after one update) of the board given its current state. + +Follow up: +Could you solve it in-place? Remember that the board needs to be updated at the same time: +You cannot update some cells first and then use their updated values to update other cells. +In this question, we represent the board using a 2D array. + In principle, the board is infinite, which would cause problems when the active area encroaches the border of the array. + How would you address these problems? +Credits: +Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases. + +Hide Company Tags Google TinyCo +Hide Tags Array +Hide Similar Problems (M) Set Matrix Zeroes + +*/ + +/* + Thoughts: + https://segmentfault.com/a/1190000003819277 + http://my.oschina.net/Tsybius2014/blog/514447 + build state machine. + take mod of 2 at the end. +*/ + +public class Solution { + public void gameOfLife(int[][] board) { + + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Generate Parentheses.java b/Others/old records/LeetCode-Merged/Java/Generate Parentheses.java new file mode 100644 index 0000000..bba7814 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Generate Parentheses.java @@ -0,0 +1,73 @@ +/* +Given n pairs of parentheses, write a function to generate all combinations of well-formed parentheses. + +For example, given n = 3, a solution set is: + +"((()))", "(()())", "(())()", "()(())", "()()()" + +Hide Company Tags Google Zenefits +Hide Tags Backtracking String +Hide Similar Problems (M) Letter Combinations of a Phone Number (E) Valid Parentheses + +*/ + +/* + Thoughts: + Pick ( or not. + Pick ) or not. + Back tracking. + when #"(" and #")" are both n, return the result. +*/ +public class Solution { + public List generateParenthesis(int n) { + List rst = new ArrayList(); + if (n <= 0) { + return rst; + } + ArrayList list = new ArrayList(); + helper(rst, list, 0, 0, n); + + return rst; + } + + public void helper(List rst, ArrayList list, + int left, int right, int n) { + if (left == n && right == n) { + StringBuffer sb = new StringBuffer(); + for (String s : list) { + sb.append(s); + } + rst.add(sb.toString()); + return; + } + //add ( + if (left < n) { + list.add("("); + helper(rst, list, left + 1, right, n); + list.remove(list.size() - 1); + } + //add ) + if (right < left) { + list.add(")"); + helper(rst, list, left, right + 1, n); + list.remove(list.size() - 1); + } + } +} + + + + + + + + + + + + + + + + + diff --git a/Others/old records/LeetCode-Merged/Java/Graph Valid Tree.java b/Others/old records/LeetCode-Merged/Java/Graph Valid Tree.java new file mode 100644 index 0000000..2838d70 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Graph Valid Tree.java @@ -0,0 +1,87 @@ +/* +Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), +write a function to check whether these edges make up a valid tree. + +For example: + +Given n = 5 and edges = [[0, 1], [0, 2], [0, 3], [1, 4]], return true. + +Given n = 5 and edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], return false. + +Hint: + +Given n = 5 and edges = [[0, 1], [1, 2], [3, 4]], what should your return? Is this case a valid tree? +According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactly one path. +In other words, any connected graph without simple cycles is a tree.” +Note: you can assume that no duplicate edges will appear in edges. +Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges. + +Hide Company Tags Google Facebook Zenefits +Hide Tags Depth-first Search Breadth-first Search Graph Union Find +Hide Similar Problems (M) Course Schedule + +*/ + +/* + Thoughts: + The edge here has no directin. + The goal is to check if the graphc is connected && if there is cycle. + Do union-find. + Check each pair: If they are in the same set: do they share same parent? + If they do, that makes a triangle between this node elementsi the pair and their parent. That is a cycle. False. + If no common parent, union them into 1 set. + + find(x): recursively set the current node equals to it's parent node's parent's parent's .... partent's node. + Via just 1 call to x, it will update all parent nodes to equal to the root parent lol +*/ + + +public class Solution { + int[] parents; + public boolean validTree(int n, int[][] edges) { + //edges can be [], when n == 1, it'll be true. + if (edges.length != n - 1) { + return false; + } + parents = new int[n]; + //Init + for (int i = 0; i < parents.length; i++) { + parents[i] = i; + } + //Use union-find to detect if pair has common parents, and merge then to 1 set + for (int i = 0; i < edges.length; i++){ + if (find(edges[i][0]) == find(edges[i][1])) { + return false; + } + union(edges[i][0], edges[i][1]); + } + + return true; + } + + /* + Not only find parent, also update the spot parents[node] with parent node, recursively. + */ + public int find(int node) { + if (parents[node] == node) { + return node; + } + parents[node] = find(parents[node]); + return parents[node]; + } + + public void union(int x, int y) { + int findX = parents[x]; + int findY = parents[y]; + if (findX != findY) { + parents[findX] = findY; + } + } +} + + + + + + + diff --git a/Others/old records/LeetCode-Merged/Java/Gray Code.java b/Others/old records/LeetCode-Merged/Java/Gray Code.java new file mode 100644 index 0000000..e6287cf --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Gray Code.java @@ -0,0 +1,144 @@ +李特这特题目有点蛋疼,因为目前只接受一种结果。 +我做的恰好和它要的结果不一样,但是我觉得我这种走法走出来也是没错的。 +基本想法就是从一个点开始往一个方向走,每次flip一个bit, 碰壁的时候就回头走。 +``` +/* + +The gray code is a binary numeral system where two successive values differ in only one bit. + +Given a non-negative integer n representing the total number of bits in the code, print the sequence of gray code. A gray code sequence must begin with 0. + +For example, given n = 2, return [0,1,3,2]. Its gray code sequence is: + +00 - 0 + +01 - 1 + +11 - 3 + +10 - 2 + +Note: + +For a given n, a gray code sequence is not uniquely defined. + +For example, [0,2,3,1] is also a valid gray code sequence according to the above definition. + +For now, the judge is able to judge based on one instance of gray code sequence. Sorry about that. + +Tags:Backtracking + +*/ + +/* + +Leetcode tags shows backtracking. That should be different approach than what I hvae below: + +*/ + +/* + +TRY: My code works for this run-through, however does not fit the OJ yet + +0 0 0 [start, noting happend, flip index 0] + +0 0 <-1 [move to flip left adjacent] + +0 <-1 1 [move to flip left adjacent] + +1-> 1 1 [move to flip right adjacent] + +1 0-> 1 [move to flip right adjacent] + +1 0 <-0 [move to flip left adjacent] + +1 <-1 0 [move to flip left adjacent] + +0 1 0 [done] + +Conclusion: hit the wall and flip the other direction. + +Every flip, add integer to list + +Convert the char[] to string, then Integer.parse(str, 2) to integer + +Simulate the steps: + +For example, when n = 3, step = n - 1. It takes 2 steps from right side to reach left side, it hits the wall and turn around. + +Now: + +1. Initialize char[] and add '000' + +2. do for loop on 1 ~ 2^n -2. last step '010' is stepped into, but no further action, so take 2^3 - 1 = 7 steps. + +*/ + +public class Solution { + +public List grayCode(int n) { + +List rst = new ArrayList(); + +if (n < 0) { + +return rst; + +} + +char[] bits = new char[n]; + +for (int i = 0; i < bits.length; i++) { + +bits[i] = '0'; + +} + +String str = new String(bits); + +if (n == 0) { + +str = "0"; + +} + +rst.add(Integer.parseInt(str, 2)); + +int step = n - 1; + +boolean LR = true;//L: true; R: false + +int steps = (int)Math.pow(2, n) - 1; + +for (int i = 0; i < steps; i++) { + +bits[step] = bits[step] == '0' ? '1' : '0'; + +str = new String(bits); + +rst.add(Integer.parseInt(str, 2)); + +if (LR) { + +step--; + +} else { + +step++; + +} + +if (step == (n - 1) || step == 0) {//Turn around + +LR = !LR; + +} + +} + +return rst; + +} + +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Group Anagrams.java b/Others/old records/LeetCode-Merged/Java/Group Anagrams.java new file mode 100644 index 0000000..f2aff48 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Group Anagrams.java @@ -0,0 +1,62 @@ +/* +Given an array of strings, group anagrams together. + +For example, given: ["eat", "tea", "tan", "ate", "nat", "bat"], +Return: + +[ + ["ate", "eat","tea"], + ["nat","tan"], + ["bat"] +] +Note: +For the return value, each inner list's elements must follow the lexicographic order. +All inputs will be in lower-case. +Update (2015-08-09): +The signature of the function had been updated to return list> instead of list, as suggested here. If you still see your function signature return a list, please click the reload button to reset your code definition. + +Tags: Hash Table String +Similar Problems: (E) Valid Anagram, (E) Group Shifted Strings + +*/ + +/* +Thoughts: +Store the anagram in a order list. Collections.sort it. +Store all the anagram strings into +Colelction.sort all lists and put them into final result. + +Note: use Arrays.sort() to sort string. +Note2: can do (element : array, arraylist) in for loop + +*/ +public class Solution { + public List> groupAnagrams(String[] strs) { + List> rst = new ArrayList>(); + if (strs == null) { + return rst; + } + + List order = new ArrayList(); + HashMap> map = new HashMap>(); + for (String str : strs) { + char[] arr = str.toCharArray(); + Arrays.sort(arr); + String s = new String(arr); + if (!map.containsKey(s)){ + List l = new ArrayList(); + l.add(str); + order.add(s); + map.put(s, l); + } else { + map.get(s).add(str); + } + } + Collections.sort(order); + for (String str : order) { + Collections.sort(map.get(str)); + rst.add(map.get(str)); + } + return rst; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/H-Index II.java b/Others/old records/LeetCode-Merged/Java/H-Index II.java new file mode 100644 index 0000000..ec03820 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/H-Index II.java @@ -0,0 +1,59 @@ +H-index的一个优化。 +binary search +``` +/* +Follow up for H-Index: What if the citations array is sorted in ascending order? +Could you optimize your algorithm? +Hint: + +Expected runtime complexity is in O(log n) and the input is sorted. + +Hide Company Tags Facebook +Hide Tags Binary Search +Hide Similar Problems (M) H-Index + +*/ + +/* + citations[0,1,3,5,6] + look for a h, where x = N-h, arr[x] >= h + h is from right ot left. + We want to find smallest x that has arr[x] >= n-x + binary search: + start,mid,end + if match, keep going left until not able to + O(nLogN) +*/ + +public class Solution { + public int hIndex(int[] citations) { + if (citations == null || citations.length == 0) { + return 0; + } + int n = citations.length; + int start = 0; + int end = n - 1; + int mid; + while (start + 1 < end) { + mid = start + (end - start)/2; + if (citations[mid] == n - mid) { + if (mid - 1 >= 0 && citations[mid - 1] == n - (mid-1)) { + end = mid; + } else { + return n - mid;// that is n - x + } + } else if (citations[mid] < n - mid) { + start = mid; + } else { + end = mid; + } + } + if (citations[start] >= n - start) { + return n - start; + } else if (citations[end] >= n - end) { + return n - end; + } + return 0; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/H-Index.java b/Others/old records/LeetCode-Merged/Java/H-Index.java new file mode 100644 index 0000000..d062c09 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/H-Index.java @@ -0,0 +1,111 @@ +例子写出来,发现可以sort以后按照定义搜索一遍。 nlogn. + 当然,搜索一遍时候可以优化,用binary search. 但是没意义,因为array.sort已经用了nlogn + +o(n)也可以,用bucket. 比较巧妙。 + + +``` +/* +Given an array of citations (each citation is a non-negative integer) of a researcher, +write a function to compute the researcher's h-index. + +According to the definition of h-index on Wikipedia: "A scientist has index h if h of his/her N papers +have at least h citations each, +and the other N − h papers have no more than h citations each." + +For example, given citations = [3, 0, 6, 1, 5], which means the researcher has 5 papers in total and +each of them had received 3, 0, 6, 1, 5 citations respectively. +Since the researcher has 3 papers with at least 3 citations each and the remaining two with no more than 3 citations each, his h-index is 3. + +Note: If there are several possible values for h, the maximum one is taken as the h-index. + +Hint: + +An easy approach is to sort the array first. +What are the possible values of h-index? +A faster approach is to use extra space. + +Google Facebook +Hide Tags Hash Table Sort +Hide Similar Problems (M) H-Index II + +*/ + +/* + Thoughts:O(nlogn) + N = 5, so max of h = 5. min of h = 0. + 1. h = 5, loop through the array and count the # of citations that are >= h. + 2 .h = 4 ... h=1, h=0. + => O(n^2). + + If sort it : 0,1,3,5,6 + Find find index x = N - h, and arr[x] >= h + that becomes find index x that arr[x] >=h ,where h = N - x. + Foor loop on h, O(n) + pick x = N - h, and check if arr[x] >= h + h = 5, x = 5 -5 = 0. arr[x] = 0 < h. not working + h = 4, x = 5 - 4 = 1. arr[x] = 1. no. + h=3,x=5-3 =2,arr[x]=3 3>=3, okay. + nLogn + N = O(nlogn) + + +*/ +public class Solution { + public int hIndex(int[] citations) { + if (citations == null || citations.length == 0) { + return 0; + } + Arrays.sort(citations); + for (int h = citations.length; h > 0; h--) { + int x = citations.length - h; + if (citations[x] >= h) { + return h; + } + } + return 0; + } +} + + +/* + Thoughts: as the hint shows, use extra space and make it faster. + citations = [3, 0, 6, 1, 5], + (http://buttercola.blogspot.com/2015/09/leetcode-h-index.html?_sm_au_=iHVFjb76ZHj7ND5D) + 1. For loop to count++ in correct buttkit regardless of the index. + If arr[x] <= n, then bucket[arr[x]]++. that means, the bucket with index of arr[x] should store this arr[x] element. + If arr[x] > n, well, that means it exceeds bucket.length (in this application, it means it's already greater than the max of h=n) + so let's just put it in bucket[n]++. + Obvisouly, we need bucket[n + 1] + 2. Each bucket slot now stores #of values that's >= it's index h, in ascending order ofcourse. + so we can do another for loop, to sum one by one, from h = n ~ 0, (because we need higest possible h) + if sum >= h, that is the rst. +*/ +public class Solution { + public int hIndex(int[] citations) { + if (citations == null || citations.length == 0) { + return 0; + } + int n = citations.length; + int[] bucket = new int[n + 1]; + //fill bucket + for (int i = 0; i < n; i++) { + int bucketSlot = citations[i]; + if (citations[i] <= n) { + bucket[bucketSlot]++; + } else {//bucketSlot > n + bucket[n]++; + } + } + + //Find best H + int sum = 0; + for (int h = n; h >= 0; h--) { + if (sum + bucket[h] >= h) { + return h; + } + sum += bucket[h]; + } + return 0; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Implement Trie (Prefix Tree).java b/Others/old records/LeetCode-Merged/Java/Implement Trie (Prefix Tree).java new file mode 100644 index 0000000..9f0f47b --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Implement Trie (Prefix Tree).java @@ -0,0 +1,111 @@ +Trie自己不多用到。 +如果是遇到一个一个字查询的题,可以考虑一下。 +构建TrieNode的时候要注意:如何找孩子?如果是个map的话,其实就挺好走位的。 +而且,每个node里面的 char 或者string有时候用处不大, +可以为空。但是有些题目,比如在结尾要return一些什么String,就可以在end string那边存一个真的String。 + + +``` +/* +Implement a trie with insert, search, and startsWith methods. + +Note: +You may assume that all inputs are consist of lowercase letters a-z. + +Tags: Trie Design +Similar Problems: (M) Add and Search Word - Data structure design + +*/ +/* +Thoughts: +Understand how prefix tree works, create TrieNode() smartly. (http://www.cnblogs.com/tonyluis/p/4563990.html) +Insert: whenever node does not exist, create a new node. +Search: search through each node, if any node not exist, false; at the end, that node has to be end of that path. +StartWith: very similar to search; at the end, as long as all nodes appear through the path, just return true. + +Note: +The contents of the TrieNode can be just a char, or it can be empty String except the end string: for that case +when we need return the end string. +Also, search and startWith is quite similar, remember that. +*/ + +class TrieNode { + char c; + boolean isEnd = false; + HashMap children = new HashMap(); + // Initialize your data structure here. + public TrieNode() { + } + public TrieNode(char c) { + this.c = c; + } +} + +public class Trie { + private TrieNode root; + + public Trie() { + root = new TrieNode(); + } + + // Inserts a word into the trie. + public void insert(String word) { + TrieNode node = root; + HashMap children = node.children; + for (int i = 0; i < word.length(); i++) { + char c = word.charAt(i); + if (!children.containsKey(c)) { + TrieNode newNode = new TrieNode(c); + children.put(c, newNode); + } + node = children.get(c); + children = node.children; + if (i == word.length() - 1) { + node.isEnd = true; + } + } + } + + // Returns if the word is in the trie. + public boolean search(String word) { + TrieNode node = root; + HashMap children = node.children; + for (int i = 0; i < word.length(); i++) { + char c = word.charAt(i); + if (!children.containsKey(c)) { + return false; + } else { + node = children.get(c); + children = node.children; + } + if (i == word.length() - 1) { + return node.isEnd; + } + } + return false; + } + + // Returns if there is any word in the trie + // that starts with the given prefix. + public boolean startsWith(String prefix) { + TrieNode node = root; + HashMap children = node.children; + for (char c : prefix.toCharArray()) { + if (!children.containsKey(c)) { + return false; + } else { + node = children.get(c); + children = node.children; + } + } + return true; + } + + +} + +// Your Trie object will be instantiated and called as such: +// Trie trie = new Trie(); +// trie.insert("somestring"); +// trie.search("key"); +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Implement strStr().java b/Others/old records/LeetCode-Merged/Java/Implement strStr().java new file mode 100644 index 0000000..4d95a0b --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Implement strStr().java @@ -0,0 +1,48 @@ +还挺多坑的. +1. border condition。 如果 haystack.length() < needle.length() 的话,必须错。但是这个可以优化省略。 +2. 当S2是“”的时候,也就是能在s1的其实位置找到s2....index = 0. +3. 记得如何在s1里面找s2. 就是把遍历s1的 i , 加上遍历s2的 j。 + +优化: +1. s1, s2长短可以不比较。因为forloop的时候: s1.length() - s2.length() + 1,如果s2长于s1,这里自然就断了。 +2. if(s1.charAT(i+j) == s2.charAT(j)). 可以省略。For loop 里面就Check到这个了。 +``` +/* +Implement strStr(). + +Returns the index of the first occurrence of needle in haystack, or -1 if needle is not part of haystack. + +Hide Company Tags Facebook +Hide Tags Two Pointers String +Hide Similar Problems (H) Shortest Palindrome + +*/ +public class Solution { + public int strStr(String haystack, String needle) { + if (needle == null || needle.length() == 0) { + return 0; + } + /* + else if (haystack == null || haystack.length() == 0 + || haystack.length() < needle.length()) { + return -1; + }*/ + int i = 0; + int j = 0; + + for (i = 0; i < haystack.length() - needle.length() + 1; i++) { + // if (haystack.charAt(i + j) == needle.charAt(j)) { + for (j = 0; j < needle.length(); j++) { + if (haystack.charAt(i + j) != needle.charAt(j)) { + break; + } + } + if (j == needle.length()) { + return i; + } + // } + } + return -1; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Insert Interval.java b/Others/old records/LeetCode-Merged/Java/Insert Interval.java new file mode 100644 index 0000000..1cdb29d --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Insert Interval.java @@ -0,0 +1,124 @@ +扫描线问题。 +Interval 拆点,PriorityQueue排点。 +Merge时用count==0作判断点。 + +PriorityQueue: O(logN). 扫n点,总共:O(nLogn) + +``` +/* +Given a set of non-overlapping intervals, insert a new interval into the intervals (merge if necessary). + +You may assume that the intervals were initially sorted according to their start times. + +Example 1: +Given intervals [1,3],[6,9], insert and merge [2,5] in as [1,5],[6,9]. + +Example 2: +Given [1,2],[3,5],[6,7],[8,10],[12,16], insert and merge [4,9] in as [1,2],[3,10],[12,16]. + +This is because the new interval [4,9] overlaps with [3,5],[6,7],[8,10]. + +Tags:Array, Sort +Similar Problems: (H) Merge Intervals + + +*/ + +/* +Thoughts: +What's the difference from merge intervals? +1. Create Class point (x, flag) +2. sort point in min-heap +3. when count increase and decreases to 0, that means we can close an interval +*/ + +/** + * Definition for an interval. + * public class Interval { + * int start; + * int end; + * Interval() { start = 0; end = 0; } + * Interval(int s, int e) { start = s; end = e; } + * } + */ + +public class Solution { + + class Point { + int x; + int flag; + public Point(int x, int flag) { + this.x = x; + this.flag = flag; + } + } + public List insert(List intervals, Interval newInterval) { + List rst = new ArrayList(); + if (intervals == null && newInterval == null) { + return rst; + } else if (intervals == null) { + rst.add(newInterval); + return rst; + } else if (newInterval == null) { + return intervals; + } + + PriorityQueue queue = new PriorityQueue(1, new Comparator(){ + public int compare(Point a, Point b){ + return a.x - b.x; + } + }); + + for (Interval range: intervals) { + queue.add(new Point(range.start, 1)); + queue.add(new Point(range.end, -1)); + } + + queue.add(new Point(newInterval.start, 1)); + queue.add(new Point(newInterval.end, -1)); + + int count = 0; + int startNew = 0; + int endNew = 0; + while (!queue.isEmpty()) { + Point p = queue.poll(); + if (count == 0) { + startNew = p.x; + } + count += p.flag; + + while (!queue.isEmpty() && p.x == queue.peek().x) { + p = queue.poll(); + count += p.flag; + } + + if (count == 0) { + endNew = p.x; + Interval addInterval = new Interval(startNew, endNew); + rst.add(addInterval); + } + + }//end while + + return rst; + + } +} + + + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Integer to English Words.java b/Others/old records/LeetCode-Merged/Java/Integer to English Words.java new file mode 100644 index 0000000..c6f8e58 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Integer to English Words.java @@ -0,0 +1,90 @@ +/* +Convert a non-negative integer to its english words representation. Given input is guaranteed to be less than 2^31 - 1. + +For example, +123 -> "One Hundred Twenty Three" +12345 -> "Twelve Thousand Three Hundred Forty Five" +1234567 -> "One Million Two Hundred Thirty Four Thousand Five Hundred Sixty Seven" +Hint: + +Did you see a pattern in dividing the number into chunk of words? For example, 123 and 123000. +Group the number by thousands (3 digits). You can write a helper function that takes a number less than 1000 and convert just that chunk to words. +There are many edge cases. What are some good test cases? Does your code work with input such as 0? Or 1000010? (middle chunk is zero and should not be printed out) +Tags: Math, String +Similar Problems: (M) Integer to Roman + +Thoughts: +2^31 - 1 = 2,147,483,647 +Trillion, Billion, Million, Thousand, Hundred, Ninty .... Ten, Nine ... One, Zero. + +1. Break the words to up to 4 parts: new break[4] by /(1000 ^ i) +2. For each i, deal with that 3-digit number in break[i] +3. Append corresponding words for each break[i] + +Special case: +zero +000 in one break[i]: skip the whole thing +*/ + + +public class Solution { + public String[] v1 = {"", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine", "Ten", "Eleven", "Twelve", "Thirteen", "Fourteen", "Fifteen", "Sixteen", "Seventeen", "Eighteen", "Nineteen"}; + public String[] v2 = {"", "", "Twenty", "Thirty", "Forty", "Fifty", "Sixty", "Seventy", "Eighty", "Ninety"}; + public String numberToWords(int num) { + if (num < 0) { + return ""; + } + if (num == 0) { + return "Zero"; + } + String rst = ""; + for (int i = 0; i < 4; i++) { + int partial = num - (num / 1000) * 1000; + if (partial > 0) { + rst = helper(partial, i) + " " + rst; + } + num /= 1000; + } + while (rst.charAt(rst.length() - 1) == ' ') { + rst = rst.substring(0, rst.length() - 1); + } + return rst; + } + + public String helper(int num, int i) { + String str = ""; + if (num >= 100) { + int hund = num / 100; + str = v1[hund] + " Hundred "; + num = num % 100; + } + + if (num < 20) { + str += v1[num] + " "; + } else { + int numTens = num / 10; + int numDigit = num % 10; + str += v2[numTens] + " "; + str += v1[numDigit] + " "; + } + + while (str.charAt(str.length() - 1) == ' ') { + str = str.substring(0, str.length() - 1); + } + + //depending on i: + switch (i) { + case 1 : + str += " Thousand"; + break; + case 2 : + str += " Million"; + break; + case 3 : + str += " Billion"; + break; + } + + return str; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Intersection of Two Linked Lists.java b/Others/old records/LeetCode-Merged/Java/Intersection of Two Linked Lists.java new file mode 100644 index 0000000..bbb28a9 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Intersection of Two Linked Lists.java @@ -0,0 +1,100 @@ +/* +Write a program to find the node at which the intersection of two singly linked lists begins. + + +For example, the following two linked lists: + +A: a1 → a2 + ↘ + c1 → c2 → c3 + ↗ +B: b1 → b2 → b3 +begin to intersect at node c1. + + +Notes: + +If the two linked lists have no intersection at all, return null. +The linked lists must retain their original structure after the function returns. +You may assume there are no cycles anywhere in the entire linked structure. +Your code should preferably run in O(n) time and use only O(1) memory. +Credits: +Special thanks to @stellari for adding this problem and creating all test cases. + +Tags: Linked List + +*/ +/* +Thgouts: +Check length of A,B +Start comparing from same distance from end. +*/ + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + if (headA == null && headB == null) { + return headA; + } + //Get size: + ListNode node = headA; + int sizeA = 0; + while(node != null) { + sizeA++; + node = node.next; + } + node = headB; + int sizeB = 0; + while(node != null) { + sizeB++; + node = node.next; + } + + ListNode nodeA = headA; + ListNode nodeB = headB; + int lenDiff = (sizeA >= sizeB ? 1 : -1) * (sizeA - sizeB); + node = sizeA >= sizeB ? headA : headB; + while (lenDiff > 0) { + node = node.next; + lenDiff--; + } + if (sizeA >= sizeB) { + nodeA = node; + } else { + nodeB = node; + } + while (nodeA != null) { + if (nodeA.val == nodeB.val) { + return nodeA; + } else { + nodeA = nodeA.next; + nodeB = nodeB.next; + } + }//END while + return null; + } +} + + + + + + + + + + + + + + diff --git a/Others/old records/LeetCode-Merged/Java/Jump Game.java b/Others/old records/LeetCode-Merged/Java/Jump Game.java new file mode 100644 index 0000000..b9df0a6 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Jump Game.java @@ -0,0 +1,38 @@ +LintCode做过。 +用一个farest can go 来做个一个点的DP,记录可以跑到得最远的地方。 +``` +/* +Given an array of non-negative integers, you are initially positioned at the first index of the array. + +Each element in the array represents your maximum jump length at that position. + +Determine if you are able to reach the last index. + +For example: +A = [2,3,1,1,4], return true. + +A = [3,2,1,0,4], return false. + +Hide Tags Array Greedy + +*/ + +public class Solution { + public boolean canJump(int[] nums) { + if (nums == null || nums.length <= 1) { + return true; + } + int farest = 0; + for (int i = 0; i < nums.length; i++) { + farest = Math.max(farest, nums[i] + i); + if (farest >= nums.length - 1) { + return true; + } + if (farest <= i) { + return false; + } + } + return true; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Kth Smallest Element in a BST.java b/Others/old records/LeetCode-Merged/Java/Kth Smallest Element in a BST.java new file mode 100644 index 0000000..2b70b2a --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Kth Smallest Element in a BST.java @@ -0,0 +1,127 @@ +很容想到Inorder-binary-search-tree Traversal +Recursive 不难,然后稍微优化一下,确保rst.size() == k 时候,就可以return了。 +Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +``` +/* +Given a binary search tree, write a function kthSmallest to find the kth smallest element in it. + +Note: +You may assume k is always valid, 1 ≤ k ≤ BST's total elements. + +Follow up: +What if the BST is modified (insert/delete operations) often and you need to find the kth smallest frequently? How would you optimize the kthSmallest routine? + +Hint: + +Try to utilize the property of a BST. +What if you could modify the BST node's structure? +The optimal runtime complexity is O(height of BST). +Credits: +Special thanks to @ts for adding this problem and creating all test cases. + +Hide Company Tags Google +Hide Tags Tree Binary Search +Hide Similar Problems (M) Binary Tree Inorder Traversal + +*/ + +/* + + Based on binary seach tree, just do a in-order-traversal. + Store in rst. +*/ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + + +/* + //Iterative + + Add all left. + pop top (which will be left-most node) + set node = node.right; + if right != null, add to stack. Will trigger the left-adding-while-loop + if right == null, now node = null. Will not trigger the left-adding-whilte-loop +*/ +public class Solution { + public int kthSmallest(TreeNode root, int k) { + if (root == null || k <= 0) { + return -1; + } + + Stack stack = new Stack(); + stack.push(root); + TreeNode node = root; + while(!stack.isEmpty()) { + //Left first + while (node != null && node.left != null) { + stack.add(node.left); + node = node.left; + } + //Process left/curr + node = stack.pop(); + k--; + if (k == 0) { + return node.val; + } + node = node.right; + if (node != null) { + stack.push(node); + } + } + + return -1; + } + +} + + + +// Recursive +public class Solution { + public int kthSmallest(TreeNode root, int k) { + if (root == null || k <= 0) { + return -1; + } + + ArrayList rst = new ArrayList(); + helper(rst, root, k); + + if (rst.size() < k) { + return -1; + } + return rst.get(k - 1).val; + } + + + public void helper(ArrayList rst, TreeNode node, int k) { + if (rst.size() == k) { + return; + } + if (node.left == null && node.right == null) { + rst.add(node); + return; + } + + if (node.left != null) { + helper(rst, node.left, k); + } + rst.add(node); + if (node.right != null) { + helper(rst, node.right, k); + } + } +} + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/LRU Cache.java b/Others/old records/LeetCode-Merged/Java/LRU Cache.java new file mode 100644 index 0000000..3a20665 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/LRU Cache.java @@ -0,0 +1,170 @@ +当李特第一次把这题拿出来的时候,我是慌的。 +啥是LRU Cache啊? 接下去看吧。 + +后来,我很天真的来了一个O(n) 的解法,结果果然时间过多。 +天真解法很简单啊:一个map存数值。一个queue来存排位。 +每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + +于是就来了第二个做法。其实还是跟方法一是类似的。只不过用了一个特别的双向的LinkNode,有了head和tail,这样就大大加快了速度。 +主要加快的就是那个‘更新排位’的过程,过去我是O(n),现在O(1)就好了。具体看下面。 + +巧妙点: +1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 + +李特的这个Design的题目很有意思。果然值得被作为Hard。但是一旦知道怎么解决了,就不是很特别,因为并不是难想的算法。 + +```` +/* +Design and implement a data structure for Least Recently Used (LRU) cache. +It should support the following operations: get and set. + +get(key) - Get the value (will always be positive) of the key if the key exists in the cache, otherwise return -1. +set(key, value) - Set or insert the value if the key is not already present. When the cache reached its capacity, it should invalidate the least recently used item before inserting a new item. + +Tags: Design + +*/ +/* +It looks like we need to do some design, according to (http://www.cnblogs.com/yuzhangcmu/p/4113462.html). Though, the solution's concept is quite similar as attempt1. + +1. The design uses HashMap, and 2-way LinkedList. Map +2. Use two dummy node: head and tail. When add/remove nodes, we are add/remove nodes in between head and tail. + So. head.next is our real 1st element + andd tail.pre is our real last element + +Note: +Be careful: when removing a node due to capacity issue, remember to remove both 1st node(head.next) and the corresponding entry in the map: map.remove(head.next.key) +*/ +public class LRUCache { + private class LinkNode { + int key; + int val; + LinkNode pre = null; + LinkNode next = null; + LinkNode(int key, int val) { + this.key = key; + this.val = val; + } + } + + private int cap; + private HashMap map; + private LinkNode head; + private LinkNode tail; + public LRUCache(int capacity) { + this.cap = capacity; + this.map = new HashMap(); + this.head = new LinkNode(-1, -1); + this.tail = new LinkNode(-1, -1); + head.next = tail; + tail.pre = head; + } + + public int get(int key) { + if (map.containsKey(key)) { + LinkNode temp = map.get(key); + moveUsedToTail(temp); + return temp.val; + } else { + return -1; + } + } + + public void set(int key, int value) { + if (map.containsKey(key)) { + LinkNode temp = map.get(key); + temp.val = value; + moveUsedToTail(temp); + } else { + if (map.size() >= cap) { + map.remove(head.next.key); + removeHead(); + } + LinkNode newNode = new LinkNode(key, value); + addTail(newNode); + map.put(key, newNode); + } + } + + public void moveUsedToTail(LinkNode node) { + removeNode(node); + addTail(node); + } + + public void removeHead(){//O(1) + removeNode(head.next); + } + public void addTail(LinkNode node){//O(1) + tail.pre.next = node; + node.pre = tail.pre; + node.next = tail; + tail.pre = node; + } + public void removeNode(LinkNode node) {//O(1) + node.pre.next = node.next; + node.next.pre = node.pre; + } +} + +```` +```` +/* +First Attempt: time exceeds + +Thoughts: +Easy to implement: cache the least-added item. However, we are looking for the cache of 'leaset-recently-used item'. + +'used' means the get() method: +whenever get, remove that key from the queue, and move that key to top. + +Time Cost: O(n) on get(), set() + + +*/ + +public class LRUCache { + private int cap; + private HashMap map; + private LinkedList queue; + public LRUCache(int capacity) { + this.cap = capacity; + this.map = new HashMap(); + this.queue = new LinkedList(); + } + + public int get(int key) { + if (map.containsKey(key)) { + moveUsedToTop(key); + return map.get(key); + } else { + return -1; + } + } + + public void set(int key, int value) { + if (map.containsKey(key)) { + moveUsedToTop(key); + map.put(key, value); + } else { + if (queue.size() >= cap) { + map.remove(queue.poll()); + } + queue.offer(key); + map.put(key, value); + } + + } + //O(n) + public void moveUsedToTop(int key) { + for (int i = 0; i < queue.size(); i++) { + if (queue.get(i) == key) { + queue.remove(i); + queue.offer(key); + break; + } + } + } +} +```` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Letter Combinations of a Phone Number.java b/Others/old records/LeetCode-Merged/Java/Letter Combinations of a Phone Number.java new file mode 100644 index 0000000..fa31b0c --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Letter Combinations of a Phone Number.java @@ -0,0 +1,176 @@ +/* +Given a digit string, return all possible letter combinations that the number could represent. + +A mapping of digit to letters (just like on the telephone buttons) is given below. + +Input:Digit string "23" +Output: ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"]. +Note: +Although the above answer is in lexicographical order, your answer could be in any order you want. + +Hide Company Tags Uber Facebook +Hide Tags Backtracking String +Hide Similar Problems (M) Generate Parentheses (M) Combination Sum +*/ + +/* + Thoughts: + basic: use 2 queue. one queue as base. the other is result. swap the base and result when based is all poll() out. +*/ +//Use 2 queues +public class Solution { + public List letterCombinations(String digits) { + List rst = new ArrayList(); + if (digits == null || digits.length() == 0) { + return rst; + } + HashMap map = new HashMap(); + map.put(2, "abc");map.put(3, "def"); + map.put(4, "ghi");map.put(5, "jkl");map.put(6, "mno"); + map.put(7, "pqrs");map.put(8,"tuv");map.put(9,"wxyz"); + + Queue base = new LinkedList(); + Queue queue = new LinkedList(); + + //init + int index = 0; + int digit = Integer.parseInt(digits.substring(index, index + 1)); + char[] arr = map.get(digit).toCharArray(); + index++; + + for (char c : arr) { + base.offer(c+""); + } + if (index == digits.length()) { + queue = base; + } + + while (index < digits.length() && !base.isEmpty()) { + String str = base.poll(); + digit = Integer.parseInt(digits.substring(index, index + 1)); + arr = map.get(digit).toCharArray(); + for (char key : arr) { + queue.offer(str + key); + } + + if (base.isEmpty() && index < digits.length() - 1) { + Queue temp = base; + base = queue; + queue = temp; + index++; + } + }//end while + + while (!queue.isEmpty()) { + rst.add(queue.poll()); + } + + return rst; + } +} + + +//Use 1 queue +// and optimize a bit +public class Solution { + public List letterCombinations(String digits) { + List rst = new ArrayList(); + if (digits == null || digits.length() == 0) { + return rst; + } + HashMap map = new HashMap(); + map.put(2, "abc");map.put(3, "def"); + map.put(4, "ghi");map.put(5, "jkl");map.put(6, "mno"); + map.put(7, "pqrs");map.put(8,"tuv");map.put(9,"wxyz"); + + Queue queue = new LinkedList(); + + //init + int index = 0; + int digit = Integer.parseInt(digits.substring(index, index + 1)); + String keys = map.get(digit); + index++; + + for (int i = 0; i < keys.length(); i++) { + queue.offer(keys.substring(i,i+1)); + } + int size = queue.size(); + + while (index < digits.length() && !queue.isEmpty()) { + String str = queue.poll(); + digit = Integer.parseInt(digits.substring(index, index + 1)); + keys = map.get(digit); + for (int i = 0; i < keys.length(); i++) { + queue.offer(str + keys.substring(i,i+1)); + } + size--; + if (size == 0 && index < digits.length() - 1) { + index++; + size = queue.size(); + } + }//end while + + while (!queue.isEmpty()) { + rst.add(queue.poll()); + } + + return rst; + } +} + + + + + +public class Solution { + /** + * @param digits A digital string + * @return all posible letter combinations + */ + public ArrayList letterCombinations(String digits) { + ArrayList rst = new ArrayList(); + if (digits == null || digits.length() == 0) { + return rst; + } + ArrayList map = new ArrayList(); + map.add(new String[]{});//key 0: nothing + map.add(new String[]{});//key 1: nothing + map.add(new String[]{"a","b","c"}); + map.add(new String[]{"d","e","f"}); + map.add(new String[]{"g","h","i"}); + map.add(new String[]{"j","k","l"}); + map.add(new String[]{"m","n","o"}); + map.add(new String[]{"p","q","r","s"}); + map.add(new String[]{"t","u","v"}); + map.add(new String[]{"w","x","y","z"}); + + ArrayList list = new ArrayList(); + helper(rst, list, map, digits, 0); + + return rst; + } + + public void helper(ArrayList rst, ArrayList list, + ArrayList map, String digits, int level){ + //If level is finished, compress into string + if (level == digits.length()) { + StringBuffer sb = new StringBuffer(); + for (String s : list) { + sb.append(s); + } + rst.add(sb.toString()); + return; + } + //For a specific list of candidates, face the level of chars + int num = Integer.parseInt(digits.substring(level, level + 1)); + String[] strs = map.get(num); + + for (int i = 0; i < strs.length; i++) { + list.add(strs[i]); + helper(rst, list, map, digits, level + 1); + list.remove(list.size() - 1); + } + } + +} + diff --git a/Others/old records/LeetCode-Merged/Java/Longest Palindromic Substring.java b/Others/old records/LeetCode-Merged/Java/Longest Palindromic Substring.java new file mode 100644 index 0000000..031ad99 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Longest Palindromic Substring.java @@ -0,0 +1,114 @@ +这个琢磨了我好半天。 +第一个方法至少O(n^3),果然时间太多,输了给了李特。这种方法从两头check (i,j),太慢。 + +第二个方法,是Code Granker上面的,利用了高中学排列组合时候的概念。 +有个‘abc’,那么总共可以看成'a_b_c' 5个字符位置。 +分为两种情况: 一种是在'a|b'的间隙里面分割;一种是'abc'分割在'b'上。这样是O(2n-1)*O(n) = O(n^2),还不错。 + +方法三应该是DP。还没有细细研究。 + +```` +/* +Given a string S, find the longest palindromic substring in S. +You may assume that the maximum length of S is 1000, +and there exists one unique longest palindromic substring. + +Tags: String +Similar Problems: (H) Shortest Palindrome, (E) Palindrome Permutation + +*/ + +/* +Attempt2: http://blog.csdn.net/linhuanmars/article/details/20888595, Code Granker + +Assuming index x is the middle of the longest palindromic substring. Start from the middle, and check left,right indexes see if that are matching. + +How many different index x can we check? +Think of string "abcd" as "a b c d", where the x can be any of 'a,b,c,d' character, and it can also just be in between of any two characters. That makes the total number of x to check: 2*n - 1. (n chars, and n-1 in-between space) + +More details: + When x is odd: x % 2 == 1, it's this pattern: a_b_c_d, for example x/2 = i lands on the 2nd space ' ', i == 3. + So: left = x/2 = 3 = ' '; right = x/2 = 3 = ' '. Right now, we are almost going to compare 'a_b' and '_c_d'.However, right needs to +1, because the we really want to compare 'a_b' and 'c_d'. We are split by the in-between-space, good to go. + + When x is even: x % 2 = 0, it's a_b_c, for example i lands on 'b', i ==2. + So:left = x/2 = 2 = 'b'; right = x/2 = 2 = 'b'; We are splitting the string on 'b', it's okay. Then just split them like: 'a_' and '_c'. + + In conclusion: when x % 2 == 1: right = x/2 + 1; + +Also, for each 'left' and 'right', start expanding left-- and right-- as long as they match the pattern of palindromic. Whenever failed to match, return whatever length of palindromic substring. +Track the max length and max string. +*/ + +public class Solution { + public String longestPalindrome(String s) { + if (s == null || s.length() <= 1) { + return s; + } + int x = 2 * s.length() - 1; + String str = ""; + int max = 0; + for (int i = 0; i < x; i++) {//Check through the 2*len - 1 + int left = i / 2; + int right = i / 2; + if (i % 2 == 1) { + right++; + } + //Check Palindromic + while(left >= 0 && right < s.length() && s.charAt(left) == s.charAt(right)) { + left--; + right++; + } + String temp = s.substring(left + 1, right); + //Track max + if (temp.length() > max) { + max = temp.length(); + str = temp; + } + }//END for + return str; + } +} + +/* +1st Attempt.Thoughts: +Check palindromic with while(start < end){}. +Double-for loop on i ~ n. +Time: O(n^2 * n/2) = O(n^3) +*/ +public class Solution { + public String longestPalindrome(String s) { + if (s == null || s.length() <= 1) { + return s; + } + String rst = ""; + for (int i = 0; i < s.length(); i++) { + for (int j = s.length() - 1; j > i; j--) { + int start = i; + int end = j; + String str = s.substring(i,j + 1); + boolean isPalindromic = true; + while (start < end && isPalindromic) { + if (s.charAt(start) != s.charAt(end)) { + isPalindromic = false; + } else { + start++; + end--; + } + } + if (isPalindromic) { + if (str.length() > rst.length()) { + rst = str; + } + } + } + }//END for + return rst; + } +} + + + + + + +```` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Lowest Common Ancestor of a Binary Search Tree.java b/Others/old records/LeetCode-Merged/Java/Lowest Common Ancestor of a Binary Search Tree.java new file mode 100644 index 0000000..99577cf --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Lowest Common Ancestor of a Binary Search Tree.java @@ -0,0 +1,74 @@ +/* +Given a binary search tree (BST), find the lowest common ancestor (LCA) of two given nodes in the BST. + +According to the definition of LCA on Wikipedia: “The lowest common ancestor is defined between two nodes v and w as the lowest node in T that has both v and w as descendants (where we allow a node to be a descendant of itself).” + + _______6______ + / \ + ___2__ ___8__ + / \ / \ + 0 _4 7 9 + / \ + 3 5 +For example, the lowest common ancestor (LCA) of nodes 2 and 8 is 6. Another example is LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition. + +Tags: Tree +Similar Problems: (M) Lowest Common Ancestor of a Binary Tree + +*/ +/* +Thoughts: +Create 2 path: l1, l2. +First different node's parent, will be the LCA +Do binary search to generate the path l1,l2 + +Note: +When one of the target is root, make sure parent = root, and return root at the end. This is because: the if statement (l1.get(i).val != l2.get(i).val) won't capture this case; instead, the for loop ends by i == size. So, be careful here. +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || p == null || q == null) { + return null; + } + ArrayList l1 = new ArrayList(); + ArrayList l2 = new ArrayList(); + binarySearch(root, p, l1); + binarySearch(root, q, l2); + + TreeNode parent = root; + int size = l1.size() > l2.size() ? l2.size() : l1.size(); + for (int i = 0; i < size; i++) { + if (l1.get(i).val == l2.get(i).val) { + parent = l1.get(i); + } else { + return parent; + } + } + return parent; + } + + + public void binarySearch(TreeNode root, TreeNode target, ArrayList list) { + TreeNode node = root; + while (node != null) { + list.add(node); + if (node.val == target.val) { + return; + } else if (node.val < target.val) { + node = node.right; + } else { + node = node.left; + } + }//END while + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/MaximumSubarray.java b/Others/old records/LeetCode-Merged/Java/MaximumSubarray.java new file mode 100644 index 0000000..b5f0b27 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/MaximumSubarray.java @@ -0,0 +1,36 @@ +/* +Find the contiguous subarray within an array (containing at least one number) which has the largest sum. + +For example, given the array [−2,1,−3,4,−1,2,1,−5,4], +the contiguous subarray [4,−1,2,1] has the largest sum = 6. + +click to show more practice. + +More practice: +If you have figured out the O(n) solution, try coding another solution using the divide and conquer approach, which is more subtle. + +Hide Tags Divide and Conquer Array Dynamic Programming + +Thinking process: +1. DP: store calculated sum for comparison use: compare with max to get true max value. +2. Max sum from previous index to current: + at index i, try to compare if pre + current > current index value. If yes, use pre + current; if NO, just use current as max. + Compare max with Sum[i] for final max vlaualuealuee. + +*/ + +public class Solution { + public int maxSubArray(int[] A) { + if (A == null || A.length == 0) { + return 0; + } + int[] sum = new int[A.length]; + sum[0] = A[0]; + int max = sum[0]; + for (int i = 1; i < sum.length; i++) { + sum[i] = Math.max(A[i], sum[i - 1] + A[i]); + max = Math.max(max, sum[i]); + } + return max; + } +} diff --git a/Others/old records/LeetCode-Merged/Java/Median of Two Sorted Arrays.java b/Others/old records/LeetCode-Merged/Java/Median of Two Sorted Arrays.java new file mode 100644 index 0000000..a534488 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Median of Two Sorted Arrays.java @@ -0,0 +1,35 @@ +/* +There are two sorted arrays nums1 and nums2 of size m and n respectively. +Find the median of the two sorted arrays. The overall run time complexity should be O(log (m+n)). +*/ + + +/* +THoughts: +max-heap on left and min-heap on right; Center: the median +left heap size: x +right heap size: x, or x + 1 + +However, this might (m+n)Log(m+n) + +*/ + +//NOT DONE +public class Solution { + public double findMedianSortedArrays(int[] nums1, int[] nums2) { + if (nums1 == null && nums2 == null) { + return 0; + } + //min-heap + PriorityQueue minHeap = new PriorityQueue(); + PriorityQueue maxHeap = new PriorityQueue(1, new Comparator(){ + public int compare(int x, int y){ + return y - x; + } + }); + + int median = Integer.MIN_VALUE; + + + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Meeting Rooms II.java b/Others/old records/LeetCode-Merged/Java/Meeting Rooms II.java new file mode 100644 index 0000000..586fd8a --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Meeting Rooms II.java @@ -0,0 +1,116 @@ +开会王,还是可以用PriorityQueue + 一个Class来解决。 +这里有尝试了一下用一个sorted Array + HashMap: 也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + +``` +/* +Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), find the minimum number of conference rooms required. + +For example +Given [[0, 30],[5, 10],[15, 20]], +return 2. + +Tags: Heap Greedy Sort +Similar Problems: (H) Merge Intervals, (E) Meeting Rooms +*/ + +/* +Thoughts: This seems to be: how many concurrent meetings do we have? +Just return the count that we used in Meeting I. + +Though, instead of Prority queue + Point class, let me try to use just array and a hashmap. + +Using HashMap is tricky in this: +Overallpping spot will be put on the same hashmap key. Be careful with handling the overlapping. +Here, I decide to merge the edge when generate the map, so during the count check, need to skip +duplicated time spot to prevent incorrectly re-use of certain time spot. + + +*/ + +/** + * Definition for an interval. + * public class Interval { + * int start; + * int end; + * Interval() { start = 0; end = 0; } + * Interval(int s, int e) { start = s; end = e; } + * } + */ +public class Solution { + public int minMeetingRooms(Interval[] intervals) { + if (intervals == null || intervals.length == 0) { + return 0; + } + int[] timeSpot = new int[intervals.length * 2]; + HashMap map = new HashMap(); + for (int i = 0; i < intervals.length; i++) { + timeSpot[i] = intervals[i].start; + timeSpot[intervals.length + i] = intervals[i].end; + if (map.containsKey(intervals[i].start)) { + map.put(intervals[i].start, map.get(intervals[i].start) + 1); + } else { + map.put(intervals[i].start, 1); + } + if (map.containsKey(intervals[i].end)) { + map.put(intervals[i].end, map.get(intervals[i].end) - 1); + } else { + map.put(intervals[i].end, -1); + } + } + Arrays.sort(timeSpot); + int count = 0; + int max = 0; + for (int i = 0; i < timeSpot.length; i++) { + count += map.get(timeSpot[i]); + while (i + 1 < timeSpot.length && timeSpot[i] == timeSpot[i + 1]) { + i++; + } + max = Math.max(count, max); + } + return max; + } +} + + +// Similar to Meeting Room I, using Point class and Priorityqueue +// Creating a customized class, but makes the problem a bit easier to think. + +public class Solution { + class Point { + int pos; + int flag; + public Point(int pos, int flag) { + this.pos = pos; + this.flag = flag; + } + } + public int minMeetingRooms(Interval[] intervals) { + if (intervals == null || intervals.length == 0) { + return true; + } + PriorityQueue queue = new PriorityQueue( + new Comparator(){ + public int compare(Point a, Point b){ + return (a.pos - b.pos); + } + } + ); + for (Interval range : intervals) { + queue.add(new Point(range.start, 1)); + queue.add(new Point(range.end, -1)); + } + int count = 0; + int max = 0; + while (!queue.isEmpty()) { + Point p = queue.poll(); + count += p.flag; + while(!queue.isEmpty() && p.pos == queue.peek().pos) { + p = queue.poll(); + count += p.flag; + } + max = Math.max(max, count); + } + return max; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Meeting Rooms.java b/Others/old records/LeetCode-Merged/Java/Meeting Rooms.java new file mode 100644 index 0000000..5fc790f --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Meeting Rooms.java @@ -0,0 +1,68 @@ +扫描线是个好厨师。 +注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点。 +这开会的dude是个超人。瞬间移动接上下一个会议。 +``` +/* +Given an array of meeting time intervals consisting of start and end times [[s1,e1],[s2,e2],...] (si < ei), determine if a person could attend all meetings. + +For example, +Given [[0, 30],[5, 10],[15, 20]], +return false. +*/ + +/** + * Definition for an interval. + * public class Interval { + * int start; + * int end; + * Interval() { start = 0; end = 0; } + * Interval(int s, int e) { start = s; end = e; } + * } + */ + +/* +Thought: +Use scane line. +Note: special care for edge point: make sure to process all connecting point before shuouting the result. +*/ + + public class Solution { + class Point { + int pos; + int flag; + public Point(int pos, int flag) { + this.pos = pos; + this.flag = flag; + } + } + public boolean canAttendMeetings(Interval[] intervals) { + if (intervals == null || intervals.length == 0) { + return true; + } + PriorityQueue queue = new PriorityQueue( + new Comparator(){ + public int compare(Point a, Point b){ + return (a.pos - b.pos); + } + } + ); + for (Interval range : intervals) { + queue.add(new Point(range.start, 1)); + queue.add(new Point(range.end, -1)); + } + int count = 0; + while (!queue.isEmpty()) { + Point p = queue.poll(); + count += p.flag; + while(!queue.isEmpty() && p.pos == queue.peek().pos) { + p = queue.poll(); + count += p.flag; + } + if (count > 1) { + return false; + } + } + return true; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Merge Intervals.java b/Others/old records/LeetCode-Merged/Java/Merge Intervals.java new file mode 100644 index 0000000..09eb0ba --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Merge Intervals.java @@ -0,0 +1,156 @@ +扫描线+Count无敌手。注意start end把interval给合起来。 +count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 + +空间:O(2n) -> O(n) +时间,priorityqueue: O(nlogn) + +记得怎么写comparator + + + +或者O(n) +Collections.sort() on interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +Basic implementation +/* + new Comparator(){ + public int compare(obj1, obj2) { + return obj1.x - obj2.x; + } + + } +*/ + +``` +/* +Given a collection of intervals, merge all overlapping intervals. + +For example, +Given [1,3],[2,6],[8,10],[15,18], +return [1,6],[8,10],[15,18]. + +Tags: Array, Sort +Similar Problems: (H) Insert Interval, (E) Meeting Rooms (M) Meeting Rooms II + +*/ + +/* + Thoughts: 12.09.2015 + Recap. Use O(1) + + Sort by start time. + then it overlaps: check on pre.end and curr.start. + if overlaps: curr.start will be overlapped; also check on curr.end and pre.end, decide who ends this interval + + border case: null, return itself; or length==1, return. +*/ + +class Solution { + /** + * @param intervals: Sorted interval list. + * @return: A new sorted interval list. + */ + public List merge(List intervals) { + if (intervals == null || intervals.size() <= 1) { + return intervals; + } + + Collections.sort(intervals, new Comparator(){ + public int compare(Interval a, Interval b) { + return a.start - b.start; + } + }); + Interval prev = intervals.get(0); + Interval curr; + + for (int i = 1; i < intervals.size(); i++) { + curr = intervals.get(i); + if (prev.end >= curr.start) { + if (prev.end <= curr.end) { + prev.end = curr.end; + } + intervals.remove(i); + i--; + } else { + prev = curr; + } + } + + return intervals; + } + +} + +/* +Thoughts: +Again use scan line. Quite similar to Meeting Rooms, flight schedules... etc +This one: when count ==0, make sure to keep track start and end, add into the rst +When writing out example, whenever count==0, that indicates an end of a interval. + +HOWEVER, this uses O(n) space, while this problem requests O(1) space +*/ + +/** + * Definition for an interval. + * public class Interval { + * int start; + * int end; + * Interval() { start = 0; end = 0; } + * Interval(int s, int e) { start = s; end = e; } + * } + */ +public class Solution { + class Point { + int pos; + int flag; + public Point(int pos, int flag) { + this.pos = pos; + this.flag = flag; + } + } + + public List merge(List intervals) { + List rst = new ArrayList(); + if (intervals == null || intervals.size() == 0) { + return rst; + } + PriorityQueue queue = new PriorityQueue( + new Comparator(){ + public int compare(Point a, Point b){ + return (a.pos - b.pos); + } + } + ); + for (Interval range : intervals) { + queue.add(new Point(range.start, 1)); + queue.add(new Point(range.end, -1)); + } + int count = 0; + int start = 0; + int end = 0; + while (!queue.isEmpty()) { + Point p = queue.poll(); + if (count == 0) { + start = p.pos; + } + count += p.flag; + while(!queue.isEmpty() && p.pos == queue.peek().pos) { + p = queue.poll(); + count += p.flag; + } + if (count == 0) { + end = p.pos; + rst.add(new Interval(start, end)); + } + } + return rst; + } +} + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Merge Two Sorted Lists.java b/Others/old records/LeetCode-Merged/Java/Merge Two Sorted Lists.java new file mode 100644 index 0000000..e31e208 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Merge Two Sorted Lists.java @@ -0,0 +1,50 @@ +/* +Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. + +Tags: Linked List +Similar Problems: (H) Merge k Sorted Lists, (E) Merge Sorted Array, (M) Sort List, (M) Shortest Word Distance II + +*/ + +/* +Thouhts: +Loop throgh both list. Make sure to check the border cases +*/ + + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +public class Solution { + public ListNode mergeTwoLists(ListNode l1, ListNode l2) { + if (l1 == null && l2 == null) { + return null; + } + ListNode node = new ListNode(0); + ListNode dummy = node; + while (l1 != null || l2 != null) { + if (l1 == null) { + node.next = l2; + break; + } else if (l2 == null) { + node.next = l1; + break; + } else { + if (l1.val < l2.val) { + node.next = l1; + l1 = l1.next; + } else { + node.next = l2; + l2 = l2.next; + } + node = node.next; + } + }//end while + return dummy.next; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Merge k Sorted Lists.java b/Others/old records/LeetCode-Merged/Java/Merge k Sorted Lists.java new file mode 100644 index 0000000..29e11cd --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Merge k Sorted Lists.java @@ -0,0 +1,72 @@ +事情是这样的。今天李特除了一题,据说是Hard。 +我一看好像曾经见过,于是做了做,时间复杂度最后大约是O(kn)*O(logn). 因为用到了PriorityQueue, 里面的offer() 和 poll()都是O(logn)的时间。 + +总而言之就是: +先把每个ListNode放进queue。 +然后逐个击破。每次击破,都要把小孩扔进queue。 + +这里有复习了一下 +```` +new Comparator<...>{}的用法 + +PriorityQueue queue = new PriorityQueue(new Comparator(){ + public compare(xx A, xx B) { + return A.value - B.value;//只要A queue = new PriorityQueue(new Comparator(){ + public int compare(ListNode a, ListNode b){ + return a.val - b.val; + } + }); + //Populate queue with initial node with correct ordering + for (int i = 0; i < lists.length; i++) { + if(lists[i] != null) { + queue.offer(lists[i]); + } + } + + //add to rst + while (!queue.isEmpty()) { + ListNode temp = queue.poll(); + node.next = temp; + if (temp.next != null) { + queue.offer(temp.next); + } + node = node.next; + } + return dummy.next; + } +} +```` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Min Stack.java b/Others/old records/LeetCode-Merged/Java/Min Stack.java new file mode 100644 index 0000000..63dfa9d --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Min Stack.java @@ -0,0 +1,52 @@ +双Stack. 小心stack.peek() 而不是 pop +``` +/* +Design a stack that supports push, pop, top, and retrieving the minimum element in constant time. + +push(x) -- Push element x onto stack. +pop() -- Removes the element on top of the stack. +top() -- Get the top element. +getMin() -- Retrieve the minimum element in the stack. + +Tags: Stack, Design +Similar Problems: (H) Sliding Window Maximum + + +Thoughts: +Use a regular Stack: linked list. +Save that minimum integer in a HashMap with each stack value. At each level of the stack, it always stores the min till that moment. +Use another stack to hold that 'up-to-date' min values. + +Note: +Stack: peek() + +*/ + +class MinStack { + Stack stack = new Stack(); + Stack min = new Stack(); + public void push(int x) { + stack.push(x); + if (min.isEmpty() || x < min.peek()){ + min.push(x); + } else { + min.push(min.peek()); + } + } + + public void pop() { + stack.pop(); + min.pop(); + } + + public int top() { + return stack.peek(); + } + + public int getMin() { + return min.peek(); + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Minimum Height Trees.java b/Others/old records/LeetCode-Merged/Java/Minimum Height Trees.java new file mode 100644 index 0000000..c1f3e10 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Minimum Height Trees.java @@ -0,0 +1,121 @@ +/* +For a undirected graph with tree characteristics, we can choose any node as the root. The result graph is then a rooted tree. Among all possible rooted trees, those with minimum height are called minimum height trees (MHTs). Given such a graph, write a function to find all the MHTs and return a list of their root labels. + +Format +The graph contains n nodes which are labeled from 0 to n - 1. You will be given the number n and a list of undirected edges (each edge is a pair of labels). + +You can assume that no duplicate edges will appear in edges. Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges. + +Example 1: + +Given n = 4, edges = [[1, 0], [1, 2], [1, 3]] + + 0 + | + 1 + / \ + 2 3 +return [1] + +Example 2: + +Given n = 6, edges = [[0, 3], [1, 3], [2, 3], [4, 3], [5, 4]] + + 0 1 2 + \ | / + 3 + | + 4 + | + 5 +return [3, 4] + +Show Hint +Note: + +(1) According to the definition of tree on Wikipedia: “a tree is an undirected graph in which any two vertices are connected by exactly one path. In other words, any connected graph without simple cycles is a tree.” + +(2) The height of a rooted tree is the number of edges on the longest downward path between the root and a leaf. + +Credits: +Special thanks to @peisi for adding this problem and creating all test cases. + +Hide Company Tags Google +Hide Tags Breadth-first Search Graph +Hide Similar Problems (M) Course Schedule (M) Course Schedule II + +*/ + + +/* + Starting from leaf with depth == 1, + remove all leaf, and the edge + Till the end, whatever node left, should be the root. + + * WHen there is only 1,2 nodes remaining. that's the rst. + + Put Node in HahsMap + + Iterative over map till map.size() <= 2 + + border n == 2,1, just returl rst. + edges == null, return null. + edges.length == 1, reutrn list +*/ + +public class Solution { + public List findMinHeightTrees(int n, int[][] edges) { + List rst = new ArrayList(); + if (n == 1) { + rst.add(0); + return rst; + }else if (n == 0 || edges == null || edges.length == 0 || edges.length != n - 1) { + return rst; + } + + //populate map + boolean[] nodes = new boolean[n]; + HashMap> map = new HashMap>(); + for (int i = 0; i < n; i++) { + map.put(i, new ArrayList()); + nodes[i] = true; + } + for (int i = 0; i < edges.length; i++) { + if (!map.get(edges[i][0]).contains(edges[i][1])) { + map.get(edges[i][0]).add(edges[i][1]); + } + if (!map.get(edges[i][1]).contains(edges[i][0])) { + map.get(edges[i][1]).add(edges[i][0]); + } + } + + //Remove list with leng == 1 + Queue queue = new LinkedList(); + while (n > 2) { + for (Map.Entry> entry : map.entrySet()) { + if (entry.getValue().size() == 1) { + queue.offer(entry.getKey()); + } + } + while (!queue.isEmpty()) { + n--; + Integer key = queue.poll(); + nodes[key] = false; + int from = map.get(key).get(0); + map.get(from).remove(key); + map.get(key).remove(0); + + } + } + + //Put remaining into rst + for (int i = 0; i < nodes.length; i++) { + if (nodes[i]) { + rst.add(i); + } + + } + + return rst; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Missing Ranges.java b/Others/old records/LeetCode-Merged/Java/Missing Ranges.java new file mode 100644 index 0000000..c0620b6 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Missing Ranges.java @@ -0,0 +1,69 @@ +精力旺盛症。 +自己做的时候,想的太复杂,做起了binarysearch,企图节省时间。 +下次要算清楚,是否有意义。 +binarySearch的确logn,但是在lower 和upper之间的数字,很可能还是O(n). +因此一开始就for一遍也是O(n), 而code会相对来说简单许多。 + +想法: +两个pointer, 每次计较prev和curr之间的部分。 +然后prev = curr,向前移动一格。 + +``` +/* +Given a sorted integer array where the range of elements are [lower, upper] inclusive, return its missing ranges. + +For example, given [0, 1, 3, 50, 75], lower = 0 and upper = 99, return ["2", "4->49", "51->74", "76->99"]. + +Tags: Array +Similar Problems: (E) Summary Ranges + + +*/ +/* + +Attempt2, Thoughts: +Use two pointer to mark the prev and curr value, then verify the range in between. + +matching conditoin: prev +2 >= curr. +That is, +1,...,3 + +1. When print range: print the missing [x,y] +2. missing x = prev+1, missing y = curr - 1; +3. Make sure prev represents the consecutive integer before missing x. +*/ + +public class Solution { + public List findMissingRanges(int[] nums, int lower, int upper) { + List rst = new ArrayList(); + if (nums == null || nums.length == 0) {//Though, also covered in the for + rst.add(printRange(lower, upper)); + return rst; + } else if (lower > upper) { + return rst; + } + int prev = lower - 1; + int curr; + for (int i = 0; i <= nums.length; i++) { + curr = (i == nums.length) ? upper + 1 : nums[i]; + if (prev + 2 <= curr) { + rst.add(printRange(prev + 1, curr - 1)); + } + prev = curr; + } + return rst; + } + + public String printRange(int from, int to) { + return (from == to) ? String.valueOf(from) : from + "->" + to; + } +} + + +/* +Old solution: attempted to do binary search for lower and upper, then calculate the mid range. O(logn) + O(upper - lower) = O(n) + +Therefore, don't have to do that; just do a run through. + +*/ +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Multiply Strings.java b/Others/old records/LeetCode-Merged/Java/Multiply Strings.java new file mode 100644 index 0000000..2237ef4 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Multiply Strings.java @@ -0,0 +1,98 @@ +想法不难。turn into int[], 然后每个位子乘积,然后余数carrier移位。 + +但是做起来有很多坑。适合面试黑。 + +1. 数字‘123’, 在数组里面, index == 0 是 ‘1’。 但是我们平时习惯从最小位数开始乘积,就是末尾的'3'开始。 + 所以!翻转两个数字先!我去。这个是个大坑。 + +2. 乘积product,和移动Carrier都很普通。 + +3. !!最后不能忘了再翻转。 + +4. 最后一个看坑。要是乘积是0,就返回‘0’。 但是这个其实可以在开头catch到没必要做到结尾catch。 + +用到几个StringBuffer的好东西: +reverse(); +sb.deleteCharAt(i) + +//找数字,或者26个字母,都可以: +s.charAt(i) - '0'; //数字 +s.charAt(i) - 'a'; //字母 + +``` +/* +Given two numbers represented as strings, return multiplication of the numbers as a string. + +Note: The numbers can be arbitrarily large and are non-negative. + +Hide Company Tags Facebook +Hide Tags Math String +Hide Similar Problems (M) Add Two Numbers (E) Plus One (E) Add Binary + +*/ +/* + Thoughts: + 1. too long to multiply int. so convert to int[] + 2. Multiply by definition: + a. create a product[] of num1.size() + num2.size() - 1 + b. catches each product[i + j] + 3. for loop on product array again, to carry over the carries + + if both null, return null. + if both "", return "" + + O(m + n) +*/ +public class Solution { + public String multiply(String num1, String num2) { + if (num1 == null || num2 == null) { + return ""; + } else if (num1.length() == 0 || num2.length() == 0) { + return num1.length() == 0 ? num2 : num1; + } else if (num1.equals("0") || num2.equals("0")) { + return "0"; + } + //reverse string, so to calculate from 0 base. easier to calculate + num1 = new StringBuffer(num1).reverse().toString(); + num2 = new StringBuffer(num2).reverse().toString(); + + //product array. extra leading space for carriers + //normally just need num1.length() + num2.length() -1 + int[] product = new int[num1.length() + num2.length()]; + + //Calculate the product normally + for (int i = 0; i < num1.length(); i++) { + int a = num1.charAt(i) - '0'; + for (int j = 0; j < num2.length(); j++) { + int b = num2.charAt(j) - '0'; + product[i + j] += a * b; + } + } + + //calcualte and output + //remember, now the string is reversed calculated. + //so every time, add to index 0. so it will all reverse back; OR, append, and reverse later. + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < product.length; i++) { + int number = product[i] % 10; + int carrier = product[i] / 10; + sb.append(number); + if (i < product.length - 1) { + product[i + 1] += carrier; + } + } + sb.reverse(); + //trim leading 0's + while (sb.length() > 0 && sb.charAt(0) == '0') { + sb.deleteCharAt(0); + } + + return sb.toString(); + } +} + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Number of Islands.java b/Others/old records/LeetCode-Merged/Java/Number of Islands.java new file mode 100644 index 0000000..cecaee5 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Number of Islands.java @@ -0,0 +1,136 @@ +/* +Given a 2d grid map of '1's (land) and '0's (water), count the number of islands. An island is surrounded by water and is formed by connecting adjacent lands horizontally or vertically. You may assume all four edges of the grid are all surrounded by water. + +Example 1: + +11110 +11010 +11000 +00000 +Answer: 1 + +Example 2: + +11000 +11000 +00100 +00011 +Answer: 3 + + +Tags: Depth-first Search, Breadth-first Search, Union Find +Similar Problems: (M) Surrounded Regions, (M) Walls and Gates + + +*/ + +/* +Attempt2: Use mark to mark all adjacent island. +1. set initial mark = 1 +2. double for loop on each (i,j). +3. mark method: + if a new island found, + a. mark it with mark + b. try to mark all adjacent spot + c. return true + if no new island found, return false; +4. based on returnning value of mark at the double-for loop, increase mark or not. + +return mark - 1, because we set a initial mark == 1, which eases the marking process. but it always has an extra 1. +*/ + +public class Solution { + public int[][] matrix; + public int mark = 1; + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + matrix = new int[grid.length][grid[0].length]; + + for(int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + mark = mark(i, j, grid)? (mark + 1) : mark; + } + } + return mark - 1; + } + + public boolean mark(int i, int j, char[][] grid) { + if (i >= 0 && i < grid.length && j >= 0 && j < grid[0].length) { + if (matrix[i][j] == 0 && grid[i][j] == '1') { + matrix[i][j] = mark; + mark(i - 1, j, grid); + mark(i + 1, j, grid); + mark(i, j - 1, grid); + mark(i, j + 1, grid); + return true; + } + } + return false; + } +} + + +/* +1st attempt, Not working. +Thoughts: +Ues DP to store DP[i][j]: till row i, and element j on row i, what is the number of island we had. + +public class Solution { + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int[][] DP = new int[grid.length][grid[0].length]; + DP[0][0] = grid[0][0] == '1' ? 1 : 0; + int max = DP[0][0]; + int r = grid.length; + int c = grid[0].length; + + for (int i = 0; i < r; i++) { + for (int j = 0; j < c; j++) { + if (i == 0 && j == 0) { + continue; + } + if(grid[i][j] == '0') { + DP[i][j] = max; + } else {// grid[i][j] == '1' + if (i == 0 && r == 1) { + if (j - 1 >= 0 && DP[i][j - 1] != 0 && grid[i][j - 1] != '0') { + DP[i][j] = max; + } else { + DP[i][j] = max + 1; + max = max + 1; + } + continue; + } + if (j == 0) { + if ((j + 1 < c && grid[i][j + 1] != '0') || (i - 1 >= 0 && grid[i - 1][j] != '0')) { + DP[i][j] = max; + } else { + DP[i][j] = max + 1; + max = max + 1; + } + continue; + } + //DP[i][j] != 0 && Check UP,DOWN, RIGHT, LEFT == 1? + if ((i - 1 >= 0 && DP[i - 1][j] != 0 && grid[i - 1][j] != '0') || + (i + 1 < r && DP[i + 1][j] != 0 && grid[i + 1][j] != '0') || + (j - 1 >= 0 && DP[i][j - 1] != 0 && grid[i][j - 1] != '0') || + (j + 1 < c && DP[i][j + 1] != 0 && grid[i][j + 1] != '0') ) { + DP[i][j] = max; + }else {//NO adjacent is 1 + DP[i][j] = max + 1; + max = max + 1; + } + } + + } + } + + return max; + } +} + +*/ diff --git a/Others/old records/LeetCode-Merged/Java/One Edit Distance.java b/Others/old records/LeetCode-Merged/Java/One Edit Distance.java new file mode 100644 index 0000000..be5351a --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/One Edit Distance.java @@ -0,0 +1,55 @@ +理解啥叫Edit。 就是删除,增加,和替换。 +换完之后,理论上换成的String 就应该全等。 +所以只要if statement进去以后,就结束了,总要出一个叫结局。 +``` +/* +Given two strings S and T, determine if they are both one edit distance apart. + +Hide Company Tags Uber Facebook +Hide Tags String +Hide Similar Problems (H) Edit Distance + +*/ + +/* + Thoughts: + One edit distance: delete, insert, and substitude. + For loop. Whenever differenct, chech 3 cases and return + + Note: null cannot become "" or "a", so whenever s or t is null, return false; +*/ + +public class Solution { + public boolean isOneEditDistance(String s, String t) { + if (s == null || t == null) { + return false; + } else if (Math.abs(s.length() - t.length()) > 1 || s.equals(t)) { + return false; + } + int leng = s.length() > t.length() ? t.length() : s.length(); + for (int i = 0; i < leng; i++) { + String ss = s; + String tt = t; + if (s.charAt(i) != t.charAt(i)) { + //Check delete + tt = t.substring(i + 1); + ss = s.substring(i); + if (tt.equals(ss)) { + return true; + } + //Check insert + tt = t.substring(i); + ss = s.substring(i + 1); + if (tt.equals(ss)) { + return true; + } + //check substitude + ss = s.substring(0, i) + s.substring(i + 1); + tt = t.substring(0, i) + t.substring(i + 1); + return ss.equals(tt); + } + }//end for + return true; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Paint Fence.java b/Others/old records/LeetCode-Merged/Java/Paint Fence.java new file mode 100644 index 0000000..5b707c2 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Paint Fence.java @@ -0,0 +1,71 @@ +这题目很有意思. 一开始分析的太复杂, 最后按照这个哥们的想法(http://yuanhsh.iteye.com/blog/2219891) 的来做,反而简单了许多。 +设定T(n)的做法,最后题目化简以后就跟Fibonacci number一样一样的。详细分析如下。 +做完,还是觉得如有神。本来是个Easy题,想不到,就是搞不出。 + +12.13.2015再看了一下: +因为最多2个fence 颜色相同。 +假设i是和 i-1不同,那么结果就是 (k-1)*dp[i - 1] +假设i是何 i-1相同,那么根据条件,i-1和i-2肯定不同。那么所有的结果就是(k-1)*dp[i-2] +加在一起就有了。 +``` +/* +There is a fence with n posts, each post can be painted with one of the k colors. + +You have to paint all the posts such that no more than two adjacent fence posts have the same color. + +Return the total number of ways you can paint the fence. + +Note: +n and k are non-negative integers. + +Tags: Dynamic Programming +Similar Problems: (E) House Robber, (M) House Robber II, (M) Paint House, (H) Paint House II + +*/ + +/* +Thoughts: +Inspiration(http://yuanhsh.iteye.com/blog/2219891) +Consider posts from 1 ~ n. Now we look at last post, marked n: +S(n) means: last 2 fence posts have same color. + Note: S(n) will equal to whatever that's on n-1 position. + Also, just because n and n-1 are same, that means n-2 and n-1 have to be differnet. +SO: +S(n) = D(n - 1) +D(n) means: last 2 fence posts have different color. + Note: for n - 1, and n-2 positions, we have 2 different conditions: + For example: xxy, or wxy, same 2 x's or different w vs. x. +So: +D(n) = (k - 1) * (D(n - 1) + S(n - 1)) + +We can also create T(n) = S(n) + D(n); //T(n) is our totoal results. Will need to return T(n); +Use above equations to figure out T(n) +T(n) = S(n) + D(n) = D(n - 1) + (k - 1) * (D(n - 1) + S(n - 1)) + = D(n - 1) + (k - 1)(T(n - 1)) + = (k - 1) * (D(n - 2) + S(n - 2)) + (k - 1)(T(n - 1)) + = (k - 1)(T(n - 1) + T(n - 2)) + Since n-2 >=1, so n>=3. We need fiture out cases for n = 0,1,2,3 + +Note: +n == 1: just k ways +n == 0: just 0. +k == 0: just 0; +Besides these cases, we are okay. Btw, k does not really matter as long as it's >=1, it can be plugged in. +*/ + +public class Solution { + public int numWays(int n, int k) { + if (n <= 1 || k <= 0) { + return n * k; + } + int[] dp = new int[n + 1]; //index based : 1 + dp[0] = 0; + dp[1] = k; + dp[2] = k + k*(k - 1); + for (int i = 3; i <= n; i++) { + dp[i] = (k - 1) * (dp[i - 1] + dp[i - 2]); + } + return dp[n]; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Palindrome Permutation.java b/Others/old records/LeetCode-Merged/Java/Palindrome Permutation.java new file mode 100644 index 0000000..04fc349 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Palindrome Permutation.java @@ -0,0 +1,91 @@ +注意,条件里面没说是否全是lower case letter +``` +/* +Given a string, determine if a permutation of the string could form a palindrome. + +For example, +"code" -> False, "aab" -> True, "carerac" -> True. + +Hint: + +Consider the palindromes of odd vs even length. What difference do you notice? +Count the frequency of each character. +If each character occurs even number of times, then it must be a palindrome. How about character which occurs odd number of times? + +Tags: Hash Table +Similar Problems: (M) Longest Palindromic Substring, (E) Valid Anagram, (M) Palindrome Permutation II + +*/ + + +/* +Add each char into map. +Count if odd > 1, false + +Note: Iterate HashMap +HashMap.Entry entry : map.entrySet() +*/ + +public class Solution { + public boolean canPermutePalindrome(String s) { + if (s == null || s.length() == 0) { + return true; + } + HashMap map = new HashMap(); + for (int i = 0; i < s.length(); i++) { + String str = s.charAt(i) + ""; + if (!map.containsKey(str)) { + map.put(str, 1); + } else { + map.put(str, map.get(str) + 1); + } + }//ENd for + int countOdd = 0; + for (HashMap.Entry entry : map.entrySet()) { + if (entry.getValue() % 2 == 1) { + countOdd++; + } + if (countOdd > 1) { + return false; + } + }//END for + return true; + } +} + + + + +/* +12.12.2015 recap: +use a array of length == 26 to track it? No, because ther ecould be captalized letters, other ASCII code +If with assmption of 26 chars + +AND NO, cannot make that assuption. +*/ +public class Solution { + public boolean canPermutePalindrome(String s) { + if (s == null || s.length() <= 1) { + return true; + } + int[] counts = new int[26]; + for (char c : s.toCharArray()) { + counts[c - 'a'] += 1; + } + int countOne = 0; + for (int count : counts) { + if (count == 1 && countOne >= 1) { + return false; + } else if (count == 1) { + countOne++; + } else if (count % 2 != 0) { + return false; + } + } + + return true; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Peeking Iterator.java b/Others/old records/LeetCode-Merged/Java/Peeking Iterator.java new file mode 100644 index 0000000..8c92076 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Peeking Iterator.java @@ -0,0 +1,141 @@ +再一次理解错题意. peek() 就是头顶,但是不一定是最大值啊。总是把PEEK想成了最大值,然后用2 STACK做了最大值的cache,练的一手好双stack,可惜错了。 + +回到原题,其实不难。找一个cache来存next()的值,然后每次next()里面维护这个cache就好。 + +``` +/* +Given an Iterator class interface with methods: next() and hasNext(), design and implement a PeekingIterator that support the peek() operation -- it essentially peek() at the element that will be returned by the next call to next(). + +Here is an example. Assume that the iterator is initialized to the beginning of the list: [1, 2, 3]. + +Call next() gets you 1, the first element in the list. + +Now you call peek() and it returns 2, the next element. Calling next() after that still return 2. + +You call next() the final time and it returns 3, the last element. Calling hasNext() after that should return false. + +Hint: + +Think of "looking ahead". You want to cache the next element. +Is one variable sufficient? Why or why not? +Test your design with call order of peek() before next() vs next() before peek(). +For a clean implementation, check out Google's guava library source code. (https://github.com/google/guava/blob/703ef758b8621cfbab16814f01ddcc5324bdea33/guava-gwt/src-super/com/google/common/collect/super/com/google/common/collect/Iterators.java#L1125) +Follow up: How would you extend your design to be generic and work with all types, not just integer? + +It looks like the guava library uses 'E' for generic element + +Tags: Design +Similar Problems: (M) Binary Search Tree Iterator, (M) Flatten 2D Vector, (M) Zigzag Iterator + +*/ + +/* +Second attempt. +Thoughts: Of coruse can't store in a queue, that will be brutle and meaning less. +Instead, use a iterator variable, cache, to hold next(). +When called next(), move forward; otherwise, return the cache. +Make sure also return the cached peek, and update cache with next() value. +*/ +// Java Iterator interface reference: +// https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html +class PeekingIterator implements Iterator { + private int cache; + private Iterator itt; + private boolean notEnd; + public PeekingIterator(Iterator iterator) { + // initialize any member here. + itt = iterator; + cache = itt.next(); + notEnd = iterator.hasNext(); + } + + // Returns the next element in the iteration without advancing the iterator. + public Integer peek() { + return cache; + } + + // hasNext() and next() should behave the same as in the Iterator interface. + // Override them if needed. + @Override + public Integer next() { + int curr = cache; + notEnd = itt.hasNext(); + if (itt.hasNext()) { + cache = itt.next(); + } + return curr; + } + + @Override + public boolean hasNext() { + return notEnd; + } +} + + + + +/* +Attempt1, failed. Reason: I thought we are looking for the real max-peek element! However, this problem only asks for peek() element, which is not necessarily the maximun element. This mistake is bloody. +Thoughts: +To find peek, have to run through the iterator at least once. O(n). +Store everything in 2 stacks: +We want to process the end of the iterator first, put everything into stack. +Therefore the top of the stack is the next() element of iterator. +Also, use second stack to hold max value for each element stage. + +Each stack1 element has a max coresponding element in stack2. For example, [5,9,1,3,6] +s1: 6,3,1,9,5 [5 gets out first] +s2: 6,6,6,9,9 [end 9 gets out first] +*/ + +// Java Iterator interface reference: +// https://docs.oracle.com/javase/8/docs/api/java/util/Iterator.html +class PeekingIterator implements Iterator { + private Stack s1; + private Stack s2; + private int size; + public PeekingIterator(Iterator iterator) { + // initialize any member here. + s1 = new Stack(); + s2 = new Stack(); + Stack temp = new Stack(); + size = 0; + int max = Integer.MIN_VALUE; + while(iterator.hasNext()) { + temp.push(iterator.next()); + size++; + } + while(!temp.empty()) { + s1.push(temp.peek()); + max = Math.max(max, temp.peek()); + s2.push(max); + temp.pop(); + } + } + + // Returns the next element in the iteration without advancing the iterator. + public Integer peek() { + if (s1.size() > size) { + s1.pop(); + return s2.pop(); + } else { + return s2.peek(); + } + } + + // hasNext() and next() should behave the same as in the Iterator interface. + // Override them if needed. + + @Override + public Integer next() { + size--; + return s1.pop(); + } + + @Override + public boolean hasNext() { + return !s1.empty(); + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Perfect Squares.java b/Others/old records/LeetCode-Merged/Java/Perfect Squares.java new file mode 100644 index 0000000..54a9d9a --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Perfect Squares.java @@ -0,0 +1,86 @@ +一开始没clue.看了一下提示。 + +1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 + 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 + 看我把12拆分的那个example. 那很形象的就是BFS了。 + 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 +``` +/* +Given a positive integer n, find the least number of perfect square numbers (for example, 1, 4, 9, 16, ...) which sum to n. + +For example, given n = 12, return 3 because 12 = 4 + 4 + 4; given n = 13, return 2 because 13 = 4 + 9. + +Credits: +Special thanks to @jianchao.li.fighter for adding this problem and creating all test cases. + +Hide Company Tags Google +Hide Tags Dynamic Programming Breadth-first Search Math +Hide Similar Problems (E) Count Primes (M) Ugly Number II + +*/ +/* +Thoughts: + Math: + num =13. sqrt(13) = 3.xxx. Floor() = 3. count++;//1 + num = 13 - 9 = 4. sqrt(4) = 2. No remaining. count++;//2 + DP: + state + dp[i]: min # of perfect square till i. + dp[0] = 0; + dp[1] = 1; + dp[2] = 1 + 1 = 2; + dp[3] = 1,1,1;//3 + dp[4] = 2^2;//1 + dp[5] = dp[5 - floor(sqrt(5))^2] + 1; + fn: //Pick the largest perfect square possible, then added on what's remaining's dp. Do a BFS on all possiblilities + maxFlorNum = Math.floor(Math.sqrt(i)) + 12 + -3^2 = 3 -2^2 = 8 -1^2 = 11 + 1 + dp[3] 1 + dp[8] 1 + dp[11] + for (j = 0 ~ i) + dp[i] = min(min, dp[i - j ^ 2] + 1) + init: + dp[0] = 0; + dp[1] = 1; + return dp[n]; +*/ + + + +public class Solution { + public int numSquares(int n) { + if (n <= 0) { + return 0; + } + int[] dp = new int[n + 1]; + dp[0] = 0; + + for (int i = 1; i <= n; i++) { + int maxSqrNum = (int)Math.floor(Math.sqrt(i)); + int min = Integer.MAX_VALUE; + for (int j = 1; j <= maxSqrNum; j++) { + min = Math.min(min, dp[i - j * j] + 1); + } + dp[i] = min; + } + return dp[n]; + } +} + + +/* +//Test Cases + dp[2] =2; + dp[4] = 1 + dp[5] = 2; + dp[6] = 2 + 1 =3; + dp[7] = 3 + 1 = 4; + dp[8] = dp[4] + 1 = 1 = 1 = 2; + dp[9] = 1 + dp[10] = 1 + 1 = 2; + dp[11] = 2 + 1 = 3 + dp[12] = dp[12 - 9] + 3 +*/ +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Plus One.java b/Others/old records/LeetCode-Merged/Java/Plus One.java new file mode 100644 index 0000000..3a22830 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Plus One.java @@ -0,0 +1,84 @@ +/* +Given a non-negative number represented as an array of digits, plus one to the number. + +The digits are stored such that the most significant digit is at the head of the list. + +Hide Company Tags Google +Hide Tags Array Math +Hide Similar Problems (M) Multiply Strings (E) Add Binary + +*/ + + +public class Solution { + public int[] plusOne(int[] digits) { + if(digits.length==0) return digits; + + digits[digits.length-1] += 1; + //Check index digit.length-1 to 1 + for(int i = digits.length-1; i>0; i--){ + if(digits[i] == 10){ + digits[i]=0; + digits[i-1]+=1; + } + else return digits; + } + + //Check index 0. If ==0, set it to 0 and carry over 1 + if(digits[0]==10){ + int[] output = new int[digits.length+1]; + output[0] = 1; + output[1] = 0; + for(int i=2; i= 0; i--) { + digits[i] += 1; + if (digits[i] != 10) { + return digits; + } + digits[i] = 0; + } + //Create a array with length + 1 + int[] rst = new int[n + 1]; + rst[0] = 1; + rst[1] = 0; + for (int i = 2; i < rst.length; i++) { + rst[i] = digits[i - 1]; + } + return rst; + } +} +/* Trivial solution + create a secondary method func(int index, int[]digits). + add check index from digits.length-1 to 0: digits[index]+1==10? 0 : digits[index]+1; + if add up to 10, push into another level; if not ,return digits. + if index==0, check if add up to 10. If ==10, create a new array and put 1 infront. else return digits. + +*/ diff --git a/Others/old records/LeetCode-Merged/Java/Restore IP Addresses.java b/Others/old records/LeetCode-Merged/Java/Restore IP Addresses.java new file mode 100644 index 0000000..1d4b809 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Restore IP Addresses.java @@ -0,0 +1,89 @@ +递归的终点:list.zie() == 4 +递归在一个index上面(具体问题,具体分析的情况) +validate string要注意leading '0' + +注意: 递归的时候可以用一个start/level/index来跑路 +但是尽量不要去改变Input source, 会变得非常confusing. + +``` +/* + +Given a string containing only digits, restore it by returning all possible valid IP address combinations. + +For example: +Given "25525511135", + +return ["255.255.11.135", "255.255.111.35"]. (Order does not matter) + +Hide Tags Backtracking String + +*/ + +/* + Thoughts: + NOT DONE. NEED CLEAR MIND + Break into 4 parts. + At each index, either close it as one IP spot, or not. + recursive down. + If level == 4 validate if valid IP address. If so, add it. + pass along: rst, list (store the 4 IP spots), level (0 ~ 3), s, + for (0 ~ 2): can pick 1 digit, 2 digits, or 3 digits +*/ +public class Solution { + public List restoreIpAddresses(String s) { + List rst = new ArrayList(); + if (s == null || s.length() == 0) { + return rst; + } + if (s.length() < 4 || s.length() > 12) { + return rst; + } + ArrayList list = new ArrayList(); + helper(rst, list, 0, s); + + return rst; + } + + public void helper(List rst, ArrayListlist, + int start, String s) { + if (list.size() == 4) { + if (start != s.length()) { + return; + } + StringBuffer sb = new StringBuffer(); + for (String str : list) { + sb.append(str + "."); + } + rst.add(sb.substring(0, sb.length() - 1).toString()); + return; + } + //run for loop 3 times: one IP spot has at most 3 digits + for (int i = start; i < s.length() && i <= start + 3; i++) { + String temp = s.substring(start, i + 1); + if (isValid(temp)) { + list.add(temp); + helper(rst, list, i + 1, s); + list.remove(list.size() - 1); + } + } + } + //Valid the IP [0,255]; cannot start with 0 if it's not 0 + public boolean isValid(String str) { + if (str.charAt(0) == '0') { + return str.equals("0"); + } + int num = Integer.parseInt(str); + return num <= 255 && num >= 0; + } + +} + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Reverse Linked List.java b/Others/old records/LeetCode-Merged/Java/Reverse Linked List.java new file mode 100644 index 0000000..e6af97a --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Reverse Linked List.java @@ -0,0 +1,39 @@ +/* +Reverse a singly linked list. + +Hint: +A linked list can be reversed either iteratively or recursively. Could you implement both? + +Tags: Linked List +Similar Problems: (M) Reverse Linked List II, (M) Binary Tree Upside Down, (E) Palindrome Linked List + +*/ +/* +Thoughts: +Cut off everything from [2 ~ ] and save it in cutoff; +Append old reversed list to current head. Make itself as the new reversedList. Basically: append the 1st element to head of the reversedList, like a stack. +Save head = cutOff: basically moves on to next element. +*/ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +public class Solution { + public ListNode reverseList(ListNode head) { + if (head == null) { + return head; + } + ListNode reversedList = null; + while (head != null) { + ListNode cutOff = head.next; + head.next = reversedList; + reversedList = head; + head = cutOff; + } + return reversedList; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Reverse Words in a String II.java b/Others/old records/LeetCode-Merged/Java/Reverse Words in a String II.java new file mode 100644 index 0000000..ba54a7c --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Reverse Words in a String II.java @@ -0,0 +1,65 @@ +/* +Given an input string, reverse the string word by word. A word is defined as a sequence of non-space characters. + +The input string does not contain leading or trailing spaces and the words are always separated by a single space. + +For example, +Given s = "the sky is blue", +return "blue is sky the". + +Could you do it in-place without allocating extra space? + +Related problem: Rotate Array + +Tags: String +Similar Problems: (M) Reverse Words in a String, (E) Rotate Array + + +*/ + +/* +Thoughts: write an example: reverse the whole thing, then reverse each individual word, split by space. + +Note: becase we don't have space at end of the char[], so we will ignore last word. Remember to reverse that one. +*/ +public class Solution { + public void reverseWords(char[] s) { + if (s == null || s.length == 0) { + return; + } + int len = s.length; + //reverse whole + for (int i = 0; i < len / 2; i++) { + char temp = s[i]; + s[i] = s[len - 1 - i]; + s[len - 1 - i] = temp; + } + + //reverse partial + int start = 0; + int mid = 0; + int end = 0; + for (int i = 0; i < len; i++) { + if (s[i] == ' ') { + mid = start + (end - start) / 2; + for (int j = start; j <= mid; j++) { + char temp = s[j]; + s[j] = s[end - (j - start)]; + s[end - (j - start)] = temp; + } + start = i + 1; + } else { + end = i; + } + } + + //Process last word + mid = start + (end - start) / 2; + for (int j = start; j <= mid; j++) { + char temp = s[j]; + s[j] = s[end - (j - start)]; + s[end - (j - start)] = temp; + } + + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Rotate Image.java b/Others/old records/LeetCode-Merged/Java/Rotate Image.java new file mode 100644 index 0000000..5ad6129 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Rotate Image.java @@ -0,0 +1,85 @@ +M + +``` +/* +You are given an n x n 2D matrix representing an image. + +Rotate the image by 90 degrees (clockwise). + +Follow up: +Could you do this in-place? + +Tags: +Array +*/ +/* +Thoughts: +Method1: 1. flip the upper half UP/DOWN. 2. Flip the diagal where j >= i + 1 +In place +*/ + +public class Solution { + public void rotate(int[][] matrix) { + if (matrix == null || matrix[0].length == 0) { + return; + } + int n = matrix.length; + //Flip UP/DOWN + for (int i = 0; i < n / 2; i++){ + for (int j = 0; j < n; j++) { + int temp = matrix[i][j]; + matrix[i][j] = matrix[n - 1 - i][j];//(n-1) is end index, then, matrix[(n-1) - i] means the Symmetry element opposed item matrix[i] + matrix[n - 1 - i][j] = temp; + } + } + //Flip '\' diagnal. + for (int i = 0; i < n; i++) { + for (int j = i + 1; j < n; j++) {//Skip j = i, which is the diagnal. It's not necessary, so we do j = i + 1 + int temp = matrix[i][j]; + matrix[i][j] = matrix[j][i]; + matrix[j][i] = temp; + } + } + } +} + +/* +Method2: Based on method1, we can figure out: if the current position is (i,j), what's the four corresponding positoins? +Then, we just do a overall swap. + +Note: We only do this on the one of the corner in the matrix. Let's do top-left corner. + +About the 4 swapping: +Already figuired out matrix[n-1-i] in method1. +X0 W2 W1 D +X1 Z2 +X2 Z1 +B Y1 Y2 C +1st swap: Use[i] to map [j] is because: + When matching B to X0, and Y1 to X1, and Y2 to X2, X's i is equal to Y' j. We want Y's j increase with X's i increasing. So, map matrix[i][] = matrix[][i] +2nd swap: + Z's i decrease as X's i incease. Same for Z's j +3rd swap: + W's i stable as X's j. However, W's j decrease as X's j increase. +4th swap: + +*/ +public class Solution { + public void rotate(int[][] matrix) { + if (matrix == null || matrix[0].length == 0) { + return; + } + int n = matrix.length; + for (int i = 0; i < n / 2; i++){ + for (int j = 0; j < Math.ceil(n / 2.0); j++) { + int temp = matrix[i][j]; + matrix[i][j] = matrix[n - 1 - j][i]; + matrix[n - 1 - j][i] = matrix[n - 1 - i][n - 1 - j]; + matrix[n - 1 - i][n - 1 - j] = matrix[j][n - 1 - i]; + matrix[j][n - 1 - i] = temp; + } + } + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Search a 2D Matrix II.java b/Others/old records/LeetCode-Merged/Java/Search a 2D Matrix II.java new file mode 100644 index 0000000..464bb70 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Search a 2D Matrix II.java @@ -0,0 +1,161 @@ +自己的想法比较老爷车啊。其实一旦想透了就有救。下次试一试自己来画一画来approach。 +题目想法:在右上角开刷,大小不等那么就往唯一可以去的两个方向移动,绝不回头,总可以找到。 +对题目描述的情景和性质要认真考虑清楚再下手。。 +``` +/* +Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties: + +Integers in each row are sorted in ascending from left to right. +Integers in each column are sorted in ascending from top to bottom. +For example, + +Consider the following matrix: + +[ + [1, 4, 7, 11, 15], + [2, 5, 8, 12, 19], + [3, 6, 9, 16, 22], + [10, 13, 14, 17, 24], + [18, 21, 23, 26, 30] +] +Given target = 5, return true. + +Given target = 20, return false. + +Tags: Divide and Conquer, Binary Search +Similar Problems: (M) Search a 2D Matrix + +*/ +/* +Attempt3, http://blog.csdn.net/xudli/article/details/47015825 +Do a run through smartly. Think about in a 1D array, we only have 2 directions, which is easy: if less, addup; if more, minus some. +In this relationship, the only we know is: every row, rightside is greater; every column, below is greater. +Magic from that post: set the starting point on right-up-corner, where you only have 2 directions to go: +1. If target is less than curr, move left, j--. (Upper place has nothing, don't go up) +2. If target is more than curr, move down, i++. (right side has ntohing, don't go right) +In next iteration, cut off upper row and right column. Here is the idea: +In 1: target < curr, then target < j+1 , then can put j+1 column on a throw pending +In 2: target > curr, then target > i - 1 row, then can put i - 1 column on a throw pending. +Combine both cases, the upper row and right column will not impact our next iteration, so just imagine we cut them off after dealing with them. +Therefore, in next iteration, we only need to care about the same case again + +*/ +public class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return false; + } + int i = 0; + int j = matrix[0].length - 1; + while (i < matrix.length && j >= 0) { + if (matrix[i][j] == target) { + return true; + } else if (target > matrix[i][j]) { + i++; + } else { + j--; + } + } + return false; + } +} + + + +/* +Attempt2, Thoughts: +1. Targe has to live in a column, so find this column first +2. If target > columnA(i), and target < columA(i+1), then target must be in columnA. +3. Do a binary search on columnA. +However, there is simple counter case: +[ +1, 2, 3, 4, 5, +100,101,102,103,104 +] +Look for 100. +Won't work in below code, because when I do binary search on 1st row, it goes all across the row but can't identify that the target is actually in front columns. This is beacuse, the end of 1st row does not have a meaningful relationship with the head of 2nd row. +It fails on locating the correct column. And code is too long ... +*/ +/* + +public class Solution { + public static boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return false; + } + + int m = matrix.length; + int n = matrix[0].length; + int start = 0; + int end = matrix[0].length - 1; + int i = 0; + int j = 0; + int mid; + int col = -1; + //Find column + while (start + 1 < end) { + mid = start + (end - start) / 2; + j = mid; + if (matrix[i][j] == target) { + return true; + } else if (j + 1 < n && matrix[i][j] < target && target < matrix[i][j + 1]) { + col = j; + break; + } else if (j - 1 >= 0 && matrix[i][j - 1] < target && target < matrix[i][j]) { + col = j - 1; + break; + } else if (target > matrix[i][j]) { + start = mid; + } else if (target < matrix[i][j]) { + end = mid; + } + } + if (col == -1) {//1 + j = end; + col = target >= matrix[i][j] ? end : start; + } + + //Find target + start = 0; + end = m - 1; + j = col; + while (start + 1 < end) { + mid = start + (end - start) / 2; + i = mid; + if (matrix[i][j] == target) { + return true; + } else if (target > matrix[i][j]) { + start = mid; + } else if (target < matrix[i][j]) { + end = mid; + } + }//End while + return matrix[start][j] == target || matrix[end][j] == target; + } +} + +*/ + +/* +Attempt1, Thoughts: +But not correct: because the mid point calculation is based on linear relationship between start and end. In this problem, the relationship is not linear. +The following idea only considers the way to jump to different spots, but didn't take care of how to generate the mid index in the first place. +Each spot has 3 direction to go for larget element, and another 3 directions to go for smaller element. +If match, return true; +If target is greater than mid, move start to large element around mid + if target > (i+1,j+1) : start = (i+1, j+1) + if target > (i,j+1) : start = (i, j+1) + if target > (i+1,j) : start = (i+1, j) +If target is less than mid, move end to small element around mid + if target < (i-1,j-1) : end = (i-1,j-1) + if target < (i,j-1) : end = (i, j-1) + if target < (i-1,j) : end = (i-1, j) +init: +start = 0 +end = m*n - 1; + +However, mid = start + (end - start) / 2; won't work here. +*/ + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Search a 2D Matrix.java b/Others/old records/LeetCode-Merged/Java/Search a 2D Matrix.java new file mode 100644 index 0000000..ced63f7 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Search a 2D Matrix.java @@ -0,0 +1,63 @@ +曾经有过。2D转1D。注意 行 = index/宽度 ; 列 = index%宽度。 +``` +/* +Write an efficient algorithm that searches for a value in an m x n matrix. This matrix has the following properties: + +Integers in each row are sorted from left to right. +The first integer of each row is greater than the last integer of the previous row. +For example, + +Consider the following matrix: + +[ + [1, 3, 5, 7], + [10, 11, 16, 20], + [23, 30, 34, 50] +] +Given target = 3, return true. + +Tags: Array, Binary Search +Similar Problems: (M) Search a 2D Matrix II + +*/ + +/* +Thought: +Binary search. +Treat 2D matrix as a 1D array. spot = m * i + j; +start = 0; +end = m * n - 1; +*/ +public class Solution { + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return false; + } + int m = matrix.length; + int n = matrix[0].length; + int start = 0; + int end = m * n - 1; + int mid; + int i; + int j; + while (start + 1 < end) { + mid = start + (end - start) / 2; + i = mid / n; + j = mid % n; + if (matrix[i][j] == target) { + return true; + } else if (matrix[i][j] > target) { + end = mid; + } else { + start = mid; + } + } + int s1 = start / n; + int s2 = start % n; + int e1 = end / n; + int e2 = end % n; + + return matrix[s1][s2] == target || matrix[e1][e2] == target; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/String to Integer.java b/Others/old records/LeetCode-Merged/Java/String to Integer.java new file mode 100644 index 0000000..81c7042 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/String to Integer.java @@ -0,0 +1,60 @@ +/* +Implement atoi to convert a string to an integer. + +Hint: Carefully consider all possible input cases. If you want a challenge, please do not see below and ask yourself what are the possible input cases. + +Notes: It is intended for this problem to be specified vaguely (ie, no given input specs). You are responsible to gather all the input requirements up front. +*/ + +/* +Thoughts: +Ask for requirement, and ajust the code: +Requirements for atoi: +The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value. + +The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function. + +If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed. + +If no valid conversion could be performed, a zero value is returned. If the correct value is out of the range of representable values, INT_MAX (2147483647) or INT_MIN (-2147483648) is returned. +*/ + +public class Solution { + public int myAtoi(String str) { + if (str == null || str.length() == 0) { + return 0; + } + String sign = ""; + String digits = ""; + char[] arr = str.toCharArray(); + for (int i = 0; i < arr.length; i++) { + if (digits.length() == 0 && sign.length() == 0) { + if (arr[i] == '+' || arr[i] == '-') { + sign = arr[i] + ""; + continue; + } else if (arr[i] == ' ') { + continue; + } else if (arr[i] < '0' || arr[i] > '9') { + break; + } + }//END: integer haven't begin, but can take '+/-' and skip space + + if (arr[i] >= '0' && arr[i] <= '9') { + digits += arr[i]; + } else { + break; + } + } + if (digits.length() == 0) {//Check if empty() + return 0; + } + if (digits.length() > 10) {//Check if we have more than 10 digits. It's here because it can exceed Long's MAX as well. + return sign.equals("-") ? Integer.MIN_VALUE: Integer.MAX_VALUE; + } + long rst = Long.parseLong(digits) * (sign.equals("-") ? -1 : 1); + if (rst > Integer.MAX_VALUE || rst < Integer.MIN_VALUE ) {//Double check if digits is within Integer's MAX and MIN + return rst > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + return (int)rst;//Convert long to int + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Strobogrammatic Number II.java b/Others/old records/LeetCode-Merged/Java/Strobogrammatic Number II.java new file mode 100644 index 0000000..9d47fa6 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Strobogrammatic Number II.java @@ -0,0 +1,73 @@ +耗了一点时间。本以为需要DP一下,把做过的n存一下。后来发现,其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +难的case先不handle.到底之后来一次O(n) scan. +总共的时间起码是O(n/2) + O(n), 所以还是O(n) +``` +/* +A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside down). + +Find all strobogrammatic numbers that are of length = n. + +For example, +Given n = 2, return ["11","69","88","96"]. + +Hint: +Try to use recursion and notice that it should recurse with n - 2 instead of n - 1. + +Tags: Math Recursion +Similar Problems: (E) Strobogrammatic Number, (H) Strobogrammatic Number III + +*/ +/* +Thoughts: +For n, there can be k kinds of combination. Save it to map(n,k-list) +For n+2, there can be x + k-kinds-of-inner-number + y; +Treat n=0,1,2 differently. Then recurse on rest, layer by layer +At end end, do a O(n) scan to remove non-wanted items. +*/ +public class Solution { + private HashMap candidate = new HashMap(); + public List findStrobogrammatic(int n) { + List rst = new ArrayList(); + candidate.put("0", "0"); + candidate.put("1", "1"); + candidate.put("8", "8"); + candidate.put("6", "9"); + candidate.put("9", "6"); + rst = searchAndCombine(n); + for (int i = 0; i < rst.size(); i++) { + if ((Long.parseLong(rst.get(i))+"").length() != n) { + rst.remove(i); + i--; + } + } + return rst; + } + + public List searchAndCombine(int n) { + List list = new ArrayList(); + if (n <= 0) { + return list; + } else if (n == 1) { + list.add("0"); + list.add("1"); + list.add("8"); + return list; + } else if (n == 2){ + list.add("69"); + list.add("11"); + list.add("88"); + list.add("96"); + list.add("00"); + return list; + }else {//n >= 2 + List temp = searchAndCombine(n - 2); + for (String str : temp) { + for (Map.Entry entry : candidate.entrySet()) { + list.add(entry.getKey() + str + entry.getValue()); + } + } + } + return list; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Strobogrammatic Number.java b/Others/old records/LeetCode-Merged/Java/Strobogrammatic Number.java new file mode 100644 index 0000000..9d7d0eb --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Strobogrammatic Number.java @@ -0,0 +1,83 @@ +/* +A strobogrammatic number is a number that looks the same when rotated 180 degrees (looked at upside down). +Write a function to determine if a number is strobogrammatic. The number is represented as a string. +For example, the numbers "69", "88", and "818" are all strobogrammatic. +Tags: Hash Table Math +Similar Problems: (M) Strobogrammatic Number II, (H) Strobogrammatic Number III +*/ + +/* +OPTS 11.04.2015 +Thoughts: +Because the symmetric pairs are: +1-1, 8-8,0-0,6-9,9-6, we make a hashmap of it. +Create left/right pointer, where each compare has to match the pair in hashmap. + +Note: +On map.containsKey() line, need to check (right), or whichever item that map is going to map.get(...) afterwards. +If containsKey fails, return false; only when it passes through, then proceed to mpa.get() +*/ +public class Solution { + public boolean isStrobogrammatic(String num) { + if (num == null || num.length() == 0) { + return true; + } + HashMap map = new HashMap(); + map.put('0','0'); + map.put('1','1'); + map.put('8','8'); + map.put('6','9'); + map.put('9','6'); + int left = 0; + int right = num.length() - 1; + while (left <= right) { + if (!map.containsKey(num.charAt(right)) || num.charAt(left) != map.get(num.charAt(right))) { + return false; + } + left++; + right--; + } + return true; + } +} + +/* +Thoughts: +Compare digits to the symmetric postion; special care for (6,9) pair, mark it after comparision. +Elimite the cases before the for-loop run through(can do it in or as well, but that just make the code a bit complex) +Note: +Didn't use HashMap. I beleive hash map is used to mark the spot? +*/ +public class Solution { + public boolean isStrobogrammatic(String num) { + if (num == null || num.length() == 0) { + return true; + } + //Any non-strobogrammatic + if (num.indexOf("2") >= 0 || num.indexOf("3") >= 0 || + num.indexOf("4") >= 0 || num.indexOf("5") >= 0 || + num.indexOf("7") >= 0) { + return false; + } + //If only 6 or 9 exist: + if ((num.indexOf("6") >= 0 && num.indexOf("9") < 0) || + (num.indexOf("9") >= 0 && num.indexOf("6") < 0)) { + return false; + } + //Check if (6,9) or other strobogrammatic # are appearing at symmetric position + char[] arr = num.toCharArray(); + int leng = num.length(); + for (int i = 0; i < leng; i++) { + if (arr[i] == '6' || arr[i] == '9') { + if ((arr[i] == '6' && arr[leng - i - 1] != '9') || + (arr[i] == '9' && arr[leng - i - 1] != '6')) { + return false; + } + arr[i] = arr[leng - i - 1] = 'M';//marker + } else if (arr[i] != 'M' && arr[i] != arr[leng - i - 1]) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Summary Ranges.java b/Others/old records/LeetCode-Merged/Java/Summary Ranges.java new file mode 100644 index 0000000..f6370f5 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Summary Ranges.java @@ -0,0 +1,37 @@ +/* +Given a sorted integer array without duplicates, return the summary of its ranges. + +For example, given [0,1,2,4,5,7], return ["0->2","4->5","7"]. + +Tags: Array +Similar Problems: (M) Missing Ranges + + +*/ +/* +Thoughts: basic implementation, use a arraylist to catch candidates. +Detect condition, and return results. +*/ +public class Solution { + public List summaryRanges(int[] nums) { + List rst = new ArrayList(); + if (nums == null || nums.length == 0) { + return rst; + } + ArrayList list = new ArrayList(); + for (int i = 0; i < nums.length; i++) { + list.add(nums[i]); + if (i + 1 == nums.length || nums[i] + 1 != nums[i + 1]) { + if (list.size() == 1) { + rst.add(list.get(0) + ""); + } else { + rst.add(list.get(0) + "->" + list.get(list.size() - 1)); + } + list = new ArrayList(); + } + } + return rst; + } +} + +//O(n) \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Surrounded Regions.java b/Others/old records/LeetCode-Merged/Java/Surrounded Regions.java new file mode 100644 index 0000000..3cb5138 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Surrounded Regions.java @@ -0,0 +1,152 @@ +/* +Given a 2D board containing 'X' and 'O', capture all regions surrounded by 'X'. + +A region is captured by flipping all 'O's into 'X's in that surrounded region. + +For example, +X X X X +X O O X +X X O X +X O X X +After running your function, the board should be: + +X X X X +X X X X +X X X X +X O X X +Hide Tags Breadth-first Search + +Thinking Process: +Since dfs does not work, try bfs. +Very similar to DFS, however, when checking the 4 bounaries: +1. chcek the curruent point. +2. Add surrounding points into a queue. +3. Deal with the queue immediately via a while loop + +*/ +public class Solution { + private char[][] board; + private int row; + private int col; + private char target; + private char mark; + private Queue queue = new LinkedList(); + public void solve(char[][] board) { + if (board == null || board.length == 0) { + return; + } + this.board = board; + row = board.length; + col = board[0].length; + target = 'O'; + mark = 'M'; + //Check the board + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (i == 0 || i == row - 1 || j == 0 || j == col - 1) { + check(i,j); + } + } + } + //Replacement + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (board[i][j] == target) { + board[i][j] = 'X'; + } + if (board[i][j] == mark) { + board[i][j] = target; + } + } + } + } + //BFS + public void check(int i, int j) { + fill(board, i, j); + while(!queue.isEmpty()) { + int val = queue.poll(); + int x = val / col; + int y = val % col; + fill(board, x - 1, y); + fill(board, x + 1, y); + fill(board, x, y - 1); + fill(board, x, y + 1); + } + } + public void fill(char[][] board, int i, int j) { + if (i < 0 || i >= row || j < 0 || j >= col || board[i][j] != target) { + return; + } + board[i][j] = mark; + queue.offer(i * col + j); + } +} + + + + +/* +Thinking process: +Using DFS. +1. Whenever the edge has an 'O', all touching point with 'O' will be non-surrounded by 'X'. SO check the 4 bounds first. Mark all non-surrounded point as M. +2. Replace all remaining 'O' with 'X' +3. Replace 'M' with 'O' +However, in the LeetCode test, DFS gives stack overflow. So we'd use BFS instead. +*/ + +/* + +//The following is using DFS, but gives Stackoverflow. +public class Solution { + private char[][] board; + private int row; + private int col; + private char target; + private char mark; + public void solve(char[][] board) { + if (board == null || board.length == 0) { + return; + } + this.board = board; + target = 'O'; + mark = 'M'; + row = board.length; + col = board[0].length; + + //check bound + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (i == 0 || j == 0 || i == row - 1 || j == col - 1) { + check(i, j); + } + } + } + //1. replace remaining target with 'x' + //2. replace all mark with 'O' + for (int i = 0; i < row; i++) { + for (int j = 0; j < col; j++) { + if (board[i][j] == target) { + board[i][j] = 'X'; + } else if (board[i][j] == mark) { + board[i][j] = 'O'; + } + } + } + } + + public void check(int i, int j) { + if (i < 0 || j < 0 || i > row - 1 || j > col - 1) { + return; + } + if (board[i][j] == target) { + board[i][j] = mark; + check(i - 1, j); + check(i + 1, j); + check(i, j - 1); + check(i, j + 1); + } + } + +} + +*/ diff --git a/Others/old records/LeetCode-Merged/Java/Symmetric Binar Tree.java b/Others/old records/LeetCode-Merged/Java/Symmetric Binar Tree.java new file mode 100644 index 0000000..a4cb826 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Symmetric Binar Tree.java @@ -0,0 +1,93 @@ +注意Symmetric Binary Tree的例子和定义。 +并不是说左右两个tree相等。 +``` +/* +Given a binary tree, check whether it is a mirror of itself (ie, symmetric around its center). + +For example, this binary tree is symmetric: + + 1 + / \ + 2 2 + / \ / \ +3 4 4 3 +But the following is not: + 1 + / \ + 2 2 + \ \ + 3 3 +Note: +Bonus points if you could solve it both recursively and iteratively. + +confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ. + +Hide Tags Tree Depth-first Search + +*/ +/* + Recursive +*/ +public class Solution { + /** + * @param root, the root of binary tree. + * @return true if it is a mirror of itself, or false. + */ + public boolean isSymmetric(TreeNode root) { + if (root == null) { + return true; + } + return check(root.left, root.right); + } + + public boolean check(TreeNode A, TreeNode B) { + if (A == null && B == null) { + return true; + } else if (A == null || B == null) { + return false; + } + return A.val == B.val && check(A.left, B.right) && check(A.right, B.left); + } +} + + +//Non-recursive, iterative +/* + Thoughts: + Use 2 stack to hold the child that's needed to compare. + Have to use stack, otherwise, can't iterate through root node. +*/ + +public class Solution { + public boolean isSymmetric(TreeNode root) { + if (root == null) { + return true; + } + + Stack s1 = new Stack(); + Stack s2 = new Stack(); + s1.push(root.left); + s2.push(root.right); + while (!s1.isEmpty() && !s2.isEmpty()) { + TreeNode node1 = s1.pop(); + TreeNode node2 = s2.pop(); + if (node1 == null && node2 == null) { + continue; + } else if (node1 == null || node2 == null) { + return false; + } else if (node1.val != node2.val) { + return false; + } + s1.push(node1.left); + s2.push(node2.right); + s1.push(node1.right); + s2.push(node2.left); + } + + return true; + } +} + + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Trapping Rain Water.java b/Others/old records/LeetCode-Merged/Java/Trapping Rain Water.java new file mode 100644 index 0000000..adc5f7d --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Trapping Rain Water.java @@ -0,0 +1,137 @@ +/* +Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining. + +Example +Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6. + +Challenge +O(n) time and O(1) memory + +O(n) time and O(n) memory is also acceptable. + +Tags Expand +Two Pointers Forward-Backward Traversal Array + +Related Problems Expand +Medium Container With Most Water + +LeetCode: +Tags: Array, Stack, Two Pointers +Similar Problems: (M) Container With Most Water, (M) Product of Array Except Self + + +*/ +/* +Solution2: imagine there is always 2 bar that holds the water in middle; or imagine: we only have one side of the wall (right wall in this case), +and we are adding a hard-paper on leftside of the water, thus we can calculate the volumn of water in middle. +Idea is: two artificial wall has to both > water in middle, then will have water in middle. +Also, another idea of shifting highest wall: the highest wall from left and highest wall from right will hold a triangle shape of water in between, +when calculating the water in middle index by index, we can simulate the two walls in by comparing max. +Note: the true volumn for each index calculated should respect the min of the 2 highest walls. + +Note2: leftSideHighWall always store the heigest wall on left side of current index. +*/ +public class Solution { + public int trap(int[] heights) { + if (heights == null || heights.length == 0) { + return 0; + } + int[] leftSideHighWall = new int[heights.length + 1]; + leftSideHighWall[0] = 0; + for (int i = 0; i < heights.length; i++) { + leftSideHighWall[i + 1] = Math.max(leftSideHighWall[i], heights[i]); + } + int rightSideHighWall = 0; + int sum = 0; + for (int i = heights.length - 1; i >= 0; i--) { + int minOfTwoWalls = Math.min(leftSideHighWall[i], rightSideHighWall); + sum += minOfTwoWalls > heights[i] ? minOfTwoWalls - heights[i] : 0; + rightSideHighWall = Math.max(heights[i], rightSideHighWall); + } + return sum; + } +} + +/* +Solution1, Add extra and remove extra +Thoughts: +1. Find max's index, and use this index as the central pivot. (WHY? because highest bar can hold any volumn of water) +2. Process left and right separately. + a. assume each height jump fills that increased height of volumn. that is, if increased by r rows, add r rows of water into sum + b. all the black blocks (stones) should be minus from the sum on each index. +O(n) on finding the max. +O(n/2) for both left and right. +Total 2*O(n) = O(n) + +*/ + + + + +public class Solution { + /** + * @param heights: an array of integers + * @return: a integer + */ + public int trap(int[] heights) { + if (heights == null || heights.length == 0) { + return 0; + } + int max = 0; + int maxIndex = -1; + int sum = 0; + int prev; + for (int i = 0; i < heights.length; i++) { + if (heights[i] > max) { + max = heights[i]; + maxIndex = i; + } + } + + //Process left + prev = 0; + for (int i = 0; i < maxIndex; i++) { + if (heights[i] > prev) { + sum += (heights[i] - prev) * (maxIndex - i); + prev = heights[i]; + } + sum -= heights[i]; + } + + //Process right: + prev = 0; + for (int i = heights.length - 1; i > maxIndex; i--) { + if (heights[i] > prev) { + sum += (heights[i] - prev) * (i - maxIndex); + prev = heights[i]; + } + sum -= heights[i]; + } + + return sum; + } + + } + +} + + + + + + + + + + + + + + + + + + + + + diff --git a/Others/old records/LeetCode-Merged/Java/Two Sum II - Input array is sorted.java b/Others/old records/LeetCode-Merged/Java/Two Sum II - Input array is sorted.java new file mode 100644 index 0000000..f061dd9 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Two Sum II - Input array is sorted.java @@ -0,0 +1,46 @@ +/* +Given an array of integers that is already sorted in ascending order, find two numbers such that they add up to a specific target number. + +The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based. + +You may assume that each input would have exactly one solution. + +Input: numbers={2, 7, 11, 15}, target=9 +Output: index1=1, index2=2 + +Tags: Array Two Pointers, Binary Search +Similar Problems: (M) Two Sum + +*/ + + +/* +Thoughts: +Do a binary search, but do not over-complicate it: +Start, end. Check if nums[start] + nums[end] == target. +binary move it: in fact, moving the two border, 1 position at a time +*/ + +public class Solution { + public int[] twoSum(int[] nums, int target) { + int[] rst = new int[2]; + if (nums == null || nums.length <= 1) { + return rst; + } + int start = 0; + int end = nums.length - 1; + while(start < end) { + long sum = (long)(nums[start] + nums[end]); + if (target == sum) { + rst[0] = start + 1; + rst[1] = end + 1; + break; + } else if (target > sum) { + start++; + } else { + end--; + } + }//END while + return rst; + } +} diff --git a/Others/old records/LeetCode-Merged/Java/Two Sum.java b/Others/old records/LeetCode-Merged/Java/Two Sum.java new file mode 100644 index 0000000..9dc4fdd --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Two Sum.java @@ -0,0 +1,45 @@ +E +Same as https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum.java + +``` +/* +Given an array of integers, find two numbers such that they add up to a specific target number. + +The function twoSum should return indices of the two numbers such that they add up to the target, where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are not zero-based. + +You may assume that each input would have exactly one solution. + +Input: numbers={2, 7, 11, 15}, target=9 +Output: index1=1, index2=2 + +Array, Hash Table +Similar Problems: (M) 3Sum, (M) 4Sum (M), Two Sum II - Input array is sorted, (E) Two Sum III - Data structure design + +*/ + +/* +Thought: +Hash added value . +Check remaining value == target ? +*/ +public class Solution { + public int[] twoSum(int[] nums, int target) { + int[] rst = new int[2]; + if (nums == null || nums.length <= 1) { + return rst; + } + HashMap map = new HashMap(); + for (int i = 0; i < nums.length; i++) { + int remaining = target - nums[i]; + if (!map.containsKey(remaining)) { + map.put(nums[i], i); + } else { + rst[0] = map.get(remaining) + 1; + rst[1] = i + 1; + break; + } + } + return rst; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Ugly Number II.java b/Others/old records/LeetCode-Merged/Java/Ugly Number II.java new file mode 100644 index 0000000..cbeae40 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Ugly Number II.java @@ -0,0 +1,76 @@ +非常brutle的。 +每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +最后时间是n*log(n*3) + +注意: +Long +HashSet确保没有重复。 +``` +/* +Write a program to find the n-th ugly number. + +Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. +For example, 1, 2, 3, 4, 5, 6, 8, 9, 10, 12 is the sequence of the first 10 ugly numbers. + +Note that 1 is typically treated as an ugly number. +*/ + +/* + Thought: + Could do a infinite while loop, check each number see if that's an ugly number; till we find the nth. + time = m * O(avg # of factors for each integer) [m is probably a lot greater than n]~= O(m^2) + + So we definitely need to calculate it: + if we know a number x is ugly, we can *2, *3, *5 to make furture ugly numbers. + State: + dp[i]: ith ugly number. + fn: + candidate: Priority queue to hold candiates. + A HashSet() that makes sure dp[i-1]*2/3/5 is not duplicate. + candidate.add(dp[i - 1] * 2) + candidate.add(dp[i - 1] * 3) + candidate.add(dp[i - 1] * 5) + dp[i] = candidate.poll(); + init: + dp[0] = 0; + dp[1] = 1; + + return dp[n] + +Note: some number * 5 could be long. Just make sure it's long, then convert to int at the end. +*/ + +public class Solution { + public int nthUglyNumber(int n) { + if (n <= 0) { + return 0; + } else if (n == 1) { + return 1; + } + long[] dp = new long[n + 1]; + dp[0] = 0; + dp[1] = 1; + + PriorityQueue candidate = new PriorityQueue(); + HashSet set = new HashSet(); + set.add(dp[1]); + for (int i = 2; i <= n; i++) { + if (!set.contains(dp[i - 1] * 2)) { + candidate.add(dp[i - 1] * 2); + set.add(dp[i - 1] * 2); + } + if (!set.contains(dp[i - 1] * 3)) { + candidate.add(dp[i - 1] * 3); + set.add(dp[i - 1] * 3); + } + if (!set.contains(dp[i - 1] * 5)) { + candidate.add(dp[i - 1] * 5); + set.add(dp[i - 1] * 5); + } + dp[i] = candidate.poll(); + } + + return (int)dp[n]; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Ugly Number.java b/Others/old records/LeetCode-Merged/Java/Ugly Number.java new file mode 100644 index 0000000..dc8cc4e --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Ugly Number.java @@ -0,0 +1,44 @@ +/* +Write a program to check whether a given number is an ugly number. + +Ugly numbers are positive numbers whose prime factors only include 2, 3, 5. +For example, 6, 8 are ugly while 14 is not ugly since it includes another prime factor 7. + +Note that 1 is typically treated as an ugly number. + + +Hide Tags Math +Hide Similar Problems (E) Happy Number (E) Count Primes (M) Ugly Number II + +*/ +/* + Thoughts: + Find all prime factors, check if other factors exist rather than 2,3,5. + 3 while loops. + If num % 5 = 0 , keep it going. + If num % 3 = 0, keep it going. + If num % 2 = 0, keep it going ... + At the end, it should == 1. If not, return false; +*/ + + +public class Solution { + public boolean isUgly(int num) { + if (num <= 0) { + return false; + } else if (num == 1) { + return true; + } + + while (num != 0 && num % 5 == 0) { + num = num / 5; + } + while (num != 0 && num % 3 == 0) { + num = num / 3; + } + while (num != 0 && num % 2 == 0) { + num = num / 2; + } + return num == 1; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Unique Word Abbreviation.java b/Others/old records/LeetCode-Merged/Java/Unique Word Abbreviation.java new file mode 100644 index 0000000..f4a48f3 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Unique Word Abbreviation.java @@ -0,0 +1,91 @@ +/* +An abbreviation of a word follows the form . Below are some examples of word abbreviations: + +a) it --> it (no abbreviation) + + 1 +b) d|o|g --> d1g + + 1 1 1 + 1---5----0----5--8 +c) i|nternationalizatio|n --> i18n + + 1 + 1---5----0 +d) l|ocalizatio|n --> l10n +Assume you have a dictionary and given a word, find whether its abbreviation is unique in the dictionary. A word's abbreviation is unique if no other word from the dictionary has the same abbreviation. + +Example: +Given dictionary = [ "deer", "door", "cake", "card" ] + +isUnique("dear") -> false +isUnique("cart") -> true +isUnique("cane") -> false +isUnique("make") -> true + +Tags: Hash Table, Design +Similar Problems: (E) Two Sum III - Data structure design + + + +*/ +/* +Thought: +Originally, used a hashset to store all existing pattern. If checked word exist in dict hashset, then return false. +However, there is a case that: the word existed in the dict only for once, which is by accident the same as the checked work, then return true. +Therefore, we need to keep track of what word has been catagrize into pattern. SO, use a HashMap instead. + +Note: Dealing with char, integer, string. Be careful if char are turnning int integers. +*/ +public class ValidWordAbbr { + HashMap> map; + public ValidWordAbbr(String[] dict) { + if (dict == null || dict.length == 0) { + return; + } + map = new HashMap>(); + for (String s : dict) { + String str = ""; + if (s.length() <= 2) { + str = s; + } else { + str += s.charAt(0) + (s.length() - 2 + "") + s.charAt(s.length() - 1); + } + if (!map.containsKey(str)) { + ArrayList list = new ArrayList(); + list.add(s); + map.put(str, list); + } else { + if (!map.get(str).contains(s)) { + map.get(str).add(s); + } + + } + } + } + + public boolean isUnique(String word) { + if (map == null || map.size() == 0) { + return true; + } + String str = ""; + if (word.length() <= 2) { + str = word; + } else { + str += word.charAt(0) + (word.length() - 2 + "") + word.charAt(word.length() - 1); + } + if (map.containsKey(str) && map.get(str).size() == 1 && map.get(str).get(0).equals(word)) { + return true; + } + return !map.containsKey(str); + } +} + + + + + +// Your ValidWordAbbr object will be instantiated and called as such: +// ValidWordAbbr vwa = new ValidWordAbbr(dictionary); +// vwa.isUnique("Word"); +// vwa.isUnique("anotherWord"); \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Valid Anagram.java b/Others/old records/LeetCode-Merged/Java/Valid Anagram.java new file mode 100644 index 0000000..177b324 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Valid Anagram.java @@ -0,0 +1,58 @@ +/* +Given two strings s and t, write a function to determine if t is an anagram of s. + +For example, +s = "anagram", t = "nagaram", return true. +s = "rat", t = "car", return false. + +Note: +You may assume the string contains only lowercase alphabets. + +Follow up: +What if the inputs contain unicode characters? How would you adapt your solution to such case? + +Tags: Hash Table, Sort +Similar Problems: (M) Group Anagrams, (E) Palindrome Permutation + + +*/ +/* +Thoughts: +Anagram: reorder of letters. +Use HashMap to store the frequency of chars of 1st string, and check aginst 2nd string. +s character: +1; +t character: -1; +check count of each index in the map; they should all be 0 +*/ + + +public class Solution { + public boolean isAnagram(String s, String t) { + if (s == null || t == null) { + return s == null && t == null; + } else if (s.length() != t.length()) { + return false; + } + + HashMap map = new HashMap(); + for (int i = 0; i < s.length(); i++) { + if (!map.containsKey(s.charAt(i))) { + map.put(s.charAt(i), 1); + } else { + map.put(s.charAt(i), map.get(s.charAt(i)) + 1); + } + if (!map.containsKey(t.charAt(i))) { + map.put(t.charAt(i), -1); + } else { + map.put(t.charAt(i), map.get(t.charAt(i)) - 1); + } + }//END for + + for (int i = 0; i < s.length(); i++) { + if (map.get(s.charAt(i)) != 0) { + return false; + } + } + return true; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Valid Parentheses.java b/Others/old records/LeetCode-Merged/Java/Valid Parentheses.java new file mode 100644 index 0000000..98afe40 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Valid Parentheses.java @@ -0,0 +1,114 @@ +/* +Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. + +The brackets must close in the correct order, "()" and "()[]{}" are all valid but "(]" and "([)]" are not. + +Tags: Stack, String +Similar Problems: (M) Generate Parentheses, (H) Longest Valid Parentheses +*/ +/* +Attemp3: http://fisherlei.blogspot.com/2013/01/leetcode-valid-parentheses.html +Use stack. + +If the incoming char is '(,{,[', add on top. +Otherwise, check incoming char against stack.peek(), and see if they are '),},]'. If not, false. +It's the natural process of checking the parentheses by looking at the most inner circle; well, easist is just to use a stack to hold the front part of parentheses. + +Note: +Stack: isEmpty(), empty() +Be careful with the case '[])' where stack could be empty, but we still try to peek() it. +*/ +public class Solution { + public boolean isValid(String s) { + if (s == null || s.equals("()") || s.equals("{}") ||s.equals("[]")) { + return true; + } + char[] arr = s.toCharArray(); + Stack stack = new Stack(); + stack.push(arr[0]); + for (int i = 1; i < s.length(); i++) { + if ("({[".indexOf(arr[i]+"") != -1) { + stack.push(arr[i]); + continue; + } + if (stack.isEmpty()) { + return false; + } + if (arr[i] == ')' && stack.peek() != '(') { + return false; + } + if (arr[i] == '}' && stack.peek() != '{') { + return false; + } + if (arr[i] == ']' && stack.peek() != '[') { + return false; + } + stack.pop(); + } + if (!stack.isEmpty()) { + return false; + } + return true; + } +} + +/* +2nd attempt failed. +Recursive: when one block is correct, the stuff inside of them must be correct. +Incorrect: for some cases. +*/ +public class Solution { + public boolean isValid(String s) { + if (s == null || s.equals("()") || s.equals("{}") ||s.equals("[]")) { + return true; + } + if (s.length() % 2 == 1) { + return false; + } + char c = s.charAt(0); + int index = 0; + switch (c) { + case '(' : index = s.indexOf(')'); break; + case '{' : index = s.indexOf('}'); break; + case '[' : index = s.indexOf(']'); break; + default: index = -1; break; + } + + if (index == -1) { + return false; + } + + boolean middle = isValid(s.substring(1, index)); + boolean after = (index == s.length() - 1) ? true : isValid(s.substring(index)); + + return middle && after; + } +} + +/* +First attemp, Thoughts: +break into char array. +Count against each char + +However didnt consider :"([)]". failed +*/ + +public class Solution { + public boolean isValid(String s) { + if (s == null) { + return true; + } + int[] count = new int[3]; + for (char c : s.toCharArray()) { + switch (c) { + case '(' : count[0]++; break; + case ')' : count[0]--; break; + case '{' : count[1]++; break; + case '}' : count[1]--; break; + case '[' : count[2]++; break; + case ']' : count[2]--; break; + } + } + return (count[0] + count[1] + count[2]) == 0; + } +} \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Validate Binary Search Tree.java b/Others/old records/LeetCode-Merged/Java/Validate Binary Search Tree.java new file mode 100644 index 0000000..36e20af --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Validate Binary Search Tree.java @@ -0,0 +1,73 @@ +注意带有upper/lower bound. +定义:binary search tree, 左边的所有node都比root和右边的小;右边的所有node都比root和左边的大。 +最短的这个解法,判断对的case,其余false. + +``` +/* +Given a binary tree, determine if it is a valid binary search tree (BST). + +Assume a BST is defined as follows: + The left subtree of a node contains only nodes with keys less than the node's key. + The right subtree of a node contains only nodes with keys greater than the node's key. + Both the left and right subtrees must also be binary search trees. + +confused what "{1,#,2,3}" means? > read more on how binary tree is serialized on OJ. + +Tags: Tree Depth-first Search +Similar Problems: (M) Binary Tree Inorder Traversal + +*/ + +/*Current solution: Do the same as before, however, make sure to add MIN and MAX in the recursive funtion + +Note: becareful with Integer MAX and MIN. Use Long MAX and MIN +*/ + + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public boolean isValidBST(TreeNode root) { + return check(root, Long.MIN_VALUE, Long.MAX_VALUE); + } + public boolean check(TreeNode node, long MIN, long MAX) { + if (node == null) { + return true; + } + if (node.val > MIN && node.val < MAX && + check(node.left, MIN, node.val) && + check(node.right, node.val, MAX)) { + return true; + } else { + return false; + } + } +} + + + +//Another long solution, check false case and return the true case. +//NOTE: be careful with the false case on '==' +public class Solution { + public boolean isValidBST(TreeNode root) { + return check(root, Long.MIN_VALUE, Long.MAX_VALUE); + } + public boolean check(TreeNode node, long MIN, long MAX) { + if (node == null) { + return true; + } + if (node.val >= MAX || node.val <= MIN) { + return false; + } + return check(node.left, MIN, node.val) && check(node.right, node.val, MAX); + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Wiggle Sort.java b/Others/old records/LeetCode-Merged/Java/Wiggle Sort.java new file mode 100644 index 0000000..b467cbc --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Wiggle Sort.java @@ -0,0 +1,92 @@ +第一遍想太多. 其实做一个fall-through就能把问题解决,原因是因为: +这样的fall-through每次在乎两个element,可以一口气搞定,无关乎再之前的elements。 +特别的一点:flag来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! + +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 +``` +/* +Given an unsorted array nums, reorder it in-place such that nums[0] <= nums[1] >= nums[2] <= nums[3].... + +For example, given nums = [3, 5, 2, 1, 6, 4], one possible answer is [1, 6, 2, 5, 3, 4]. + +Tags: Array Sort +Similar Problems: (M) Sort Colors + +*/ + +/* +Attemp2, Thoughts: (http://www.cnblogs.com/easonliu/p/4798814.html) +Draw wiggle on original array. Whenever any postion i is not working with i-1, swap them. +Cases: +1. when nums[i] is supposed to >= nums[i - 1], however it's nums[i] < nums[i - 1], swap them. For example (nums[1] vs. nums[0]) +2. when nums[i] is supposed to <= nums[i - 1], however it's nums[i] > nums[i - 1], swap them. For example (nums[2] vs. nums[1]) +Specially, for case 2: can times a -1 on both side, to make nums[i] * -1 < nums[i - 1] * -1. Therefore we only need 1 if statement. + +Concept: whenver something does not work, fix it. (especailly now we are only taking caer of 2 elements at a time, so we can do it as a fall-through) +*/ +public class Solution { + public void wiggleSort(int[] nums) { + if (nums == null || nums.length <= 1) { + return; + } + int flag = 1; + for (int i = 1; i < nums.length; i++) { + if (flag * nums[i] < flag * nums[i - 1]) { + swap(nums, i, i - 1); + } + flag = -1 * flag; + } + } + + public void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} + + +/* +Attempt1: +Wiggle sort, ofcourse can't do Arrays.sort()! Should sort in one pass. +Also, we don't need to make each wiggle elements in ascending order (1,(4),2,(5),3,(6) ... etc). I thought it over. +---- +List out the example of 1,2,3,4,5,6,7,8, want to move (5,6,7,8) and insert between (1,2,3,4). It follows the follows patter: +Assume total length = n, and we are moving index i = (1 ~ n) +Step1: swap (n + i)/2, and i, (where initially i == 1) +Step2: swap (n + i)/2, and i+1 +Step2: i+=2; + + +public class Solution { + public void wiggleSort(int[] nums) { + if (nums == null || nums.length == 0) { + return; + } + Arrays.sort(nums); + if (nums.length <= 2) { + return; + } + int leng = nums.length; + int ind1 = 0; + int ind2 = 0 + for (int i = 1; i < leng; i++) { + //Step1 + ind1 = (leng + i) / 2; + ind2 = i; + swap(ind1, ind2); + //Step2: + ind2 = i + 1; + swap(ind1, ind2); + } + } + + public void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } +} +*/ + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Word Pattern.java b/Others/old records/LeetCode-Merged/Java/Word Pattern.java new file mode 100644 index 0000000..fa8d176 --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Word Pattern.java @@ -0,0 +1,69 @@ +每个char代表一个pattern。用HashMap. +但不够,如果a也match dog, b也match dog, 纠错了。比如pattern = "abba", str = "dog dog dog dog"。 +因此第二个HashMap 反过来。 +确保pattern和str一一对应。 +``` +/* +Given a pattern and a string str, find if str follows the same pattern. + +Here follow means a full match, such that there is a bijection between a letter in pattern and a non-empty word in str. + +Examples: +pattern = "abba", str = "dog cat cat dog" should return true. +pattern = "abba", str = "dog cat cat fish" should return false. +pattern = "aaaa", str = "dog cat cat dog" should return false. +pattern = "abba", str = "dog dog dog dog" should return false. +Notes: +You may assume pattern contains only lowercase letters, and str contains lowercase letters separated by a single space. + +Credits: +Special thanks to @minglotus6 for adding this problem and creating all test cases. + +Hide Company Tags Dropbox +Hide Tags Hash Table +Hide Similar Problems (E) Isomorphic Strings (H) Word Pattern II + +*/ + +/* +Thoughts: +2 HashMap, HashMap double check +*/ +public class Solution { + public boolean wordPattern(String pattern, String str) { + if (pattern != null && str != null && pattern.length() == 0 && str.length() == 0) { + return true; + } + if (pattern == null || pattern.length() == 0 || str == null || str.length() == 0) { + return false; + } + String[] strArr = str.split(" "); + if (pattern.length() != strArr.length) { + return false; + } + + HashMap map = new HashMap(); + HashMap mapStr = new HashMap(); + + for (int i = 0; i < strArr.length; i++){ + if (!map.containsKey(pattern.charAt(i))) { + map.put(pattern.charAt(i), strArr[i]); + } else { + if (!map.get(pattern.charAt(i)).equals(strArr[i])) { + return false; + } + } + if (!mapStr.containsKey(strArr[i])) { + mapStr.put(strArr[i], pattern.charAt(i)); + } else { + if (mapStr.get(strArr[i]) != pattern.charAt(i)) { + return false; + } + } + } + return true; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/Zigzag Iterator.java b/Others/old records/LeetCode-Merged/Java/Zigzag Iterator.java new file mode 100644 index 0000000..e0d02fd --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/Zigzag Iterator.java @@ -0,0 +1,90 @@ +这个题目相对简单. 做的时候我先考虑起来k条怎么办. 那么用个map把index和每个listmark一下就好了。 +每次next(), 相应的list的头拿下来就好。 +然后就跑圈呗,每次刷一个list头。不难。只要把几个variable维护清楚就行。 +``` +/* +Given two 1d vectors, implement an iterator to return their elements alternately. + +For example, given two 1d vectors: + +v1 = [1, 2] +v2 = [3, 4, 5, 6] +By calling next repeatedly until hasNext returns false, the order of elements returned by next should be: [1, 3, 2, 4, 5, 6]. + +Follow up: What if you are given k 1d vectors? How well can your code be extended to such cases? + +Clarification for the follow up question - Update (2015-09-18): +The "Zigzag" order is not clearly defined and is ambiguous for k > 2 cases. If "Zigzag" does not look right to you, replace "Zigzag" with "Cyclic". For example, given the following input: + +[1,2,3] +[4,5,6,7] +[8,9] +It should return [1,4,8,2,5,9,3,6,7]. + +Tags: Design +Similar Problems: (M) Binary Search Tree Iterator, (M) Flatten 2D Vector, (M) Peeking Iterator + +*/ + +/* +Thoughts: +HashMap +alter the index to pick correct list +maintain a total size variable. +hashNext: size > 0? +next(): while(map.get(index).length() == 0) : find next list. +return list.get(0), also list.remove(0) + +Note: be careful with all the size, length, index. size does not change once fixed. Remember in next(): index++ and length--; +*/ +public class ZigzagIterator { + private HashMap> map; + private int length = 0; + private int size = 0; + private int index = 0; + public ZigzagIterator(List v1, List v2) { + map = new HashMap>(); + if (v1 != null && v1.size() > 0) { + map.put(size, v1); + length += v1.size(); + size++; + } + if (v2 != null && v2.size() > 0) { + map.put(size, v2); + length += v2.size(); + size++; + } + if (length == 0) { + return; + } + } + + public int next() { + while(map.get(index).size() == 0) { + index++; + if (index == size) { + index = 0; + } + } + int rst = map.get(index).get(0); + map.get(index).remove(0); + length--; + index++; + if (index == size) { + index = 0; + } + return rst; + } + + public boolean hasNext() { + return length > 0; + } +} + + +/** + * Your ZigzagIterator object will be instantiated and called as such: + * ZigzagIterator i = new ZigzagIterator(v1, v2); + * while (i.hasNext()) v[f()] = i.next(); + */ +``` \ No newline at end of file diff --git a/Others/old records/LeetCode-Merged/Java/reverseInteger.java b/Others/old records/LeetCode-Merged/Java/reverseInteger.java new file mode 100644 index 0000000..22df33d --- /dev/null +++ b/Others/old records/LeetCode-Merged/Java/reverseInteger.java @@ -0,0 +1,35 @@ +/* +Reverse Integer + +Reverse digits of an integer. + +Example1: x = 123, return 321 +Example2: x = -123, return -321 +//input = 1534236469 + +Thinking process: +Make sure of operators. +Note: check for overflow using long. When integer is > Integer.MAX_VALUE, then it's overflow. +Initialize long : long x = 1234L; +Convert using (int) + +*/ +public class Solution { + public int reverse(int x) { + if (x == 0) { + return x; //123 + } + boolean sign = x > 0; //sign = true + long rst = 0L; + x = Math.abs(x); // 123 + while (x != 0) { //x = 123, 12, 1 + rst = rst * 10 + x % 10; //rst = 3, 30 + 2 = 32, 320 + 1 = 321 + x = x / 10; //x = 12; 1; 0 + } + if (rst < 0 || rst > Integer.MAX_VALUE) { + return 0; + } + return sign ? (int)rst : -(int)rst; + } +} + diff --git a/Others/old records/LeetCode-Merged/README.md b/Others/old records/LeetCode-Merged/README.md new file mode 100644 index 0000000..f56befe --- /dev/null +++ b/Others/old records/LeetCode-Merged/README.md @@ -0,0 +1,94 @@ +# LeetCode + +To host Java Solutions to problems from LeetCode(https://leetcode.com/problemset/algorithms/). +I Will try to revise the solutions once new problem or new testing case occurs. +Since I do not run .java files, they are formatted with markdowns to help compressing code in blog format. + +| Squence | Problem | Level | Language | +|:-------:|:--------------|:---------------|:---------:| +|0|[3Sum Smaller.java](https://github.com/shawnfan/LeetCode/blob/master/Java/3Sum Smaller.java)| |Java| +|1|[3Sum.java](https://github.com/shawnfan/LeetCode/blob/master/Java/3Sum.java)| |Java| +|2|[Alien Dictionary.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Alien Dictionary.java)| |Java| +|3|[Binary Search Tree Iterator.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Binary Search Tree Iterator.java)| |Java| +|4|[Binary Tree Inorder Traversal.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Binary Tree Inorder Traversal.java)| |Java| +|5|[Binary Tree Level Order Traversal.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Binary Tree Level Order Traversal.java)| |Java| +|6|[Binary Tree Longest Consecutive Sequence.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Binary Tree Longest Consecutive Sequence.java)| |Java| +|7|[Binary Tree Paths.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Binary Tree Paths.java)| |Java| +|8|[Binary Tree Right Side View.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Binary Tree Right Side View.java)| |Java| +|9|[Building Outline.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Building Outline.java)| |Java| +|10|[Burst Balloons.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Burst Balloons.java)| |Java| +|11|[Closest Binary Search Tree Value.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Closest Binary Search Tree Value.java)| |Java| +|12|[Count Primes.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Count Primes.java)| |Java| +|13|[Course Schedule II.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Course Schedule II.java)| |Java| +|14|[Course Schedule.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Course Schedule.java)| |Java| +|15|[Encode and Decode Strings.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Encode and Decode Strings.java)| |Java| +|16|[Excel Sheet Column Number.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Excel Sheet Column Number.java)| |Java| +|17|[ExcelSheetColumnNumber .java](https://github.com/shawnfan/LeetCode/blob/master/Java/ExcelSheetColumnNumber .java)| |Java| +|18|[Find Peak Element.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Find Peak Element.java)| |Java| +|19|[First Bad Version.java](https://github.com/shawnfan/LeetCode/blob/master/Java/First Bad Version.java)| |Java| +|20|[Flatten 2D Vector.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Flatten 2D Vector.java)| |Java| +|21|[Flattern 2D Vector.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Flattern 2D Vector.java)| |Java| +|22|[Flip Game II.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Flip Game II.java)| |Java| +|23|[Flip Game.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Flip Game.java)| |Java| +|24|[Fraction to Recurring Decimal.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Fraction to Recurring Decimal.java)| |Java| +|25|[Game of Life.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Game of Life.java)| |Java| +|26|[Generate Parentheses.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Generate Parentheses.java)| |Java| +|27|[Graph Valid Tree.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Graph Valid Tree.java)| |Java| +|28|[Gray Code.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Gray Code.java)| |Java| +|29|[Group Anagrams.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Group Anagrams.java)| |Java| +|30|[H-Index II.java](https://github.com/shawnfan/LeetCode/blob/master/Java/H-Index II.java)| |Java| +|31|[H-Index.java](https://github.com/shawnfan/LeetCode/blob/master/Java/H-Index.java)| |Java| +|32|[Implement strStr().java](https://github.com/shawnfan/LeetCode/blob/master/Java/Implement strStr().java)| |Java| +|33|[Implement Trie (Prefix Tree).java](https://github.com/shawnfan/LeetCode/blob/master/Java/Implement Trie (Prefix Tree).java)| |Java| +|34|[Insert Interval.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Insert Interval.java)| |Java| +|35|[Integer to English Words.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Integer to English Words.java)| |Java| +|36|[Intersection of Two Linked Lists.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Intersection of Two Linked Lists.java)| |Java| +|37|[Jump Game.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Jump Game.java)| |Java| +|38|[Kth Smallest Element in a BST.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Kth Smallest Element in a BST.java)| |Java| +|39|[Letter Combinations of a Phone Number.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Letter Combinations of a Phone Number.java)| |Java| +|40|[Longest Palindromic Substring.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Longest Palindromic Substring.java)| |Java| +|41|[Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Lowest Common Ancestor of a Binary Search Tree.java)| |Java| +|42|[LRU Cache.java](https://github.com/shawnfan/LeetCode/blob/master/Java/LRU Cache.java)| |Java| +|43|[MaximumSubarray.java](https://github.com/shawnfan/LeetCode/blob/master/Java/MaximumSubarray.java)| |Java| +|44|[Median of Two Sorted Arrays.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Median of Two Sorted Arrays.java)| |Java| +|45|[Meeting Rooms II.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Meeting Rooms II.java)| |Java| +|46|[Meeting Rooms.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Meeting Rooms.java)| |Java| +|47|[Merge Intervals.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Merge Intervals.java)| |Java| +|48|[Merge k Sorted Lists.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Merge k Sorted Lists.java)| |Java| +|49|[Merge Two Sorted Lists.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Merge Two Sorted Lists.java)| |Java| +|50|[Min Stack.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Min Stack.java)| |Java| +|51|[Minimum Height Trees.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Minimum Height Trees.java)| |Java| +|52|[Missing Ranges.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Missing Ranges.java)| |Java| +|53|[Multiply Strings.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Multiply Strings.java)| |Java| +|54|[Number of Islands.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Number of Islands.java)| |Java| +|55|[One Edit Distance.java](https://github.com/shawnfan/LeetCode/blob/master/Java/One Edit Distance.java)| |Java| +|56|[Paint Fence.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Paint Fence.java)| |Java| +|57|[Palindrome Permutation.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Palindrome Permutation.java)| |Java| +|58|[Peeking Iterator.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Peeking Iterator.java)| |Java| +|59|[Perfect Squares.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Perfect Squares.java)| |Java| +|60|[Plus One.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Plus One.java)| |Java| +|61|[Restore IP Addresses.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Restore IP Addresses.java)| |Java| +|62|[Reverse Linked List.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Reverse Linked List.java)| |Java| +|63|[Reverse Words in a String II.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Reverse Words in a String II.java)| |Java| +|64|[reverseInteger.java](https://github.com/shawnfan/LeetCode/blob/master/Java/reverseInteger.java)| |Java| +|65|[Rotate Image.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Rotate Image.java)| |Java| +|66|[Search a 2D Matrix II.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Search a 2D Matrix II.java)| |Java| +|67|[Search a 2D Matrix.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Search a 2D Matrix.java)| |Java| +|68|[String to Integer.java](https://github.com/shawnfan/LeetCode/blob/master/Java/String to Integer.java)| |Java| +|69|[Strobogrammatic Number II.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Strobogrammatic Number II.java)| |Java| +|70|[Strobogrammatic Number.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Strobogrammatic Number.java)| |Java| +|71|[Summary Ranges.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Summary Ranges.java)| |Java| +|72|[Surrounded Regions.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Surrounded Regions.java)| |Java| +|73|[Symmetric Binar Tree.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Symmetric Binar Tree.java)| |Java| +|74|[Trapping Rain Water.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Trapping Rain Water.java)| |Java| +|75|[Two Sum II - Input array is sorted.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Two Sum II - Input array is sorted.java)| |Java| +|76|[Two Sum.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Two Sum.java)| |Java| +|77|[Ugly Number II.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Ugly Number II.java)| |Java| +|78|[Ugly Number.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Ugly Number.java)| |Java| +|79|[Unique Word Abbreviation.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Unique Word Abbreviation.java)| |Java| +|80|[Valid Anagram.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Valid Anagram.java)| |Java| +|81|[Valid Parentheses.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Valid Parentheses.java)| |Java| +|82|[Validate Binary Search Tree.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Validate Binary Search Tree.java)| |Java| +|83|[Wiggle Sort.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Wiggle Sort.java)| |Java| +|84|[Word Pattern.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Word Pattern.java)| |Java| +|85|[Zigzag Iterator.java](https://github.com/shawnfan/LeetCode/blob/master/Java/Zigzag Iterator.java)| |Java| diff --git a/Others/old records/LeetCode-Merged/WordPress.txt b/Others/old records/LeetCode-Merged/WordPress.txt new file mode 100644 index 0000000..dd1c56f --- /dev/null +++ b/Others/old records/LeetCode-Merged/WordPress.txt @@ -0,0 +1,2 @@ +Java Solutions to problems from LeetCode(https://leetcode.com/problemset/algorithms/). +
#Problem      Level  Language
03Sum Smaller.javaJava
13Sum.javaJava
2Alien Dictionary.javaJava
3Binary Search Tree Iterator.javaJava
4Binary Tree Inorder Traversal.javaJava
5Binary Tree Level Order Traversal.javaJava
6Binary Tree Longest Consecutive Sequence.javaJava
7Binary Tree Paths.javaJava
8Binary Tree Right Side View.javaJava
9Building Outline.javaJava
10Burst Balloons.javaJava
11Closest Binary Search Tree Value.javaJava
12Count Primes.javaJava
13Course Schedule II.javaJava
14Course Schedule.javaJava
15Encode and Decode Strings.javaJava
16Excel Sheet Column Number.javaJava
17ExcelSheetColumnNumber .javaJava
18Find Peak Element.javaJava
19First Bad Version.javaJava
20Flatten 2D Vector.javaJava
21Flattern 2D Vector.javaJava
22Flip Game II.javaJava
23Flip Game.javaJava
24Fraction to Recurring Decimal.javaJava
25Game of Life.javaJava
26Generate Parentheses.javaJava
27Graph Valid Tree.javaJava
28Gray Code.javaJava
29Group Anagrams.javaJava
30H-Index II.javaJava
31H-Index.javaJava
32Implement strStr().javaJava
33Implement Trie (Prefix Tree).javaJava
34Insert Interval.javaJava
35Integer to English Words.javaJava
36Intersection of Two Linked Lists.javaJava
37Jump Game.javaJava
38Kth Smallest Element in a BST.javaJava
39Letter Combinations of a Phone Number.javaJava
40Longest Palindromic Substring.javaJava
41Lowest Common Ancestor of a Binary Search Tree.javaJava
42LRU Cache.javaJava
43MaximumSubarray.javaJava
44Median of Two Sorted Arrays.javaJava
45Meeting Rooms II.javaJava
46Meeting Rooms.javaJava
47Merge Intervals.javaJava
48Merge k Sorted Lists.javaJava
49Merge Two Sorted Lists.javaJava
50Min Stack.javaJava
51Minimum Height Trees.javaJava
52Missing Ranges.javaJava
53Multiply Strings.javaJava
54Number of Islands.javaJava
55One Edit Distance.javaJava
56Paint Fence.javaJava
57Palindrome Permutation.javaJava
58Peeking Iterator.javaJava
59Perfect Squares.javaJava
60Plus One.javaJava
61Restore IP Addresses.javaJava
62Reverse Linked List.javaJava
63Reverse Words in a String II.javaJava
64reverseInteger.javaJava
65Rotate Image.javaJava
66Search a 2D Matrix II.javaJava
67Search a 2D Matrix.javaJava
68String to Integer.javaJava
69Strobogrammatic Number II.javaJava
70Strobogrammatic Number.javaJava
71Summary Ranges.javaJava
72Surrounded Regions.javaJava
73Symmetric Binar Tree.javaJava
74Trapping Rain Water.javaJava
75Two Sum II - Input array is sorted.javaJava
76Two Sum.javaJava
77Ugly Number II.javaJava
78Ugly Number.javaJava
79Unique Word Abbreviation.javaJava
80Valid Anagram.javaJava
81Valid Parentheses.javaJava
82Validate Binary Search Tree.javaJava
83Wiggle Sort.javaJava
84Word Pattern.javaJava
85Zigzag Iterator.javaJava
\ No newline at end of file diff --git a/Others/old records/LintCode-Backup/2 Sum.java b/Others/old records/LintCode-Backup/2 Sum.java new file mode 100644 index 0000000..e924d2c --- /dev/null +++ b/Others/old records/LintCode-Backup/2 Sum.java @@ -0,0 +1,126 @@ +M + +解法1:相对暴力简洁, HashMap,找到一个value, 存一个; 若在HashMap里面 match 到结果, 就return HashMap里存的index. O(n) space && time. + +解法2:Sort array, two pointer 前后++,--搜索。Sort 用时O(nlogn). + 1. 第一步 two pointer 找 value. + 2. 注意,要利用额外的空间保留original array, 用来时候找index. (此处不能用HashMap,因为以value 为key,但value可能重复) + O(n) space, O(nlogn) time. + + +``` + +/* +Given an array of integers, find two numbers such that they add up to a specific target number. +The function twoSum should return indices of the two numbers such that they add up to the target, +where index1 must be less than index2. Please note that your returned answers (both index1 and index2) are NOT zero-based. +Example +numbers=[2, 7, 11, 15], target=9 +return [1, 2] +Note +You may assume that each input would have exactly one solution +Challenge +Either of the following solutions are acceptable: +O(n) Space, O(nlogn) Time +O(n) Space, O(n) Time +Tags Expand +Two Pointers Sort Hash Table Array Airbnb Facebook +*/ + +/* +Thoughts: + Using a HashMap, O(n) space and O(n) time. + Thinking process: + Push everything into a HashMap. + Check if one element exist in the HashMap, if so save it. Meanwhile, save the other one. + Trick: after adding into the HashMap, we are looking for the 2nd index first. + Always check (target - current) from the HashMap. + If exist, that means index0 has already been pushed into the HashMap and current value is at index1. + (key, value) = (numbers[i], i) + Note: return index+1 because this is not 0-based. +*/ + +public class Solution { + //Using HashMap + public int[] twoSum(int[] numbers, int target) { + if (numbers == null || numbers.length == 0) { + return null; + } + int[] rst = new int[2]; + HashMap map = new HashMap(); + for (int i = 0; i < numbers.length; i++) { + if (map.containsKey(target - numbers[i])) { + rst[0] = map.get(target - numbers[i]) + 1; + rst[1] = i + 1; + } else { + map.put(numbers[i], i); + } + } + return rst; + } +} + + + +//2. O(n) Space O(nlogn) time +/* + Feels like binary search when looking at O(nlogn) + 1. sort + 2. loop all number + 3. binary search on rest +*/ +public class Solution { + public int[] twoSum(int[] numbers, int target) { + if (numbers == null || numbers.length == 0) { + return null; + } + int[] original = new int[numbers.length]; + for (int i = 0; i < numbers.length; i++) { + original[i] = numbers[i]; + } + + Arrays.sort(numbers); + int start = 0; + int end = numbers.length - 1; + int num1 = -1; + int num2 = -1; + while (start != end) { + int sum = numbers[start] + numbers[end]; + if (sum == target) { + num1 = numbers[start]; + num2 = numbers[end]; + break; + }else if (sum < target) { + start++; + } else { + end--; + } + } + + //Find the num1,num2 in original array and record the index + int[] rst = new int[2]; + rst[0] = -1; + rst[1] = -1; + for (int i = 0; i < original.length; i++) { + if (original[i] == num1 || original[i] == num2) { + if (rst[0] == -1) { + rst[0] = i + 1; + } else { + rst[1] = i + 1; + break; + } + } + } + return rst; + } +} + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/3 Sum Closest.java b/Others/old records/LintCode-Backup/3 Sum Closest.java new file mode 100644 index 0000000..da399e9 --- /dev/null +++ b/Others/old records/LintCode-Backup/3 Sum Closest.java @@ -0,0 +1,57 @@ +M + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + +``` +/* +Given an array S of n integers, find three integers in S such that the sum is closest to a given number, target. Return the sum of the three integers. +Note +You may assume that each input would have exactly one solution. +Example +For example, given array S = {-1 2 1 -4}, and target = 1. The sum that is closest to the target is 2. (-1 + 2 + 1 = 2). +Tags Expand +Two Pointers Sort Array + +*/ + +/* +Thoughts: + Similar to 3 SUM. + Starting from the left-element, assume it's the solution. Move the 2 pointers in the right-side-array. + Using the two pointers, trying to find ele1 + ele2 + ele3 = closest number to target. + Note: for comparing closet, use initial value Integer.MAX_VALUE. Be aware of the overflow of integer, use long to handle. + +*/ +public class Solution { + + public int threeSumClosest(int[] num, int target) { + if (num == null || num.length < 3) { + return Integer.MAX_VALUE; + } + Arrays.sort(num); + long closest = (long) Integer.MAX_VALUE; + for (int i = 0; i < num.length - 2; i++) { + int left = i + 1; + int right = num.length - 1; + while (left < right) { + int sum = num[i] + num[left] + num[right]; + if (sum == target) { + return sum; + } else if (sum < target) { + left++; + } else { + right--; + } + closest = Math.abs(sum - target) < Math.abs(closest - target) + ? (long) sum : closest; + }//while + }//for + return (int) closest; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/3 Sum.java b/Others/old records/LintCode-Backup/3 Sum.java new file mode 100644 index 0000000..ec2c9b9 --- /dev/null +++ b/Others/old records/LintCode-Backup/3 Sum.java @@ -0,0 +1,148 @@ +M + +用个for loop 加上 2sum 的土办法。 + +注意: + 1. 找 value triplets, 多个结果。注意,并非找index。 + 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 + +步骤: + 1. For loop 挑个数字A. + 2. 2Sum 出一堆2个数字的结果 + 3. Cross match 步骤1里面的A. + +时间 O(n^2), 两个nested loop + + +另外, 还是可以用HashMap来做2Sum。稍微短点。还是要注意handle duplicates. + + +``` +/* +Given an array S of n integers, are there elements a, b, c in S such that a + b + c = 0? +Find all unique triplets in the array which gives the sum of zero. + +Example +For example, given array S = {-1 0 1 2 -1 -4}, A solution set is: + +(-1, 0, 1) +(-1, -1, 2) +Note +Elements in a triplet (a,b,c) must be in non-descending order. (ie, a ≤ b ≤ c) + +The solution set must not contain duplicate triplets. + +Tags Expand +Two Pointers Sort Array Facebook +*/ + +/* +Thoughts: + Remember to check for null and edge-soluton. + Before everything, Arrays.sort() the given array, in order to effectively handle the duplicates. + At 3SUM level, takes 1 element out and do 2SUM on the rest of the front elements of the array. Note, 2SUM has multitple solutions (need to handle duplicates) + Cross-match the 2SUM solution with the selected element from 3SUM level. +*/ + +public class Solution { + public ArrayList> threeSum(int[] numbers) { + ArrayList> rst = new ArrayList>(); + if (numbers == null && numbers.length <= 2) {// Length at least >= 3 + return rst; + } + Arrays.sort(numbers);//Sort in order to handle duplicates + for (int i = numbers.length - 1; i >= 2; i--) {// i >=2 because at least 3 element in result; starting from end, ensures non-descending order + if (i < numbers.length - 1 && numbers[i] == numbers[i + 1]) { + continue;//The case of numbers[i + 1]: should have already covered all possibilities of the case numbers[i], so safe to skip + } + ArrayList> twoSum = calTwoSum(numbers, i - 1, 0 - numbers[i]);//Pick the 3rd element numbers[i] + for (int j = 0; j < twoSum.size(); j++) {//Find two sum of rest-front elements. Cross add them with numbers[i] + twoSum.get(j).add(numbers[i]); + } + rst.addAll(twoSum); + } + return rst; + } + //Two Sum. Multiple answer + public ArrayList> calTwoSum(int[] num, int end, int target) { + ArrayList> rst = new ArrayList>(); + int left = 0; + int right = end; + while (left < right) { + if (num[left] + num[right] == target) { + ArrayList match = new ArrayList(); + match.add(num[left]); + match.add(num[right]); + rst.add(match); + left++; + right--; + //For unique number A, there is only 1 unique number B such that A + B == target. + //Therefore, once found the match, erase all numbers that's equal to A or equal to B + while (left < right && num[left] == num[left - 1]) { + left++; + } + while (left < right && num[right] == num[right + 1]) { + right--; + } + } else if (num[left] + num[right] < target) {//Since int[] num is sorted: move L to right-side to get larger value. + left++; + } else { + right--; + } + } + return rst; + } +} + + +/* + Thoughts: + Exact same approach, except using HashMap in 2Sum +*/ +//With HashMap 2Sum +public class Solution { + public ArrayList> threeSum(int[] numbers) { + ArrayList> rst = new ArrayList>(); + if (numbers == null && numbers.length <= 2) {// Length at least >= 3 + return rst; + } + Arrays.sort(numbers);//Sort in order to handle duplicates + for (int i = numbers.length - 1; i >= 2; i--) {// i >=2 because at least 3 element in result; starting from end, ensures non-descending order + if (i < numbers.length - 1 && numbers[i] == numbers[i + 1]) { + continue;//The case of numbers[i + 1]: should have already covered all possibilities of the case numbers[i], so safe to skip + } + ArrayList> twoSum = calTwoSum(numbers, i - 1, 0 - numbers[i]);//Pick the 3rd element numbers[i] + for (int j = 0; j < twoSum.size(); j++) {//Find two sum of rest-front elements. Cross add them with numbers[i] + twoSum.get(j).add(numbers[i]); + } + rst.addAll(twoSum); + } + return rst; + } + //Two Sum. Multiple answer, with HashMap + public ArrayList> calTwoSum(int[] num, int end, int target) { + ArrayList> rst = new ArrayList>(); + ArrayList match; + HashMap map = new HashMap(); + for (int i = 0; i <= end; i++) { + if (map.containsKey(num[i])) { + match = new ArrayList(); + match.add(num[map.get(num[i])]); + match.add(num[i]); + if (!rst.contains(match)) { + rst.add(new ArrayList(match)); + } + } else { + map.put(target - num[i], i); + } + //Skip duplicate + if (i < end && num[i] == num[i + 1]) { + continue; + } + } + return rst; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/4 Sum.java b/Others/old records/LintCode-Backup/4 Sum.java new file mode 100644 index 0000000..f8fee31 --- /dev/null +++ b/Others/old records/LintCode-Backup/4 Sum.java @@ -0,0 +1,230 @@ +M + +方法1: 3Sum外面再加一层. 参考3Sum. 时间O(n^3)。 但此方法在k-sum时候,无疑过于费时间. O(n^k) + +方法2: 参见 http://lifexplorer.me/leetcode-3sum-4sum-and-k-sum/ + 1. 利用2Sum的原理,把4Sum分为连个2Sum。左一个pair,右一个pair,每个pair里面放2个数字。 + 2. 以一个点,i,作为分界口,也要列举出所有i之前的pair,作为基础。 + 3. 再尝试从所有i+1后面,找合适的2nd pair。 + + 注意:在造class Pair时候,要做@override的function: hashCode(), equals(Object d). 平时不太想得起来用。 + +``` +/* +Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? + +Find all unique quadruplets in the array which gives the sum of target. + +Example +Given array S = {1 0 -1 0 -2 2}, and target = 0. A solution set is: + +(-1, 0, 0, 1) +(-2, -1, 1, 2) +(-2, 0, 0, 2) +Note +Elements in a quadruplet (a,b,c,d) must be in non-descending order. (ie, a ≤ b ≤ c ≤ d) +The solution set must not contain duplicate quadruplets. + +Tags Expand +Two Pointers Sort Hash Table Array + +*/ + +/* +Thoughts +Perform another layer outside of 3SUM. O(n^3). +Note: If try to divide and perform two 2SUM, it will be a bit difficult. Refer to http://blog.csdn.net/linhuanmars/article/details/24826871 + +*/ +public class Solution { + + public ArrayList> fourSum(int[] numbers, int target) { + ArrayList> rst = new ArrayList>(); + if(numbers == null || numbers.length < 4) { + return rst; + } + Arrays.sort(numbers); + //Pick 1st element + for (int i = 0; i < numbers.length - 3; i++) { + if (i != 0 && numbers[i] == numbers[i - 1]) {//Check for duplicate of 1st element + continue; + } + //Pick 2nd element + for (int j = i + 1; j < numbers.length - 2; j++) { + if (j != i + 1 && numbers[j] == numbers[j - 1]) {//Check for duplicate of 2nd element + continue; + } + //Pick 3rd and 4th element + int third = j + 1; + int fourth = numbers.length - 1; + while (third < fourth) { + int sum = numbers[i] + numbers[j] + numbers[third] + numbers[fourth]; + if (sum < target) { + third++; + } else if (sum > target) { + fourth--; + } else {//sum == target + ArrayList list = new ArrayList(); + list.add(numbers[i]); + list.add(numbers[j]); + list.add(numbers[third]); + list.add(numbers[fourth]); + rst.add(list); + third++; + fourth--; + while (third < fourth && numbers[third] == numbers[third - 1]) { + third++; + } + while (third < fourth && numbers[fourth] == numbers[fourth + 1]){ + fourth--; + } + } + } + } + } + return rst; + } +} + + +/* +NOT Complete yet. Has a order issue in HashSet +http://lifexplorer.me/leetcode-3sum-4sum-and-k-sum/ +Thoughts: +Utilize 2Sum. + +*/ + +public class Solution { + //Create class Pair for HashSet to use + class Pair { + Integer x; + Integer y; + + public Pair(int x, int y){ + this.x = x; + this.y = y; + } + + @Override + public int hashCode(){ + return this.x.hashCode() + this.y.hashCode(); + } + + @Override + public boolean equals(Object d) { + if (!(d instanceof Pair)) { + return false; + } + Pair p = (Pair)d; + return (this.x == p.x) && (this.y == p.y); + } + } + + public ArrayList> fourSum(int[] numbers, int target) { + ArrayList> rst = new ArrayList>(); + if (numbers == null || numbers.length < 4) { + return rst; + } + Arrays.sort(numbers); + HashMap> map = new HashMap>(); + for (int i = 0; i < numbers.length; i++) { + for (int j = i + 1; j < numbers.length; j++) { + int sum = numbers[i] + numbers[j]; + if (map.containsKey(target - sum)) { + for (Pair p : map.get(target - sum)) { + ArrayList list = new ArrayList(); + list.add(p.x); + list.add(p.y); + list.add(numbers[i]); + list.add(numbers[j]); + if (!rst.contains(list)) { + rst.add(list); + } + } + } + } + //Add all pairs up to i + for (int j = 0; j < i; j++) { + int sum = numbers[i] + numbers[j]; + if (!map.containsKey(sum)) { + map.put(sum, new HashSet()); + } + map.get(sum).add(new Pair(numbers[j], numbers[i])); + } + } + + return rst; + } + +} + + + +public class Solution { + //Create class Pair for HashSet to use + class Pair { + Integer x; + Integer y; + + public Pair(int x, int y){ + this.x = x; + this.y = y; + } + + @Override + public int hashCode(){ + return this.x.hashCode() + this.y.hashCode(); + } + + @Override + public boolean equals(Object d) { + if (!(d instanceof Pair)) { + return false; + } + Pair p = (Pair)d; + return (this.x == p.x) && (this.y == p.y); + } + } + + public ArrayList> fourSum(int[] numbers, int target) { + ArrayList> rst = new ArrayList>(); + if (numbers == null || numbers.length < 4) { + return rst; + } + Arrays.sort(numbers); + HashMap> map = new HashMap>(); + for (int i = 0; i < numbers.length; i++) { + for (int j = i + 1; j < numbers.length; j++) { + int sum = numbers[i] + numbers[j]; + if (map.containsKey(target - sum)) { + for (Pair p : map.get(target - sum)) { + ArrayList list = new ArrayList(); + list.add(p.x); + list.add(p.y); + list.add(numbers[i]); + list.add(numbers[j]); + if (!rst.contains(list)) { + rst.add(list); + } + } + } + } + //Add all pairs up to i + for (int j = 0; j < i; j++) { + int sum = numbers[i] + numbers[j]; + if (!map.containsKey(sum)) { + map.put(sum, new ArrayList()); + } + map.get(sum).add(new Pair(numbers[j], numbers[i])); + } + } + + return rst; + } + +} + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/A+B.java b/Others/old records/LintCode-Backup/A+B.java new file mode 100644 index 0000000..63dab71 --- /dev/null +++ b/Others/old records/LintCode-Backup/A+B.java @@ -0,0 +1,52 @@ +E + +Bit Operation + + a & b: 每bit可能出得余数 + a ^ b: 每bit在此次操作可能留下的值,XOR 操作 + 每次左移余数1位,然后存到b, 再去跟a做第一步。loop until b == 0 + + +``` +/* +Write a function that add two numbers A and B. You should not use + or any arithmetic operators. + +Example +Given a=1 and b=2 return 3 + +Note +There is no need to read data from standard input stream. Both parameters are given in function aplusb, you job is to calculate the sum and return it. + +Challenge +Of course you can just return a + b to get accepted. But Can you challenge not do it like that? + +Clarification +Are a and b both 32-bit integers? + +Yes. +Can I use bit operation? + +Sure you can. +Tags Expand +Cracking The Coding Interview Bit Manipulation + + +*/ + +/* +Thought: + Bit operation. Just to remmeber this problem, doing A+B using bit. +*/ +class Solution { + public int aplusb(int a, int b) { + while (b != 0) { + int carry = a & b; + a = a ^ b; + b = carry << 1; + } + return a; + } +}; + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Add Binary.java b/Others/old records/LintCode-Backup/Add Binary.java new file mode 100644 index 0000000..3bbd79f --- /dev/null +++ b/Others/old records/LintCode-Backup/Add Binary.java @@ -0,0 +1,90 @@ +E + +方法一:土办法没技术,把binary换成数字,加起来,再换成binary。如果input很大,那么很可能int,long都hold不住。不保险。 + +方法二:一般方法,string化为charArray,然后逐位加起,最后记得处理多余的一个carry on + + +``` +/* +Add Binary + +Given two binary strings, return their sum (also a binary string). + +Example +a = 11 + +b = 1 + +Return 100 + +Tags Expand +String Binary Facebook + + + +*/ + +/* +//Thougths: +1. Turn string binary format into integer +2. add integer +3. turn integer into binary string +Note: this just test if we know how to manipulate string/binary/Integer +*/ + +public class Solution { + /** + * @param a a number + * @param b a number + * @return the result + */ + public String addBinary(String a, String b) { + if (a == null || b == null || a.length() == 0 || b.length() == 0) { + return null; + } + int decimalA = Integer.parseInt(a, 2); + int decimalB = Integer.parseInt(b, 2); + + int sum = decimalA + decimalB; + + return Integer.toBinaryString(sum); + } +} + + + +/* + Thought: + Use binary property, add all and move carry-on + String to charArray +*/ + +public class Solution { + public String addBinary(String a, String b) { + if (a == null || b == null || a.length() == 0 || b.length() == 0) { + return null; + } + char[] shortArr = a.length() < b.length() ? a.toCharArray() : b.toCharArray(); + char[] longArr = a.length() < b.length() ? b.toCharArray() : a.toCharArray(); + int carry = 0; + int shortVal = 0; + int nextCarry = 0; + int diff = longArr.length - shortArr.length; + for (int i = longArr.length - 1; i >= 0; i--) { + shortVal = (i - diff) >= 0 ? shortArr[i - diff] - '0': 0; + nextCarry = (longArr[i] - '0' + shortVal + carry) / 2; + longArr[i] =(char)((longArr[i] - '0' + shortVal + carry) % 2 + '0'); + carry = nextCarry; + } + + if (carry != 0) { + return "1" + new String(longArr); + } + + return new String(longArr); + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Add Two Numbers II.java b/Others/old records/LintCode-Backup/Add Two Numbers II.java new file mode 100644 index 0000000..5b3a138 --- /dev/null +++ b/Others/old records/LintCode-Backup/Add Two Numbers II.java @@ -0,0 +1,105 @@ +M + +LinkedList并没有反过来,那么自己反: + 方向相反。巧用stack. + +做加法都一样: + 1. carrier + 2. carrier = (rst + carrier) / 10 + 3. rst = (rst + carrier) % 10 + +``` +/* +You have two numbers represented by a linked list, where each node contains a single digit. +The digits are stored in forward order, such that the 1's digit is at the head of the list. +Write a function that adds the two numbers and returns the sum as a linked list. + +Example +Given 6->1->7 + 2->9->5. That is, 617 + 295. + +Return 9->1->2. That is, 912. + +Tags Expand +Linked List High Precision +*/ + +/* + Thoughts: Different from Add Two Numbers I, which is in reverse order. + 6 1 7 + 2 9 5 + 8 10 12 + +put the 2 linked list in 2 stacks. process the reversed list. +Save into another result stack. +At the end, return the actual order. +O(n) +*/ + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + /** + * @param l1: the first list + * @param l2: the second list + * @return: the sum list of l1 and l2 + */ + public ListNode addLists2(ListNode l1, ListNode l2) { + if (l1 == null && l2 == null) { + return null; + } else if (l1 == null || l2 == null) { + return l1 == null ? l2 : l1; + } + + Stack result = new Stack(); + Stack s1 = new Stack(); + Stack s2 = new Stack(); + + while (l1 != null) { + s1.push(l1); + l1 = l1.next; + } + + while (l2 != null) { + s2.push(l2); + l2 = l2.next; + } + + int carrier = 0; + while(!s1.isEmpty() || !s2.isEmpty()){ + int sum = 0; + if (!s1.isEmpty() && !s2.isEmpty()) { + sum += s1.pop().val + s2.pop().val; + } else if (!s1.isEmpty()) { + sum += s1.pop().val; + } else { + sum += s2.pop().val; + } + result.push(new ListNode((sum + carrier) % 10));//2, 1, 9 + carrier = (sum + carrier) / 10; // 12/10 = 1; 11/10 = 1; (8+1)/ 10 = 0 + } + if (carrier == 1) { + result.push(new ListNode(carrier)); + } + + //return results: + ListNode node = new ListNode(0); + ListNode dummy = node; + while (!result.isEmpty()) {//219 + node.next = result.pop(); + node = node.next; + } + + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Add Two Numbers.java b/Others/old records/LintCode-Backup/Add Two Numbers.java new file mode 100644 index 0000000..116250e --- /dev/null +++ b/Others/old records/LintCode-Backup/Add Two Numbers.java @@ -0,0 +1,73 @@ +E + +LinkedList都已经反转好了,直接做。 + +遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on。 + +跟Add Binary的理解方式一模一样。 + + +``` +/* +You have two numbers represented by a linked list, +where each node contains a single digit. +The digits are stored in reverse order, +such that the 1's digit is at the head of the list. +Write a function that adds the two numbers and returns the sum as a linked list. + +Example +Given 7->1->6 + 5->9->2. That is, 617 + 295. + +Return 2->1->9. That is 912. + +Given 3->1->5 and 5->9->2, return 8->0->8. + +Tags Expand +Cracking The Coding Interview Linked List High Precision +*/ + + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + /** + * @param l1: the first list + * @param l2: the second list + * @return: the sum list of l1 and l2 + */ + public ListNode addLists(ListNode l1, ListNode l2) { + ListNode rst = new ListNode(0); + ListNode dummy = rst; + int carrier = 0; + //while + while (l1 != null || l2 != null) { + if (l1 != null) { + carrier += l1.val; + l1 = l1.next; + } + if (l2 != null) { + carrier += l2.val; + l2 = l2.next; + } + rst.next = new ListNode(carrier % 10); + carrier = carrier / 10; + rst = rst.next; + } + //check the carrier + if (carrier == 1) { + rst.next = new ListNode(1); + } + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Java/Add and Search Word.java b/Others/old records/LintCode-Backup/Add and Search Word.java similarity index 83% rename from Java/Add and Search Word.java rename to Others/old records/LintCode-Backup/Add and Search Word.java index 8d8f0bf..693c4d5 100644 --- a/Java/Add and Search Word.java +++ b/Others/old records/LintCode-Backup/Add and Search Word.java @@ -1,39 +1,35 @@ +M + Trie结构, prefix tree. 节点里面有char, isEnd, HashMap -记得怎么造trie:无增有移,没node就加,有Node就移动。 -寻找word也一样,无错有移。 +记得怎么造trie:无增有移,没node就加,有node就移动。 +寻找word也一样逻辑:无错有移。 + + ``` /* -Design a data structure that supports the following two operations: - -void addWord(word) +Design a data structure that supports the following two operations: addWord(word) and search(word) -bool search(word) +search(word) can search a literal word or a regular expression string containing only letters a-z or .. -search(word) can search a literal word or a regular expression string containing only letters a-z or .. A . means it can represent any one letter. Example addWord("bad") - addWord("dad") - addWord("mad") - -search("pad") -> false - -search("bad") -> true - -search(".ad") -> true - -search("b..") -> true - +search("pad") // return false +search("bad") // return true +search(".ad") // return true +search("b..") // return true Note You may assume that all words are consist of lowercase letters a-z. -Tags: -Backtracking +Tags Expand +Trie */ + + /* Build the WordDictionary like a TrieTree. Note: the '.' indicates any letter, which means we'd have to traverse through all possible letters in current level. @@ -43,8 +39,6 @@ bool search(word) TrieNode contains that char, boolean, and HashMap of children */ -//NOT DONE YET: http://www.lintcode.com/en/problem/add-and-search-word/# - public class WordDictionary { class TrieNode{ HashMap children; @@ -108,17 +102,4 @@ public boolean searchHelper(TrieNode root, String word, int index) { // wordDictionary.search("pattern"); - - - - - - - - - - - - - ``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Anagrams.java b/Others/old records/LintCode-Backup/Anagrams.java new file mode 100644 index 0000000..4b47340 --- /dev/null +++ b/Others/old records/LintCode-Backup/Anagrams.java @@ -0,0 +1,77 @@ +M + +HashMap 的做法. sort每个string, 存进HashMap, 重复的就是anagrams,最后输出。 + toCharArray + Arrays.sort + Stirng.valueOf(char[]) + +时间n*L*O(logL),L是最长string的长度。 + + + +另一种做法:http://www.jiuzhang.com/solutions/anagrams/ + 1. take each string, count the occurrance of the 26 letters. save in int[]count. + 2. hash the int[] count and output a unique hash value. + hash = hash * a + num + a = a * b. + 3. save to hashmap in the same way as we do. + +这一步把for s: strs 里面的时间复杂度降到了O(L). L = s.length(). +Need to work on the getHash() function. + +时间变成n*O(L). Better. + + +``` +/* +Given an array of strings, return all groups of strings that are anagrams. + +Example +Given ["lint", "intl", "inlt", "code"], return ["lint", "inlt", "intl"]. + +Given ["ab", "ba", "cd", "dc", "e"], return ["ab", "ba", "cd", "dc"]. + +Note +All inputs will be in lower-case + +Tags Expand +String Hash Table + + +*/ + +/* + + Recap 12.09.2015 + Feels like put into a hashmap of each string's sorted version. > + compare each string. If match, add into it. + reurn all that has >= 2 items +*/ +public class Solution { + public List anagrams(String[] strs) { + List rst = new ArrayList(); + if (strs == null || strs.length == 0) { + return rst; + } + HashMap> map = new HashMap>(); + for (int i = 0; i < strs.length; i++) { + char[] arr = strs[i].toCharArray(); + Arrays.sort(arr); + String s = String.valueOf(arr); + if (!map.containsKey(s)) { + ArrayList list = new ArrayList(); + map.put(s, list); + } + map.get(s).add(strs[i]); + } + //check instance occurs >= 2 + for (Map.Entry> entry : map.entrySet()) { + if (entry.getValue().size() >= 2) { + rst.addAll(entry.getValue()); + } + } + return rst; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Backpack II.java b/Others/old records/LintCode-Backup/Backpack II.java new file mode 100644 index 0000000..74f50ea --- /dev/null +++ b/Others/old records/LintCode-Backup/Backpack II.java @@ -0,0 +1,122 @@ +M + +做了Backpack I, 这个就如出一辙。 +想法还是,选了A[i-1] 或者没选A[i]. +一路往前跑不回头。就出来了。 +其实这个Backpack II 还更容易看懂代码。 + +O(m)的做法: +想想,的确我们只care 最后一行,所以一个存value的就够了。 +注意:和bakcpackI的 O(m)一样的,j是倒序的。如果没有更好的j,就不要更新。就是这个道理。 + + +``` +/* +Given n items with size Ai and value Vi, and a backpack with size m. +What's the maximum value can you put into the backpack? + + +Example +Given 4 items with size [2, 3, 5, 7] and value [1, 5, 2, 4], and a backpack with size 10. +The maximum value is 9. + +Note +You cannot divide item into small pieces and the total size of items you choose should smaller or equal to m. + +Challenge +O(n x m) memory is acceptable, can you do it in O(m) memory? + +Tags Expand +LintCode Copyright Dynamic Programming Backpack +*/ + + +/* + Thoughts: + In Backpack I, we store true/false to indicate the largest j in last dp row. + Here, we can store dp[i][j] == max value. + + State: + dp[i][j] : with i-1 items that fills exaclty size j, what's the max value + + Fn: + still, picked or did not picked A[i-1] + 1. Didn't pick. Value remains the same as if we didn't add A[i-1] + 2. Picked A[i - 1]. Hence, find out previous record dp[i-1][j - A[i - 1]], then add up the A[i-1] item's value V[i-1]. + 3. Compare 1, and 2 for max value. + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - A[i - 1]] + V[i - 1]) + + Init: + dp[0][0] = 0; // 0 item, fills size 0, and of course value -> 0 + + Return: + dp[A.length][m] + + Note: + when creating dp, we do (A.length + 1) for row size, simply because we get used to checking A[i-1] for prevous record ... Just keep this style. Don't get confused. +*/ + + +public class Solution { + /** + * @param m: An integer m denotes the size of a backpack + * @param A & V: Given n items with size A[i] and value V[i] + * @return: The maximum value + */ + public int backPackII(int m, int[] A, int V[]) { + if (A == null || V == null || A.length == 0 || V.length == 0 || A.length != V.length || m <= 0) { + return 0; + } + int[][] dp = new int[A.length + 1][m + 1]; + dp[0][0] = 0; // 0 item, to make pack size = 0, of course value = 0. + + for (int i = 1; i <= A.length; i++) { + for (int j = 0; j <= m; j++) { + if (j - A[i - 1] >= 0) { + dp[i][j] = Math.max(dp[i - 1][j], dp[i - 1][j - A[i - 1]] + V[i - 1]); + } else { + dp[i][j] = dp[i - 1][j]; + } + } + } + + return dp[A.length][m]; + } +} + + +/* + To use just O(m) sapce. + Just like in Backpack I, at the end, we only care about the last row. + Why not just maintain a row, always keep the max value. + + Note: Only update dp[j] if adding A[i-1] would be greater than current dp[j] + + It's a bit hard to come up with this... but it's good exercise. +*/ + +public class Solution { + + public int backPackII(int m, int[] A, int V[]) { + if (A == null || V == null || A.length == 0 || V.length == 0 || A.length != V.length || m <= 0) { + return 0; + } + int[]dp = new int[m + 1]; + dp[0] = 0; // 0 item, to make pack size = 0, of course value = 0. + + for (int i = 1; i <= A.length; i++) { + for (int j = m; j >= 0; j--) { + if (j - A[i - 1] >= 0 && dp[j - A[i - 1]] + V[i - 1] > dp[j]) { + dp[j] = dp[j - A[i - 1]] + V[i - 1]; + } + } + } + + return dp[m]; + } +} + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Backpack.java b/Others/old records/LintCode-Backup/Backpack.java new file mode 100644 index 0000000..d2a8d92 --- /dev/null +++ b/Others/old records/LintCode-Backup/Backpack.java @@ -0,0 +1,140 @@ +M + +DP。 + row是item大小: 0, A[0], A[1] ... A[A.length -1] + col是背包累积的size: 0, 1, 2, ... m. + +想法: + dp[i][j]有这么i-1个item, 用他们可否组成size为j的背包?true/false. (反过来考虑了,不是想是否超过size j, 而是考虑是否能拼出exact size == j)。 + 注意注意:虽然dp里面一直存在i的位置,实际上考虑的是在i位置的时候,看前i-1个item. + +看一遍code,会发现: + 1. picked A[i-1]: 如果上一个item, A[i-1],被加了上来, 用j-A[i-1]看看,是否这再前一步也true. true就好啦。 + 2. did not pick A[i-1]: 那就是说,不加上A[i-1], 上一行d[i-1][j]还是需要是true。 + +最后: + 跑一边dp 最下面一个row. 从末尾开始找,最末尾的一个j (能让dp[i][j] == true)的,就是最多能装的大小 :) + +时间,空间都是:O(mn) + + + +再有: +O(m)时间的做法,具体看solution. 注意j是倒序的啊! +依然是O(mn)的空间 + + +``` +/* +Given n items with size Ai, an integer m denotes the size of a backpack. +How full you can fill this backpack? + +Example +If we have 4 items with size [2, 3, 5, 7], the backpack size is 11, we can select [2, 3, 5], +so that the max size we can fill this backpack is 10. +If the backpack size is 12. we can select [2, 3, 7] so that we can fulfill the backpack. + +You function should return the max size we can fill in the given backpack. + +Note +You can not divide any item into small pieces. + +Challenge +O(n x m) time and O(m) memory. + +O(n x m) memory is also acceptable if you do not know how to optimize memory. + +Tags Expand +LintCode Copyright Dynamic Programming Backpack + + +*/ + +/* + Thoughts: Recap on 12.02.2015 + State + DP[i][j]: i is the index of Ai, and j is the size from (0 ~ m). + It means: depending if we added A[i-1], can we full-fill j-space? Return ture/false. + Note: that is, even j == 0, and I have a item with size == 0. There is nothing to add, which means the backpack can reach j == 0. True. + However, if we have a item with size == 2, but I need to fill space == 1. + I will either pick/not pick item of size 2; either way, can't fill a backpack with size 1. False; + Function: + DP[i][j] = DP[i - 1][j] || DP[i - 1][j - A[i - 1]]; // based on if previous value is true/false: 1. didn't really add A[i-1] || 2. added A[i-1]. + Init: + DP[0][0] = true; // 0 space and 0 items, true. + All the rest are false; + + Return the very last j that makes dp[A.length][j] true. +*/ + +public class Solution { + public int backPack(int m, int[] A) { + if (A == null || A.length == 0 || m <= 0) { + return 0; + } + boolean[][] dp = new boolean[A.length + 1][m + 1]; + dp[0][0] = true; + + for (int i = 1; i <= A.length; i++) { + for (int j = 0; j <= m; j++) { + //j is large enough: + if (j - A[i - 1] >= 0) { + //not added A[i - 1] or added A[i - 1] + dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]]; + } else {// j not large enough, ofcourse not adding A[i-1] + dp[i][j] = dp[i - 1][j]; + } + } + } + //Largest possible size with dp[j] == true + for (int j = m; j >= 0; j--) { + if (dp[A.length][j]) { + return j; + } + } + return 0; + } +} + + + +/* +1D array: memory mxn, space m. Tricky tho ... + +Looking at the 2D version, we are really just checking the DP with fixed i=A.length. + +DP[j]: can we fit i items into j? +DP[j] == false || DP[j - A[i - 1]]. +Similar two cases: +1. Can't fit in, set false; +2. Can fit in, then just return if (j - A[i - 1]) works + +Core difference: only set the DP[j] when (j - A[i - 1] >= 0 && DP[j - A[i - 1]]): since we are running from m ~ 0, we don't want to override some existing values +*/ + +public class Solution { + public int backPack(int m, int[] A) { + if (A == null || m == 0) { + return 0; + } + + boolean[] DP = new boolean[m + 1]; + DP[0] = true; + for (int i = 1; i <= A.length; i++) { + for (int j = m; j >= 0; j--) { + if (j - A[i - 1] >= 0 && DP[j - A[i - 1]]) { + DP[j] = true; + } + } + } + + for (int j = m; j >= 0; j--) { + if (DP[j]) { + return j; + } + } + return 0; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Balanced Binary Tree.java b/Others/old records/LintCode-Backup/Balanced Binary Tree.java new file mode 100644 index 0000000..93b9c87 --- /dev/null +++ b/Others/old records/LintCode-Backup/Balanced Binary Tree.java @@ -0,0 +1,130 @@ +M + +1. DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. + 一旦有-1, 就全部返回。 + 最后比较返回结果是不是-1. 是-1,那就false. + Traverse 整个tree, O(n) + +2. 从基本的题目理解考虑,想到leaf node的情况。如果判断了leaf node, 那其他node应该就是可以recursive。 + 直接在isBalanced上面recursive. + 关键return false的判断情况:如果有个node是null, 那么同一行相邻的那个,一旦有了children,那么就说明两个分支的depth已经是>=2了,那么就return false. + + 然后这个可能是个小小的优化,因为不需要计算所有的depth.一旦发现一个false,其他的就不需要计算,直接返回了。 + + +``` +/* +Given a binary tree, determine if it is height-balanced. + +For this problem, a height-balanced binary tree is defined as a binary tree, +in which the depth of the two subtrees of every node never differ by more than 1. + +Example +Given binary tree A={3,9,20,#,#,15,7}, B={3,#,20,15,7} + +A) 3 B) 3 + / \ \ + 9 20 20 + / \ / \ + 15 7 15 7 +The binary tree A is a height-balanced binary tree, but B is not. + +Tags Expand +Binary Search Divide and Conquer Recursion + +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + + +/* + 12.11.2015 + Recap: + The original way of marking depth and -1 is good. However, that has to traverse entire tree. + + Today, let's think about the leaf case, and see if we can directly recurse on isBalanced itself. + leaf case: + root == null, return true; + left = root.left; right = root.right; + left == null && right == null : true; + + left == null && right != null && (right.left != null || right.right != null) { + false; + } + + need to isBalance(left) && isBalance(right). +*/ + +public class Solution { + /** + * @param root: The root of binary tree. + * @return: True if this Binary tree is Balanced, or false. + */ + public boolean isBalanced(TreeNode root) { + if (root == null || (root.left == null && root.right == null)) { + return true; + } + + TreeNode left = root.left; + TreeNode right = root.right; + //One of left or right is null. + if (left == null && (right.left != null || right.right != null)) { + return false; + } + if (right == null && (left.left != null || left.right != null)) { + return false; + } + //none of left or right is null + return isBalanced(left) && isBalanced(right); + } +} + + +/* + +Thinking process: +making use depth first search. +same process as maxDepth() method. +after recursive call, check if Math.abs(left - right) > 1. If so, return -1. +If any case return -1, they all return -1. +at the top return, check if -1. + +*/ +public class Solution { + /** + * @param root: The root of binary tree. + * @return: True if this Binary tree is Balanced, or false. + */ + public boolean isBalanced(TreeNode root) { + return maxDepth(root) != -1; + } + + public int maxDepth(TreeNode root) { + if (root == null) { + return 0; + } + + int left = maxDepth(root.left); + int right = maxDepth(root.right); + + if (Math.abs(left - right) > 1 || left == -1 || right == -1) { + return -1; + } + + return Math.max(left, right) + 1; + } +} + + + +``` \ No newline at end of file diff --git a/Java/Best Time to Buy and Sell Stock I.java b/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock I.java similarity index 68% rename from Java/Best Time to Buy and Sell Stock I.java rename to Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock I.java index a71506f..f33f2dd 100644 --- a/Java/Best Time to Buy and Sell Stock I.java +++ b/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock I.java @@ -1,24 +1,37 @@ -n天就买卖一次。 -每天都算算目前最小值Min是多少。O(n) -每天都算算和当下的Min买卖,profit最大多少。 +M + +理解意思是关键: + 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出。 + 记录每天最小值Min是多少。O(n) + 每天都算和当下的Min买卖,profit最大多少。 + + ``` /* Say you have an array for which the ith element is the price of a given stock on day i. -If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), design an algorithm to find the maximum profit. +If you were only permitted to complete at most one transaction (ie, buy one and sell one share of the stock), +design an algorithm to find the maximum profit. +Have you met this question in a real interview? Yes Example +Given an example [3,2,3,1,2], return 1 + Tags Expand -Array Dynamic Programming +Greedy Enumeration Array Facebook Uber + +*/ + +/* -Thinking process: +Thoughts First to understand what this question is asking: Each element is a price of the same stock on that specific day. Of course we want to buy in with min-price and sell out with max-price. Find the min-price by looping through the array. Find the max-profit: price[i] - min-price, which requires a loop through the loop again. We put above 2 tasks into one-pass for loop -*/ +*/ public class Solution { /** * @param prices: Given an integer array diff --git a/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock II.java b/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock II.java new file mode 100644 index 0000000..2ca5b6c --- /dev/null +++ b/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock II.java @@ -0,0 +1,69 @@ +M + +和Stock I 的区别:可以买卖多次,求总和的最大盈利。 + +找涨幅最大的区间,买卖: +找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. + +中间的: + profit += prices[peek - 1] - prices[start]; 听特别的。 + 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +O(n) + +``` +/* +Say you have an array for which the ith element is the price of a given stock on day i. + +Design an algorithm to find the maximum profit. You may complete as many transactions as you like +(ie, buy one and sell one share of the stock multiple times). However, you may not engage in multiple transactions +at the same time (ie, you must sell the stock before you buy again). + +Example +Given an example [2,1,2,0,1], return 2 + +Tags Expand +Greedy Enumeration Array +*/ + +/* +Thought: +In this case, since we know the entire stock price for all days in the array, we want to this: +Sell it at nearest peek price and buy it on the next dropped price, then sell again at next peek. +Two pointers, start and peek, to track the starting point and the peek. +Two while loop: +While loop on start, until start reaches the last 2nd index, we only have 1 option to sell. +Inner while loop that finds the peek from start. +Note: peek always has index >= start+1. +Sum up all profit and return it. +Tricky thing: we are looking for max profit, but does not require to sell the stock by end of array. +So if the price is dropping, we are not selling and we are not losing/winning anything. + +*/ +class Solution { + /** + * @param prices: Given an integer array + * @return: Maximum profit + */ + public int maxProfit(int[] prices) { + if (prices == null || prices.length == 0) { + return 0; + } + int profit = 0; + int start = 0; + int peek = 0; + while (start < prices.length - 1) { + peek = start + 1; + while (peek < prices.length && prices[peek - 1] <= prices[peek]) { + peek++; + } + profit += prices[peek - 1] - prices[start]; + start = peek; + } + return profit; + } +}; + + +``` \ No newline at end of file diff --git a/Java/Best Time to Buy and Sell Stock III .java b/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock III .java similarity index 75% rename from Java/Best Time to Buy and Sell Stock III .java rename to Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock III .java index 90a42f1..699e1d2 100644 --- a/Java/Best Time to Buy and Sell Stock III .java +++ b/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock III .java @@ -1,29 +1,42 @@ -两次卖出机会。那反正就是:找峰头;然后往下再找一个峰头。 -怎么样在才能Optimize呢。 -从两边同时开始找Max. -leftProfit是从左往右,每个i点上的最大Profit。 -rightProfit是从i点开始到结尾,每个点上的最大profit. -那么把左右两边在i上,两边相加的最大值找到就可以了。 +M + +比stock II 多了一个限制:只有2次卖出机会。也就是:找峰头;然后往下再找一个峰头。 + +怎么样在才能Optimize两次巅峰呢? + +从两边同时开始找Max!(棒棒的想法) + + leftProfit是从左往右,每个i点上的最大Profit。 + rightProfit是从i点开始到结尾,每个点上的最大profit. + 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 + +三个O(n),还是O(n) + ``` /* Say you have an array for which the ith element is the price of a given stock on day i. Design an algorithm to find the maximum profit. You may complete at most two transactions. +Example +Given an example [4,4,6,1,1,4,2,5], return 6. + Note You may not engage in multiple transactions at the same time (ie, you must sell the stock before you buy again). -Example Tags Expand -Array Dynamic Programming +Enumeration Forward-Backward Traversal Array + +*/ -Thinking process: +/* +Thoughts: Idea from NineChapter: use two arrays to mark max values, however the max values are calculated from left/right sides. Left[] marks max profit value in the range from a left-index to current index. Tracking left-min. Right[] marks max profit value in the range from current index to a right-index. Tracking right-max. If looking at right[i] and left[i] together at index i, they are always representing left-max-profit value and right-max-profit value. Add them together and get results. -*/ +*/ class Solution { /** * @param prices: Given an integer array @@ -57,11 +70,4 @@ public int maxProfit(int[] prices) { - - - - - - - ``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock IV.java b/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock IV.java new file mode 100644 index 0000000..4c79d7a --- /dev/null +++ b/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock IV.java @@ -0,0 +1,77 @@ +H + +记得要理解: 为什么 i-1天的卖了又买,可以和第 i 天的卖合成一次交易? + 因为每天交易的price是定的。所以卖了又买,等于没卖!这就是可以合并的原因。要对价格敏感啊少年。 + +Inspired from here: +http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html + +局部最优解 vs. 全局最优解: + local[i][j] = max(global[i – 1][j – 1] + diff, local[i – 1][j] + diff) + global[i][j] = max(global[i – 1][j], local[i][j]) + +local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生。 + 当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff; + 当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。 + (Note:在我下面这个solution里面没有省去 +diff) + +global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况: + 如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j]; + 如果第i天有交易(卖出),那么global[i][j]=local[i][j]。 + + + +``` +/* +Say you have an array for which the ith element is the price of a given stock on day i. + +Design an algorithm to find the maximum profit. You may complete at most k transactions. + +Example +Given prices = [4,4,6,1,1,4,2,5], and k = 2, return 6. + +Note +You may not engage in multiple transactions at the same time +(i.e., you must sell the stock before you buy again). + +Challenge +O(nk) time. + +Tags Expand +Dynamic Programming +*/ + +/* + Thoughts: http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html + local[i][j] = max(global[i – 1][j – 1] , local[i – 1][j] + diff). WHY???? + global[i][j] = max(global[i – 1][j], local[i][j]) + +*/ +class Solution { + public int maxProfit(int k, int[] prices) { + if (prices == null || prices.length < 2 || k <= 0) { + return 0; + } + if (k >= prices.length) { + int profit = 0; + for (int i = 1; i < prices.length; i++) { + if (prices[i] > prices[i - 1]) { + profit += prices[i] - prices[i - 1]; + } + } + return profit; + } + int[][] local = new int[prices.length][k + 1]; + int[][] global = new int[prices.length][k + 1]; + for (int i = 1; i < prices.length; i++) { + int diff = prices[i] - prices[i - 1]; + for (int j = 1; j <= k; j++) { + local[i][j] = Math.max(global[i-1][j-1] + diff, local[i - 1][j] + diff); + global[i][j] = Math.max(global[i-1][j], local[i][j]); + } + } + return global[prices.length - 1][k]; + } +}; + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Binary Representation.java b/Others/old records/LintCode-Backup/Binary Representation.java new file mode 100644 index 0000000..188d5e6 --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Representation.java @@ -0,0 +1,114 @@ +H + +首先要分两半解决,断点是'.': str.split("\\."); + +Integer那一半好弄,whie loop里: num%2, num/2。 + +Decimal那边复杂点. + bit == 1的数学条件:当下num * 2 >= 1。 更新: num = num * 2 - 1; + bit == 0的数学条件: num * 2 < 1. 更新: num = num * 2 + +注意:num是 double, 小数在 (num = num * 2 -1)的公式下可能无限循环. 因此check: num重复性,以及binary code < 32 bit. + +(所以题目也才有了32BIT的要求!) + +``` +/* +Given a (decimal - e.g. 3.72) number that is passed in as a string, +return the binary representation that is passed in as a string. +If the fractional part of the number can not be represented accurately in binary with at most 32 characters, return ERROR. + +Example +For n = "3.72", return "ERROR". + +For n = "3.5", return "11.1". + +Tags Expand +String Cracking The Coding Interview Bit Manipulation + +*/ + +/* + +Thoughts: +Expan the value for binary representation: +8, 4, 2, 1, 0.5, 0.25 ... etc +3, 2, 1, 0, -1, -2, ... etc + +1. Think of a good method to split the number into front . end part. +2. Deal with integer transforming into binary: integer divided by 2 see if != 0 +3. Deal with double transforming into binary: times 2 see if >= 1 + +Split: split string by '.'. +Checkfloat first: if float part is '0' or "", can just move on the integer part. + +Note: str.split("\\.") +Note2: use a set to prevent infinite loop on float: +for example: 2x - 1 = x -> x = 1. that will cause infinite loop. + +*/ +public class Solution { + public String binaryRepresentation(String n) { + if (n.length() == 0 || n.equals("0")) { + return "0"; + } + //If no '.', no decimal, just parseInteger + if (n.indexOf(".") == -1) { + return parseInteger(n); + } + //Split the string by '.' + String[] strs = n.split("\\."); + //Deal with decimal first. + String decimal = parseDecimal(strs[1]); + //If not applicable, it won't work, don't need to calculate integer part. Just return ERROR. + if (decimal.equals("ERROR")) { + return decimal; + } + //Deal with integer part + if (decimal.length() == 0 || decimal.equals("0")) { + return parseInteger(strs[0]); + } else { + return parseInteger(strs[0]) + "." + decimal; + } + } + + public String parseInteger(String n) { + if (n.length() == 0 || n.equals("0")) { + return n; + } + int num = Integer.parseInt(n); + String rst = ""; + while (num != 0) { + rst = num % 2 + rst;//mod(2) -> binary representation + num = num / 2;//小时候转换二进制也是这样。 + } + return rst; + } + // A little bit math, but implemtable. + public String parseDecimal(String n) { + if (n.length() == 0 || n.equals("0")) { + return ""; + } + //A doublem must be able to catch it. If not, that is way bigger than 32 bit. + double num = Double.parseDouble("0." + n); + //Check existance + HashSet set = new HashSet(); + String rst = ""; + while (num > 0) { + if (rst.length() > 32 || set.contains(num)) { + return "ERROR"; + } + set.add(num); + //For decimal: binary code on one spot == 1, means: num * 2 - 1 > 0 + if (num * 2 >= 1) { + rst = rst + "1"; + num = num * 2 - 1; + } else { + rst = rst + "0"; + num = num * 2; + } + } + return rst; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Binary Search Tree Iterator.java b/Others/old records/LintCode-Backup/Binary Search Tree Iterator.java new file mode 100644 index 0000000..7ab684a --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Search Tree Iterator.java @@ -0,0 +1,199 @@ +M + +用O(h)空间的做法: + +理解binary search tree inorder traversal的规律: + 先找left.left.left ....left 到底,这里是加进stack. + 然后考虑parent,然后再right. + +例如这题: + stack里面top,也就是tree最左下角的node先考虑,取名rst. + 其实这个rst拿出来以后, 它也同时是最底层left null的parent,算考虑过了最底层的parent。 + 最后就考虑最底层的parent.right, 也就是rst.right. + +注意: + next()其实有个while loop, 很可能是O(h).题目要求average O(1),所以也是okay的. + + +用O(1)空间的做法:不存stack, 时刻update current为最小值。 + +找下一个最小值: + 如果current有right child,那么再找一遍current.right的left-most child,就是最小值了。 + 如果current没有right child,那么就要找current node的右上parent. + +注意: + 一定要确保找到的parent满足parent.left == current. + 反而言之,如果current是parent的 right child, 那么下一轮就会重新process parent。 + 但是有错:binary search tree里面parent是小于right child的,也就是在之前一步肯定visit过,如此便会死循环。 + + +``` +/* +Design an iterator over a binary search tree with the following rules: + +Elements are visited in ascending order (i.e. an in-order traversal) +next() and hasNext() queries run in O(1) time in average. + +Example +For the following binary search tree, in-order traversal by using iterator is [1, 6, 10, 11, 12] + + 10 + / \ +1 11 + \ \ + 6 12 +Challenge +Extra memory usage O(h), h is the height of the tree. + +Super Star: Extra memory usage O(1) + +Tags Expand +Binary Tree LintCode Copyright Non Recursion Binary Search Tree Google LinkedIn Facebook +*/ + + + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + * Example of iterate a tree: + * BSTIterator iterator = new BSTIterator(root); + * while (iterator.hasNext()) { + * TreeNode node = iterator.next(); + * do something for node + * } + */ +/* + Thoughts:http://blog.csdn.net/u014748614/article/details/46800891 + Put all left nodes into stack. Then top of stack must be the first element in in-order-traversal. + We never add right node into stack directly, but ever time before returnning the rst node, we take care of rst.right right away. + That is, find next() when rst.right as root. + very smart use of a 'currnt' node. + It's like a pointer on the tree, but only operates when that current node is not null, and under condition of having left child. + +*/ + +public class BSTIterator { + public Stack stack = new Stack(); + public TreeNode current; + //@param root: The root of binary tree. + public BSTIterator(TreeNode root) { + current = root; + } + + //@return: True if there has next node, or false + public boolean hasNext() { + return current != null || !stack.isEmpty(); + } + + //@return: return next node + public TreeNode next() { + while (current != null) { + stack.push(current); + current = current.left; + } + TreeNode rst = stack.pop(); + current = rst.right; + return rst; + } +} + +/* + Use O(1) space, which means we will not use O(h) stack. + + To begin: + 1. hasNext()? current.val <= endNode.val to check if the tree is fully traversed. + + 2. Find min via left-most: We can alwasy look for left-most to find next minimum value. + + 3. Once left-most min is checked (name it `current`). Next min will be 2 cases: + If current.right != null, we can keep looking for current.right's left-most child, as next min. + Or, we need to look backwards for parent. Use binary search tree to find current's parent node. + + Note: when doing binary search for parent, make sure it satisfies parent.left = current. + + Because:If parent.right == current, that parent must has been visited before. In binary search tree, + we know that parent.val < parent.right.val. We need to skip this special case, since it leads + to ifinite loop. + +*/ + + +public class BSTIterator { + public TreeNode root; + public TreeNode current; + public TreeNode endNode; + //@param root: The root of binary tree. + public BSTIterator(TreeNode root) { + if (root == null) { + return; + } + this.root = root; + this.current = root; + this.endNode = root; + + while (endNode != null && endNode.right != null) { + endNode = endNode.right; + } + while (current != null && current.left != null) { + current = current.left; + } + } + + //@return: True if there has next node, or false + public boolean hasNext() { + return current != null && current.val <= endNode.val; + } + + //@return: return next node + public TreeNode next() { + TreeNode rst = current; + //current node has right child + if (current.right != null) { + current = current.right; + while (current.left != null) { + current = current.left; + } + } else {//Current node does not have right child. + current = findParent(); + } + return rst; + } + + //Find current's parent, where parent.left == current. + public TreeNode findParent(){ + TreeNode node = root; + TreeNode parent = null; + int val = current.val; + if (val == endNode.val) { + return null; + } + while (node != null) { + if (val < node.val) { + parent = node; + node = node.left; + } else if (val > node.val) { + node = node.right; + } else {//node.val == current.val + break; + } + } + return parent; + } +} + + +``` + + + + + + diff --git a/Others/old records/LintCode-Backup/Binary Tree Inorder Traversal.java b/Others/old records/LintCode-Backup/Binary Tree Inorder Traversal.java new file mode 100644 index 0000000..1b98ec6 --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Tree Inorder Traversal.java @@ -0,0 +1,117 @@ +E + +法一: +Recursive: Divide and Conquer, with helper method + +法二: +Stack: +Add left nodes all the way +Print curr +Move to right, add right if possible. + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. + +若不右移,很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + + +``` +/* +Given a binary tree, return the inorder traversal of its nodes' values. + +Example +Given binary tree {1,#,2,3}, + + 1 + \ + 2 + / + 3 + + +return [1,3,2]. + +Challenge +Can you do it without recursion? + +Tags Expand +Recursion Binary Tree Binary Tree Traversal + +*/ + +/* + 1. Use a helper method, recursively add to rst +*/ + +public class Solution { + /** + * @param root: The root of binary tree. + * @return: Inorder in ArrayList which contains node values. + */ + public ArrayList inorderTraversal(TreeNode root) { + ArrayList rst = new ArrayList(); + if (root == null) { + return rst; + } + helper(rst, root); + + return rst; + } + + public void helper(ArrayList rst, TreeNode node) { + if (node == null) { + return; + } + helper(rst, node.left); + rst.add(node.val); + helper(rst, node.right); + } +} + +/* + 2. Non-recursive + Inorder traversal: use 1 stack, push left till end; pirnt/store curr; push right to stack + 'Curr' is always moving along with the curret position, representing the current node. + + Note: after curr = curr.right, curr could be null; this will skip the while loop, and move on to next element. + + Trick: in Inorder, we care the right node least. So we keep going with left and curr; + only when there is a right node, we add it; + even after this, we go deep into that right node's left children all the way down. +*/ + +public class Solution { + /** + * @param root: The root of binary tree. + * @return: Inorder in ArrayList which contains node values. + */ + public ArrayList inorderTraversal(TreeNode root) { + ArrayList rst = new ArrayList(); + if (root == null) { + return rst; + } + + Stack stack = new Stack(); + TreeNode curr = root; + stack.add(curr); + while (!stack.isEmpty()) { + while (curr != null && curr.left != null) { + stack.push(curr.left); + curr = curr.left; + } + //Pop the top node: the curr node + curr = stack.pop(); + rst.add(curr.val); + //Move to right node, and push to stack if needed + curr = curr.right; + if (curr!= null) { + stack.push(curr); + } + } + return rst; + } +} + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Binary Tree Level Order Traversal II.java b/Others/old records/LintCode-Backup/Binary Tree Level Order Traversal II.java new file mode 100644 index 0000000..a8dedfd --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Tree Level Order Traversal II.java @@ -0,0 +1,104 @@ +M + +普通BFS,用一个queue,加上一个queue.size()来交替换行. + + +``` + +/* +Given a binary tree, return the bottom-up level order traversal of its nodes' values. +(ie, from left to right, level by level from leaf to root). + +Example +Given binary tree {3,9,20,#,#,15,7}, + + 3 + / \ + 9 20 + / \ + 15 7 + + +return its bottom-up level order traversal as: + +[ + [15,7], + [9,20], + [3] +] +Tags Expand +Queue Binary Tree Binary Tree Traversal Breadth First Search +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + + + /* + +Thoughts: +1. Non-recursive +similar to Binary Tree Level Order Traversal I, just when adding into the final result, +add to the top all the time. Then the first added will be at the bottom: result.add(0, list) +2. Recursive: + Similar to Level Traversal I, do a dfs. The difference is: everytime, we use ArrayList> like a stack by doing add(0, newList); + when populating the levelArrayList, make sure to address the correct corresponding level. + + */ + +public class Solution { + /** + * @param root: The root of binary tree. + * @return: buttom-up level order a list of lists of integer + */ + public ArrayList> levelOrderButtom(TreeNode root) { + ArrayList> result = new ArrayList>(); + if (root == null) { + return result; + } + /* + Queue queue = new LinkedList(); + queue.offer(root); + + while (!queue.isEmpty()) { + ArrayList list = new ArrayList(); + int size = queue.size(); + for (int i = 0; i < size; i++) { + TreeNode temp = queue.poll(); + list.add(temp.val); + if (temp.left != null) { + queue.offer(temp.left); + } + if (temp.right != null) { + queue.offer(temp.right); + } + } + result.add(0, list); + }*/ + + dfs(root, 0, result); + return result; + } + public void dfs(TreeNode root, int level, ArrayList> rst) { + if (root == null) { + return; + } + if (level >= rst.size()) { + rst.add(0, new ArrayList()); + } + dfs(root.left, level + 1, rst); + dfs(root.right, level + 1, rst); + rst.get(rst.size() - level - 1).add(root.val); + } +} + +``` diff --git a/Others/old records/LintCode-Backup/Binary Tree Level Order Traversal.java b/Others/old records/LintCode-Backup/Binary Tree Level Order Traversal.java new file mode 100644 index 0000000..aa2d884 --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Tree Level Order Traversal.java @@ -0,0 +1,170 @@ +M + +方法1. 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 + 或者用两个queue. 当常规queue empty,把backup queue贴上去。 + +方法2. Recursive with dfs: + 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 + 如果没有,就加上一层。 + 之后每次都通过DFS在相应的level上面加数字。 + + +``` +/* +Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). + +Example +Given binary tree {3,9,20,#,#,15,7}, + + 3 + / \ + 9 20 + / \ + 15 7 + + +return its level order traversal as: + +[ + [3], + [9,20], + [15,7] +] +Challenge +Challenge 1: Using only 1 queue to implement it. + +Challenge 2: Use DFS algorithm to do it. + +Tags Expand +Queue Binary Tree Breadth First Search Binary Tree Traversal Uber LinkedIn Facebook + +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + +/* +Thoughts: +1. Non-recursive +Use queue to withhold the parent. +Poll one parent, add this parent’s value to arrayList +Add the children into Arraylist +jump to next level +2. Recursive +use a integer to track levels. +If at a new level, then create a new ArrayList. +At each node, add the node to corresponding level-ArrayList +*/ + +//Non-recurive Iterative way: +//Even with while + for nested loop, it's just O(n) +public class Solution { + /** + * @param root: The root of binary tree. + * @return: Level order a list of lists of integer + */ + public ArrayList> levelOrder(TreeNode root) { + ArrayList> result = new ArrayList>(); + if (root == null) { + return result; + } + + //Use a queue to list elements: each row + Queue queue = new LinkedList(); + queue.offer(root); + + while (!queue.isEmpty()) { + ArrayList list = new ArrayList(); + int size = queue.size();//Limit the size, since the queue is increasing + for (int i = 0; i < size; i++) { + TreeNode levelNode = queue.poll(); + list.add(levelNode.val);//Add all the values from this current level + if (levelNode.left != null) { + queue.offer(levelNode.left); + } + if (levelNode.right != null) { + queue.offer(levelNode.right); + } + } + result.add(list); + }//while + + return result; + } +} + +//Another Iterative way: using 2 Queues +public class Solution { + public List> levelOrder(TreeNode root) { + List> rst = new ArrayList>(); + if (root == null) { + return rst; + } + Queue queue = new LinkedList(); + Queue backQueue = new LinkedList(); + queue.offer(root); + ArrayList list = new ArrayList(); + + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + if (node.left != null) { + backQueue.offer(node.left); + } + if (node.right != null) { + backQueue.offer(node.right); + } + list.add(node.val); + + if (queue.isEmpty()) { + rst.add(new ArrayList(list)); + list = new ArrayList(); + queue = backQueue; + backQueue = new LinkedList(); + } + + } + return rst; + } +} + + + +//Recursive: +//Recursive with dfs: use a level to track. Add curr into corresponding level; each level > rst.size(), add a new []. +//Note: rst is a ArrayList>, where each level is a arraylist; that is why we can add [] into rst to represent a level. + +public class Solution { + public ArrayList> levelOrder(TreeNode root) { + ArrayList> result = new ArrayList>(); + if (root == null) { + return result; + } + + dfs(root, 0, result); + return result; + } + + public void dfs(TreeNode root, int level, ArrayList> rst) { + if (root == null) { + return; + } + if (level >= rst.size()) { + rst.add(new ArrayList()); + } + rst.get(level).add(root.val); + dfs(root.left, level + 1, rst); + dfs(root.right, level + 1, rst); + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Binary Tree Maximum Path Sum II.java b/Others/old records/LintCode-Backup/Binary Tree Maximum Path Sum II.java new file mode 100644 index 0000000..16dbcff --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Tree Maximum Path Sum II.java @@ -0,0 +1,71 @@ +M + +比Binary Tree Maximum Path Sum I 简单许多. + +因为条件给的更多:at least 1 node + have to start from root => have to have root. + +Single path: either left or right. +If the path sum < 0, just skip it. + +``` +/* +Binary Tree Maximum Path Sum II + +Given a binary tree, find the maximum path sum from root. + +The path may end at any node in the tree and contain at least one node in it. + +Example +Given the below binary tree: + + 1 + / \ +2 3 +return 4. (1->3) + +Tags Expand +Binary Tree +*/ + +/* + Thoughts: maximum path sum from root, so it must include root, and it will be a single path + from root to some point in the tree. + (seems easier than Binary Tree Maximum path Sum I) + 'contains at least 1 node' -> at least have root. + However, depending on child is positive or negative, we choose add or no add child +*/ +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + * @param root the root of binary tree. + * @return an integer + */ + public int maxPathSum2(TreeNode root) { + if (root == null) { + return 0; + } + return Math.max(helper(root.left),helper(root.right)) + root.val; + } + + public int helper(TreeNode node) { + if (node == null) { + return 0; + } else if (node.left == null && node.right == null) { + return node.val > 0 ? node.val : 0; + } + int sum = Math.max(helper(node.left), helper(node.right)) + node.val; + return sum > 0 ? sum : 0; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Binary Tree Maximum Path Sum.java b/Others/old records/LintCode-Backup/Binary Tree Maximum Path Sum.java new file mode 100644 index 0000000..1d2ad38 --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Tree Maximum Path Sum.java @@ -0,0 +1,116 @@ +M + +第一次做有点难理解,复杂原因是:因为可能有负值啊。不能乱assume正数。 + single path max 的计算是为了给后面的comboMax用的。 + 如果single path max小于0,那没有什么加到parent上面的意义,所以就被再次刷为0. + +combo的三种情况:(root可能小于0) + 1. 只有left + 2。 只有右边 + 3. root大于0,那么就left,right,curr全部加起来。 + +情况1和情况2去一个最大值,然后和情况三比较。做了两个Math.max(). 然后就有了这一层的comboMax + + +12.11.2015 recap: + So totally, 5 conditions: + (save in single) + left + curr.val OR right + curr.val + (save in combo:) + left, right, OR left + curr.val + right + + + +``` +/* +Given a binary tree, find the maximum path sum. + +The path may start and end at any node in the tree. + + +Example +Given the below binary tree: + + 1 + / \ +2 3 +return 6. + +Tags Expand +Divide and Conquer Dynamic Programming Recursion + +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + + + +/* +Thoughts: +Don't assume positive integers . + +Two ways of picking nodes: 1. Single Path (left or right)has a maximum. 2. Combine them into a final result: combinedPathMax + +1. singlePathMax, two results: either pick left+root or right+root. +2. combinedPathMax: take left-max as a whole, take right-max as a whole. + 3 possible results: left-max without parent(like...when parent<0), right-max without parent(like...when parent<0), left-max + right-max + parent. +3. Use a special container to store current node's singlePathMax and combinedPathMax. + +Note:12.03.2015 +It's complex, because we could have nagative number. + +Combo is compared through: just left, just right, or combo of all. +*/ +public class Solution { + private class PathSumType { + int singlePathMax; + int combinedPathMax; + PathSumType(int singlePathMax, int combinedPathMax) { + this.singlePathMax = singlePathMax; + this.combinedPathMax = combinedPathMax; + } + } + /** + * @param root: The root of binary tree. + * @return: An integer. + */ + public int maxPathSum(TreeNode root) { + PathSumType result = helper(root); + return result.combinedPathMax; + } + + public PathSumType helper(TreeNode root) { + if (root == null) { + return new PathSumType(0, Integer.MIN_VALUE); + } + //Divide + PathSumType left = helper(root.left); + PathSumType right = helper(root.right); + //Conquer + //Step 1: prepare single path max for parent-level comparison. + int singlePathMax = Math.max(left.singlePathMax, right.singlePathMax) + root.val; + singlePathMax = Math.max(singlePathMax, 0);//If less than 0, no need to keep, because it only decrease parent-level max. + + //first comparison: does not include root node at all(this would be applicable when curr.val < 0, so we take this condition into account) + int combinedPathMax = Math.max(left.combinedPathMax, right.combinedPathMax); + //second comparison: + combinedPathMax = Math.max(combinedPathMax, left.singlePathMax + right.singlePathMax + root.val); + + return new PathSumType(singlePathMax, combinedPathMax); + } + +} + + + +``` \ No newline at end of file diff --git a/Java/Binary Tree Path Sum.java b/Others/old records/LintCode-Backup/Binary Tree Path Sum.java similarity index 92% rename from Java/Binary Tree Path Sum.java rename to Others/old records/LintCode-Backup/Binary Tree Path Sum.java index 9e8bfbc..95ab61e 100644 --- a/Java/Binary Tree Path Sum.java +++ b/Others/old records/LintCode-Backup/Binary Tree Path Sum.java @@ -1,6 +1,9 @@ -Binary Tree的一个基本题。 -遍历到底,比较sum vs. target。 -注意divde的情况。起码要把遍历的例子写写。 +E + +Binary Tree的一个基本题。 +遍历到底,比较sum vs. target。 +注意divde的情况。要把遍历的例子写写。 + ``` /* Given a binary tree, find all paths that sum of the nodes in the path equals to a given number target. diff --git a/Others/old records/LintCode-Backup/Binary Tree Paths.java b/Others/old records/LintCode-Backup/Binary Tree Paths.java new file mode 100644 index 0000000..f15a24c --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Tree Paths.java @@ -0,0 +1,157 @@ +E + +方法1: +Recursive:分叉。Helper。 + +方法2,Iterative: + 非递归练习了一下 + 因为要每次切短list, 所以再加了一个Stack 来存level + + +``` +/* +Binary Tree Paths + +Given a binary tree, return all root-to-leaf paths. + +Example +Given the following binary tree: + + 1 + / \ +2 3 + \ + 5 +All root-to-leaf paths are: + +[ + "1->2->5", + "1->3" +] +Tags Expand +Binary Tree Binary Tree Traversal Facebook Google +*/ + +/* + Thoughts: + Recursive, need a helper (root, arraylist list, List) + Add curr. + if(root.left == null && root.right == null) { + convert to String, add to list. + } + Go left. remove last node. + Go right, remove last node. + + time: 2715m +*/ + +public class Solution { + /** + * @param root the root of the binary tree + * @return all root-to-leaf paths + */ + public List binaryTreePaths(TreeNode root) { + List rst = new ArrayList(); + if (root == null) { + return rst; + } + helper(root, rst, new ArrayList()); + return rst; + } + + public void helper(TreeNode root, List rst, ArrayList list){ + list.add(root.val); + if (root.left == null && root.right == null) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < list.size() - 1; i++) { + sb.append(list.get(i) + "->"); + } + sb.append(list.get(list.size() - 1)); + rst.add(sb.toString()); + } + if (root.left != null) { + helper(root.left, rst, list); + list.remove(list.size() - 1); + } + if (root.right != null) { + helper(root.right, rst, list); + list.remove(list.size() - 1); + } + } +} + + +/* + Iterative: + Use a stack. Note. Process push curr, right, left + Special: use a levels stack, to track level. Because we are not backtracking in interative mode, + so we need manually adjust list's length back to be same level as next item in stack. + time: 2715m +*/ + +public class Solution { + public List binaryTreePaths(TreeNode root) { + List rst = new ArrayList(); + if (root == null) { + return rst; + } + Stack stack = new Stack(); + ArrayList list = new ArrayList(); + stack.push(root); + + Stack levels = new Stack(); + levels.push(0); + + while (!stack.isEmpty()) { + int lv = levels.pop(); + TreeNode node = stack.pop(); + list.add(node.val); + + if (node.left == null && node.right == null) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < list.size() - 1; i++) { + sb.append(list.get(i) + "->"); + } + sb.append(list.get(list.size() - 1)); + rst.add(sb.toString()); + while (!levels.isEmpty() && list.size() > levels.peek()) { + list.remove(list.size() - 1); + } + } + + if (node.right != null) { + stack.push(node.right); + levels.push(lv + 1); + } + if (node.left != null) { + stack.push(node.left); + levels.push(lv + 1); + } + + }//end while + + return rst; + } +} +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Binary Tree Postorder Traversal.java b/Others/old records/LintCode-Backup/Binary Tree Postorder Traversal.java new file mode 100644 index 0000000..68ed22d --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Tree Postorder Traversal.java @@ -0,0 +1,137 @@ +E + +最prefer 2 stack的做法: + stack1和stack2合作。倒水。记这个做法。。。挺神奇的。 + +Divide and Conquer 的recursive方法也非常明了! + +注意,这些binary tree traversal的题目,常常有多个做法:recursive or iterative + +``` +/* +Binary Tree Postorder Traversal +Given a binary tree, return the postorder traversal of its nodes' values. + +Example +Given binary tree {1,#,2,3}, + + 1 + \ + 2 + / + 3 + + +return [3,2,1]. + +Challenge +Can you do it without recursion? + +Tags Expand +Binary Tree + +*/ + +/* + +Thinking process: +1. Resursive: (divide and conquer) + rec on left node + rec on right node + rst.addAll(left) + rst.addAll(right) + rst.add(curr) + +*/ +public class Solution { + /** + * @param root: The root of binary tree. + * @return: Postorder in ArrayList which contains node values. + */ + public ArrayList postorderTraversal(TreeNode root) { + ArrayList rst = new ArrayList(); + if (root == null) { + return rst; + } + //Recursive: + ArrayList right = postorderTraversal(root.right); + ArrayList left = postorderTraversal(root.left); + rst.addAll(left); + rst.addAll(right); + rst.add(root.val); + return rst; + } +} + + +/* + 2. Non-recursive, iterative + use 2 stacks: pull water from s1 into s2 + in s2, we want: at each level, parentNode at bottom, then rightNode, then leftNode + loop through s2, then we print out leftNode, rightNode, parentNode ... in postOrder. +*/ +public class Solution { + /** + * @param root: The root of binary tree. + * @return: Postorder in ArrayList which contains node values. + */ + public ArrayList postorderTraversal(TreeNode root) { + ArrayList rst = new ArrayList(); + if (root == null) { + return rst; + } + //Non-recursive: + Stack s1 = new Stack(); + Stack s2 = new Stack(); + s1.push(root); + while (!s1.empty()) { + TreeNode curr = s1.pop(); + s2.push(curr); + if (curr.left != null) { + s1.push(curr.left); + } + if (curr.right != null) { + s1.push(curr.right); + } + } + while (!s2.empty()) { + rst.add(s2.pop().val); + } + return rst; + } +} + + +/* + Recap 12.08.2015 + Besides the 2 old ways of doing it, we can: + 3. Recursive with helper method. + +*/ +//Recursive with helper +public class Solution { + public ArrayList postorderTraversal(TreeNode root) { + ArrayList rst = new ArrayList(); + if (root == null) { + return rst; + } + helper(rst, root); + return rst; + } + + public void helper(ArrayListrst, TreeNode node) { + if (node == null) { + return; + } + if (node.left == null && node.right == null) { + rst.add(node.val); + return; + } + helper(node.left); + helper(node.right); + rst.add(node.val); + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Binary Tree Preorder Traversal.java b/Others/old records/LintCode-Backup/Binary Tree Preorder Traversal.java new file mode 100644 index 0000000..1fb121f --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Tree Preorder Traversal.java @@ -0,0 +1,172 @@ +E + +Preorder 写写, stack +1. Divide and conquer +2. Stack(NON-recursive) push curr, push right, push left. +3. recursive with helper method + +``` +/* +Given a binary tree, return the preorder traversal of its nodes' values. + +Note +Given binary tree {1,#,2,3}, + + 1 + \ + 2 + / + 3 + + +return [1,2,3]. + +Example +Challenge +Can you do it without recursion? + +Tags Expand +Tree Binary Tree +//Recommend way: using a stack +//Recursive way can be seen here: http://www.ninechapter.com/solutions/binary-tree-preorder-traversal/ + +*/ + +/* + + Recap: 12.08.2015 + Draw a few nodes and will realize to use stack + Cannot use queue, because whatever added on it first, will first process. + That means if we add curr,left,right; they will be processed first... but we want to traverse all left nodes first. + IN FACT: binary tree traversal are all using stack... +*/ + +//Itereative +public class Solution { + + public ArrayList preorderTraversal(TreeNode root) { + ArrayList rst = new ArrayList(); + if (root == null) { + return rst; + } + Stack stack = new Stack(); + stack.push(root); + while (!stack.isEmpty()) { + TreeNode node = stack.pop(); + if (node != null) { + rst.add(node.val); + stack.push(node.right); + stack.push(node.left); + } + } + return rst; + } +} + +//recursive +public class Solution { + public ArrayList preorderTraversal(TreeNode root) { + ArrayList rst = new ArrayList(); + if (root == null) { + return rst; + } + helper(rst, root); + return rst; + } + + public void helper(ArrayListrst, TreeNode node){ + if (node != null) { + rst.add(node.val); + helper(rst, node.left); + helper(rst, node.right); + } + } +} + + + + +/* +Thinking process: +Check if root is null +use a container to save results + +use current node +put right on stack +put left on stack +4. In next run, the ‘left’ will be on top of stack, and will be taken first. So the order becomes: parent -> left -> right + +*/ +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + * @param root: The root of binary tree. + * @return: Preorder in ArrayList which contains node values. + */ + public ArrayList preorderTraversal(TreeNode root) { + Stack stack = new Stack(); + ArrayList result = new ArrayList(); + //Check top + if (root == null) { + return result; + } + //save root + stack.push(root); + //add to result, and load on stack. Right-side at the bottom + while (!stack.empty()) { + TreeNode node = stack.pop(); + result.add(node.val); + if (node.right != null) { + stack.push(node.right); + } + if (node.left != null) { + stack.push(node.left); + } + }//while + + return result; + } +} + + + +//Divide and Conquer - recursive +/* +Check root == null? +Dive them into 2 recursive calls: get result from left, get result from right +Conquer - add all of the results together and return. As the pre-order defines: +add current parent +add left nodes +add right nodes. +*/ + + //Divide and conquer + public ArrayList preorderTraversal(TreeNode root) { + // write your code here + ArrayList result = new ArrayList(); + + if (root == null) { + return result; + } + ArrayList left = preorderTraversal(root.left); + ArrayList right = preorderTraversal(root.right); + + result.add(root.val); + result.addAll(left); + result.addAll(right); + + return result; + } + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Binary Tree Zigzag Level Order Traversal.java b/Others/old records/LintCode-Backup/Binary Tree Zigzag Level Order Traversal.java new file mode 100644 index 0000000..fda05d8 --- /dev/null +++ b/Others/old records/LintCode-Backup/Binary Tree Zigzag Level Order Traversal.java @@ -0,0 +1,91 @@ +/* 24% Accepted +Given a binary tree, return the zigzag level order traversal of its nodes' values. (ie, from left to right, then right to left for the next level and alternate between). + +Example +Given binary tree {3,9,20,#,#,15,7}, + + 3 + / \ + 9 20 + / \ + 15 7 + + +return its zigzag level order traversal as: + +[ + [3], + [20,9], + [15,7] +] +Tags Expand +Tree Search Breadth First Search Queue Binary Tree + +Thinking Process: +1. realize: queue is no longer can be used. draw a example map to see why. +Instead, use 2 stacks. +Because we can only take the top of stack, and we are constantly adding to the top of the stac, so we need 2 stacks. One is the current one, will be empty every time when we finish the level. The other one is nextLevel, which holds next level’s nodes temporarily. +2. Use a boolean to track if which level it’s running at. +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + + +public class Solution { + /** + * @param root: The root of binary tree. + * @return: A list of lists of integer include + * the zigzag level order traversal of its nodes' values + */ + public ArrayList> zigzagLevelOrder(TreeNode root) { + ArrayList> result = new ArrayList>(); + if (root == null) { + return result; + } + + Stack currentLevel = new Stack(); + Stack nextLevel = new Stack(); + + currentLevel.push(root); + boolean regularOrder = false; + + while (!currentLevel.empty()) { + ArrayList list = new ArrayList(); + + while (!currentLevel.empty()) { + TreeNode temp = currentLevel.pop(); + list.add(temp.val); + if (regularOrder) { + addLevel(nextLevel, temp.right); + addLevel(nextLevel, temp.left); + } else { + addLevel(nextLevel, temp.left); + addLevel(nextLevel, temp.right); + } + } + result.add(list); + regularOrder = !regularOrder; + Stack tmp = currentLevel; + currentLevel = nextLevel; + nextLevel = tmp; + } + return result; + } + + public void addLevel(Stack level, TreeNode node) { + if (node != null) { + level.push(node); + } + } +} + diff --git a/Java/Building Outline.java b/Others/old records/LintCode-Backup/Building Outline.java similarity index 100% rename from Java/Building Outline.java rename to Others/old records/LintCode-Backup/Building Outline.java diff --git a/Others/old records/LintCode-Backup/Classical Binary Search.java b/Others/old records/LintCode-Backup/Classical Binary Search.java new file mode 100644 index 0000000..b4e7f42 --- /dev/null +++ b/Others/old records/LintCode-Backup/Classical Binary Search.java @@ -0,0 +1,62 @@ +E + + while: start + 1 < end + mid = start + (end - start) / 2; + 末尾double check start, end. + + +``` +/* +Find any position of a target number in a sorted array. +Return -1 if target does not exist. + +Example +Given [1, 2, 2, 4, 5, 5]. + +For target = 2, return 1 or 2. + +For target = 5, return 4 or 5. + +For target = 6, return -1. + +Challenge +O(logn) time + +Tags Expand +Binary Search +*/ + +/* +Thoughts: classic +start,mid,end +*/ +public class Solution { + /** + * @param A an integer array sorted in ascending order + * @param target an integer + * @return an integer + */ + public int findPosition(int[] A, int target) { + if (A == null || A.length == 0) { + return -1; + } + int start = 0; + int end = A.length - 1; + int mid; + while(start + 1 < end) { + mid = start + (end - start) / 2; + if (target == A[mid]) { + return mid; + } else if (target > A[mid]) { + start = mid; + } else { + end = mid; + } + }//end while + if (A[start] == target || A[end] == target) { + return A[start] == target ? start : end; + } + return -1; + } +} +``` diff --git a/Others/old records/LintCode-Backup/Climbing Stairs.java b/Others/old records/LintCode-Backup/Climbing Stairs.java new file mode 100644 index 0000000..254d0b9 --- /dev/null +++ b/Others/old records/LintCode-Backup/Climbing Stairs.java @@ -0,0 +1,44 @@ +/* +40% Accepted +You are climbing a stair case. It takes n steps to reach to the top. + +Each time you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top? + +Example +Tags Expand + +Thinking process: +State: at i level, f[i] is the ways to climb to i position. +Function: f[i] = f[i-1] + f[i-2]. + f[i] is constructed from 2 branches: + Last step is 1 from f[i-1] + Last step is 2 from f[i-2] + This idea can be presented using a tree. However we don’t need to do recursive. We just need to use two pointers to withhold 2 level’s values. +Init: The for loop starts at level2, so before level 2 there are 2 init states: + f[0] == 1. This means we jump 2 steps from level0 to level2. + f[i] == 1. This means we jump 1 steps to level1, then jump another step to level2 +Answer: f[n] +*/ + +public class Solution { + /** + * @param n: An integer + * @return: An integer + */ + public int climbStairs(int n) { + if (n <= 2) { + return n; + } + int last = 1; //Init f[1] + int lastlast = 1; //Init f[0] + int now = 0; + for (int i = 2; i <= n; i++) { //Start from level2 + now = last + lastlast; + lastlast = last; + last = now; + } + return now; + } +} + + diff --git a/Others/old records/LintCode-Backup/Clone Graph.java b/Others/old records/LintCode-Backup/Clone Graph.java new file mode 100644 index 0000000..eeaabe3 --- /dev/null +++ b/Others/old records/LintCode-Backup/Clone Graph.java @@ -0,0 +1,136 @@ +/* +Clone an undirected graph. Each node in the graph contains a label and a list of its neighbors. + + +OJ's undirected graph serialization: +Nodes are labeled uniquely. + +We use # as a separator for each node, and , as a separator for node label and each neighbor of the node. +As an example, consider the serialized graph {0,1,2#1,2#2,2}. + +The graph has a total of three nodes, and therefore contains three parts as separated by #. + +First node is labeled as 0. Connect node 0 to both nodes 1 and 2. +Second node is labeled as 1. Connect node 1 to node 2. +Third node is labeled as 2. Connect node 2 to node 2 (itself), thus forming a self-cycle. +Visually, the graph looks like the following: + + 1 + / \ + / \ + 0 --- 2 + / \ + \_/ +Hide Tags Depth-first Search Breadth-first Search Graph + + +*/ + +/* + //NEED TO RUN THIS ON LINT + Thoughts: 12.12.2015 + The original thoughs of using ArrayList, and using a index to track of which node has not been visited. + It's alright, but it uses extra space, and basically copie all nodes again. + It's similar to using a queue. + At the end, it's doing O(m * n) + Maybe can improve this. + + Need a queue and process each element. and a hashmap to track duplicates. + 1. make sure the node is no duplicate + 2. make sure to all all child + + border: case: node == nul, or node has not child, return a new instance of it'self? + +*/ + +public class Solution { + public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { + if (node == null || node.neighbors.size() == 0) { + return node; + } + + HashMap map = + new HashMap(); + Queue queue = new LinkedList(); + + queue.offer(node); + //process each node + while (!queue.isEmpty()) { + UndirectedGraphNode curr = queue.poll(); + UndirectedGraphNode newNode; + if (!map.containsKey(curr)) { + map.put(curr, new UndirectedGraphNode(curr.label)); + } + UndirectedGraphNode newNode = map.get(curr); + //Add neighbors for each node + for (UndirectedGraphNode neighbor : curr.neighbors) { + UndirectedGraphNode newNeighbor; + if (!map.containsKey(neighbor)) { + map.put(neighbor, new UndirectedGraphNode(neighbor.label)); + } + newNeighbor = map.get(neighbor); + + newNode.neighbors.add(newNeighbor); + }//end for + + }//end while + + return map.get(node); + } +} + + + +/* + + +Thinking process: +1. Clone all nodes available: using HashMap to go through all possible query. No duplicates added using HashMap. + HashMap map has the list of all new nodes. No neighbors added yet + = + At same time, the arrayList nodes has all original nodes(with neighbors) in Breadth-first order. +2. Add neighbor for nodes in map: + - Locate the 'newNode' from map by using the key: the original node + - loop through the original node's neighbor size + - use original neighbor as key to get the new neighbor instance from map + - add this new neighbor instance to the neighbor list of 'newNode' +*/ +/** + * Definition for undirected graph. + * class UndirectedGraphNode { + * int label; + * List neighbors; + * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList(); } + * }; + */ +public class Solution { + public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { + if (node == null) { + return node; + } + ArrayList nodes = new ArrayList(); + nodes.add(node); + HashMap map = new HashMap(); + map.put(node, new UndirectedGraphNode(node.label)); + int start = 0; + //Clone nodes without neighbors: + while (start < nodes.size()) { + List neighbors = nodes.get(start++).neighbors; + for (int i = 0; i < neighbors.size(); i++) { + if (!map.containsKey(neighbors.get(i))) { + map.put(neighbors.get(i), new UndirectedGraphNode(neighbors.get(i).label)); + nodes.add(neighbors.get(i)); + } + } + } + // Clone neighbors: + for (int i = 0; i < nodes.size(); i++) { + UndirectedGraphNode newNode = map.get(nodes.get(i)); + for (int j = 0; j < nodes.get(i).neighbors.size(); j++) { + newNode.neighbors.add(map.get(nodes.get(i).neighbors.get(j))); + } + } + return map.get(node); + } +} + diff --git a/Others/old records/LintCode-Backup/Closest Number in Sorted Array.java b/Others/old records/LintCode-Backup/Closest Number in Sorted Array.java new file mode 100644 index 0000000..29ab136 --- /dev/null +++ b/Others/old records/LintCode-Backup/Closest Number in Sorted Array.java @@ -0,0 +1,65 @@ +Binary search. 考虑mid-1, mid+1. +一旦没有mid = target.index。 那么target最终就narrow down在(mid-1,mid) 或者(mid,mid+1) +``` +/* +Given a target number and an integer array A sorted in ascending order, find the index i in A such that A[i] is closest to the given target. + +Return -1 if there is no element in the array. + +Example +Given [1, 2, 3] and target = 2, return 1. + +Given [1, 4, 6] and target = 3, return 1. + +Given [1, 4, 6] and target = 5, return 1 or 2. + +Given [1, 3, 3, 4] and target = 2, return 0 or 1 or 2. + +Note +There can be duplicate elements in the array, and we can return any of the indices with same value. + +Challenge +O(logn) time complexity. + +Tags Expand +Binary Search +*/ +/* + Thoughts: + Do binary search. + Check the state of A[mid] < target < A[mid + 1] or A[mid - 1] < target < A[mid] + return the index that creates smallest diff. +*/ + +public class Solution { + /** + * @param A an integer array sorted in ascending order + * @param target an integer + * @return an integer + */ + public int closestNumber(int[] A, int target) { + if (A == null || A.length == 0) { + return -1; + } + int start = 0; + int end = A.length - 1; + int mid; + while (start + 1 < end) { + mid = start + (end - start) / 2; + if (A[mid] == target) { + return mid; + } else if (mid - 1 >= 0 && A[mid - 1] <= target && target < A[mid]) { + return (target - A[mid - 1]) < (A[mid] - target) ? (mid - 1) : mid; + } else if (mid + 1 < A.length && A[mid] < target && target <= A[mid + 1]) { + return (target - A[mid]) < (A[mid + 1] - target) ? mid : mid + 1; + } else if (A[mid] < target) { + start = mid; + } else { + end = mid; + } + } + return (target - A[start]) < (A[end] - target) ? start : end; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Coins in a Line.java b/Others/old records/LintCode-Backup/Coins in a Line.java new file mode 100644 index 0000000..d7adc09 --- /dev/null +++ b/Others/old records/LintCode-Backup/Coins in a Line.java @@ -0,0 +1,95 @@ +这题没见过的时候,要分析分析。分析出来了些个DP解决。 +However, 分析过后简直屌炸天。一个 n%3就解决了。纯粹math. +``` +/* +There are n coins in a line. Two players take turns to take one or two coins from right side until there are no more coins left. The player who take the last coin wins. + +Could you please decide the first play will win or lose? + +Example +n = 1, return true. + +n = 2, return true. + +n = 3, return false. + +n = 4, return true. + +n = 5, return true. + +Challenge +O(n) time and O(1) memory + +Tags Expand +Greedy Dynamic Programming Array Game Theory +*/ + +/* +Thoughts: +Clarify: '1st play will win' means: if play properly, +1st play will surely have a way to win that 2nd play can't beat. +Make dp[i]: the result when n = i. +fn: +Think back a step, state-1: +When it's play1's turn, there might be 1 or 2 coins left so he can win. so -1, or -2. +THink back a 2nd step, state-2: +Play2 take 1 or 2 to get into state-1. play2 may take 1 or 2 coins. so again, -1 or -2. + +So consider i-1, i-2, i-3, or i-4. Note, the next stemp would be for play2, then play1. +However, if left are 1,2 coins for play2, play2 wins; if left are 4 coins, play2 wins; only when left are 3 coins, play2 will surely lose, so play1 win. +Therefore, there is just 1 case to consider: if left are 3 coins, and it's play2's turn, then play1 surely wins. +so fn: +dp[i] = dp[i-3] + +Init: +dp[0] = false; dp[1], dp[2] = tru; dp[3] = false; + +Result: +dp[n] + +*/ +/* O(n) time and O(n)space.*/ +public class Solution { + /** + * @param n: an integer + * @return: a boolean which equals to true if the first player will win + */ + public boolean firstWillWin(int n) { + if (n <= 0) { + return false; + } + if (n <= 2) { + return true; + } + boolean[] dp = new boolean[n + 1]; + dp[1] = true; + dp[2] = true; + dp[3] = false; + for (int i = 4; i <= n; i++) { + dp[i] = dp[i - 3]; + } + return dp[n]; + } +} + +/* + O(1) time and O(1)space. + Base on the analysis in 1st attempt: + As long as it's not product of 3 + However, this is pretty ... math. Not quite useful to test coding ability. +*/ + +public class Solution { + /** + * @param n: an integer + * @return: a boolean which equals to true if the first player will win + */ + public boolean firstWillWin(int n) { + if (n <= 0) { + return false; + } + return n % 3 != 0; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Combination Sum II.java b/Others/old records/LintCode-Backup/Combination Sum II.java new file mode 100644 index 0000000..001b450 --- /dev/null +++ b/Others/old records/LintCode-Backup/Combination Sum II.java @@ -0,0 +1,76 @@ +确保Helper是用i+1,下一层的数字。 + +``` +/* +Given a collection of candidate numbers (C) and a target number (T), +find all unique combinations in C where the candidate numbers sums to T. + +Each number in C may only be used once in the combination. + +Note +All numbers (including target) will be positive integers. +Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak). +The solution set must not contain duplicate combinations. +Example +For example, given candidate set 10,1,6,7,2,1,5 and target 8, + +A solution set is: + +[1,7] + +[1,2,5] + +[2,6] + +[1,1,6] + +Tags Expand +Backtracking Array + +Thinking process: +Exact same idea as in Combination Sum I. The difference is, +cannot reuse the current index in nums. Instead, in helper() function, use index of i + 1 +*/ +public class Solution { + /** + * @param num: Given the candidate numbers + * @param target: Given the target number + * @return: All the combinations that sum to target + */ + public List> combinationSum2(int[] num, int target) { + List> rst = new ArrayList>(); + List list = new ArrayList(); + if (num == null || num.length == 0 || target < 0) { + return rst; + } + Arrays.sort(num); + helper(rst, list, num, target, 0, 0); + return rst; + } + public void helper(List> rst, List list, + int[] num, int target, int sum, int start) { + if (sum == target) { + rst.add(new ArrayList(list)); + return; + } else if (sum > target) {//Stop if greater than target + return; + } + int prev = -1;//Repeat detection + for (int i = start; i < num.length; i++) { + if (prev != -1 && prev == num[i]) { + continue; + } + list.add(num[i]); + sum += num[i]; + helper(rst, list, num, target, sum, i + 1); + //Back track: + sum -= num[i]; + list.remove(list.size() - 1); + //Repeat Detection + prev = num[i]; + } + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Combination Sum.java b/Others/old records/LintCode-Backup/Combination Sum.java new file mode 100644 index 0000000..0566a71 --- /dev/null +++ b/Others/old records/LintCode-Backup/Combination Sum.java @@ -0,0 +1,77 @@ +递归,backtracking. 非常normal。 +记得求sum时候也pass 一个sum进去,backtracking一下sum也,这样就不必每次都sum the list了。 +``` +/* +Given a set of candidate numbers (C) and a target number (T), find all unique combinations in C where the candidate numbers sums to T. + +The same repeated number may be chosen from C unlimited number of times. + + + +For example, given candidate set 2,3,6,7 and target 7, +A solution set is: +[7] +[2, 2, 3] + +Note +All numbers (including target) will be positive integers. +Elements in a combination (a1, a2, … , ak) must be in non-descending order. (ie, a1 ≤ a2 ≤ … ≤ ak). +The solution set must not contain duplicate combinations. +Example +given candidate set 2,3,6,7 and target 7, +A solution set is: +[7] +[2, 2, 3] + +Tags Expand +Backtracking Array + +Thinking process: +Similar to 'Combination' problem, do back-tracking with ability to repeat itself at index i. +In order to stop duplicates of result entry, use a 'prev' tracker to 'continue' if a value is repeating at any index. Skip repeating integers because we've already allow unlimited # of same integer in one single solution. (IMPORTANT: will have to sort the int[] in order to detect the duplicates) +In particular, I pass a 'sum' to compare with 'target' (want to have sum == target). Some solution prefer to use 'target - someVlaue' == 0 to find solution. +*/ + +public class Solution { + /** + * @param candidates: A list of integers + * @param target:An integer + * @return: A list of lists of integers + */ + public List> combinationSum(int[] num, int target) { + List> rst = new ArrayList>(); + List list = new ArrayList(); + if (num == null || num.length == 0 || target < 0) { + return rst; + } + Arrays.sort(num); + helper(rst, list, num, target, 0, 0); + return rst; + } + public void helper(List> rst, List list, + int[] num, int target, int sum, int start) { + if (sum == target) { + rst.add(new ArrayList(list)); + return; + } else if (sum > target) {//Stop if greater than target + return; + } + int prev = -1;//Repeat detection + for (int i = start; i < num.length; i++) { + if (prev != -1 && prev == num[i]) { + continue; + } + list.add(num[i]); + sum += num[i]; + helper(rst, list, num, target, sum, i); + //Back track: + sum -= num[i]; + list.remove(list.size() - 1); + //Repeat Detection + prev = num[i]; + } + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Combinations.java b/Others/old records/LintCode-Backup/Combinations.java new file mode 100644 index 0000000..2286e9b --- /dev/null +++ b/Others/old records/LintCode-Backup/Combinations.java @@ -0,0 +1,44 @@ +/* +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +Example +For example, +If n = 4 and k = 2, a solution is: +[[2,4],[3,4],[2,3],[1,2],[1,3],[1,4]] +Tags Expand +Backtracking Array + +Thinking process: +Use a helper method to perform recursive backtracking:add an element to next-level recursive call, and remote the entry after the recursive call. +Note: When 'new' something, cannot use 'List' because it's a abstract class. Need to new 'ArrayList' +*/ + +public class Solution { + /** + * @param n: Given the range of numbers + * @param k: Given the numbers of combinations + * @return: All the combinations of k numbers out of 1..n + */ + public List> combine(int n, int k) { + List> rst = new ArrayList>(); + if (n <= 0 || k <= 0) { + return rst; + } + List solution = new ArrayList(); + helper(rst, solution, n, k, 1);// Start == 1 because we want 1 ~ n in this problem + return rst; + } + public void helper(List> rst, + List solution, int n, int k, int start) { + if (solution.size() == k) { + rst.add(new ArrayList(solution)); + return; + } + for (int i = start; i <= n; i++) {// <=n because we want 1 ~ n in this problem + solution.add(i); + helper(rst, solution, n, k, i + 1); + solution.remove(solution.size() - 1); //Back-track + } + } +} + diff --git a/Others/old records/LintCode-Backup/Compare Strings.java b/Others/old records/LintCode-Backup/Compare Strings.java new file mode 100644 index 0000000..1cf9b3a --- /dev/null +++ b/Others/old records/LintCode-Backup/Compare Strings.java @@ -0,0 +1,44 @@ +/* +Compare two strings A and B, determine whether A contains all of the characters in B. + +The characters in string A and B are all Upper Case letters. + +Example +For A = "ABCD", B = "ABC", return true. + +For A = "ABCD" B = "AABC", return false. + +Tags Expand +Basic Implementation String LintCode Copyright + +Thinking process: +Count the number of occurance for StringA. +Count the number of occurance for StringB. +Check if all of StringB's char# <= StringA's char# at each index. +*/ + +public class Solution { + /** + * @param A : A string includes Upper Case letters + * @param B : A string includes Upper Case letter + * @return : if string A contains all of the characters in B return true else return false + */ + public boolean compareStrings(String A, String B) { + if (A == null || B == null || A.length() < B.length()) { + return false; + } + int[] countA = new int[26]; + int[] countB = new int[26]; + for (int i = 0; i < A.length(); i++) { + countA[A.charAt(i) - 'A']++; + } + for (int i = 0; i < B.length(); i++) { + countB[B.charAt(i) - 'A']++; + if (countB[B.charAt(i) - 'A'] > countA[B.charAt(i) - 'A']) { + return false; + } + } + return true; + } +} + diff --git a/Others/old records/LintCode-Backup/Complete Binary Tree.java b/Others/old records/LintCode-Backup/Complete Binary Tree.java new file mode 100644 index 0000000..03fbba4 --- /dev/null +++ b/Others/old records/LintCode-Backup/Complete Binary Tree.java @@ -0,0 +1,88 @@ +Use a flag . 当出现了第一次有 null children的node的时候, +说明complete tree的最低level出现了。 +自此以后,再不该有node再有child, queue后面出现的node应该左右孩子都是null. + +用BFS +``` +/* +Check a binary tree is completed or not. A complete binary tree is not binary tree that every level is completed filled except the deepest level. In the deepest level, all nodes must be as left as possible. See more definition + +Have you met this question in a real interview? Yes +Example + 1 + / \ + 2 3 + / +4 +is a complete binary. + + 1 + / \ + 2 3 + \ + 4 +is not a complete binary. + +Challenge +Do it in O(n) time + +Tags Expand +Binary Tree +*/ + +/* + Thoughts: + Do a BFS. + Once null occur, all the rest following it has to be null +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + +public class Solution { + /** + * @param root, the root of binary tree. + * @return true if it is a complete binary tree, or false. + */ + public boolean isComplete(TreeNode root) { + if (root == null) { + return true; + } + + Queue queue = new LinkedList(); + queue.offer(root); + boolean flag = false; + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + if (flag && (node.left != null || node.right != null)) { + return false; + } + if (node.left == null && node.right != null) { + return false; + } else if (node.left == null || node.right == null) { + flag = true; + } + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + + return true; + } + + +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Construct Binary Tree from Inorder and Postorder Traversal.java b/Others/old records/LintCode-Backup/Construct Binary Tree from Inorder and Postorder Traversal.java new file mode 100644 index 0000000..fcdb29a --- /dev/null +++ b/Others/old records/LintCode-Backup/Construct Binary Tree from Inorder and Postorder Traversal.java @@ -0,0 +1,87 @@ +/* +Given inorder and postorder traversal of a tree, construct the binary tree. + +Note +You may assume that duplicates do not exist in the tree. + +Example +Given inorder [1,2,3] and postorder [1,3,2] + +return a tree + + 2 + / \ +1 3 + + +Tags Expand +Binary Tree + +Thinking process: +Know that the last element of PostOrder array is the root of the Binary tree. +Find this root from the InOrder array, which will be the middle point. The front-part of the inorder array will be left-tree, the end-part of the inorder array will be the right-tree. +Trick part: +1. Need a helper function to perfrom divide/conquer. +2. Need to be careful when cutting the inorder and postorder array. + For inorder array, left array: (instart, middlePosition -1), right array: (middlePosition + 1, inend) + For postorder array: when cutting, we know the very last node is cut off already, so we just need to evenly split the rest array. + left array(postStart, postStart + (middlePosition - instart) - 1). + Note: (middlePositon - instart) means the length of the left-array/size of the left-tree + However, postStart + left-array-length exceed 1 over postorder-left-tree, hence minus 1 here. + right array(postStart + (middlePosition - instart), postend - 1) + Note: postStart + left-tree-length is exactly the starting point of the post-right-array. + Because the ending element is cut off previously to serve as root, we need to do (postend - 1) for correct postorder-right-tree. + +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + + +public class Solution { + /** + *@param inorder : A list of integers that inorder traversal of a tree + *@param postorder : A list of integers that postorder traversal of a tree + *@return : Root of a tree + */ + public TreeNode buildTree(int[] inorder, int[] postorder) { + if (inorder.length != postorder.length) { + return null; + } + return buildTreeHelper(inorder, 0, inorder.length - 1, + postorder, 0, postorder.length - 1); + } + + public TreeNode buildTreeHelper(int[] inorder, int inStart, int inEnd, + int[] postorder, int postStart, int postEnd){ + if (inStart > inEnd) { + return null; + } + TreeNode root = new TreeNode(postorder[postEnd]); + int mid = findMid(inorder, inStart, inEnd, postorder[postEnd]); + root.left = buildTreeHelper(inorder, inStart, mid - 1, + postorder, postStart, postStart + (mid - inStart) - 1); + root.right = buildTreeHelper(inorder, mid + 1, inEnd, + postorder, postStart + (mid - inStart), postEnd - 1); + return root; + } + + public int findMid(int[] arr, int start, int end, int key) { + for (int i = start; i <= end; i++) { + if (arr[i] == key) { + return i; + } + } + return -1; + } +} + diff --git a/Java/Construct Binary Tree from Inorder and Preorder Traversal.java b/Others/old records/LintCode-Backup/Construct Binary Tree from Inorder and Preorder Traversal.java similarity index 100% rename from Java/Construct Binary Tree from Inorder and Preorder Traversal.java rename to Others/old records/LintCode-Backup/Construct Binary Tree from Inorder and Preorder Traversal.java diff --git a/Others/old records/LintCode-Backup/Container With Most Water.java b/Others/old records/LintCode-Backup/Container With Most Water.java new file mode 100644 index 0000000..0b0f55f --- /dev/null +++ b/Others/old records/LintCode-Backup/Container With Most Water.java @@ -0,0 +1,51 @@ +类似木桶理论。盛水的最高取决于最低的那面墙。 +左右两墙,往中间跑动。 +另,若一面墙已经小于另外一面,就要移动,换掉矮墙(可能下一面更高,或更低);但决不能换掉当下的高墙,因为低墙已经limit的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) +``` +/* +Given n non-negative integers a1, a2, ..., an, where each represents a point at coordinate (i, ai). n vertical lines are drawn such that the two endpoints of line i is at (i, ai) and (i, 0). Find two lines, which together with x-axis forms a container, such that the container contains the most water. + +Example +Given [1,3,2], the max area of the container is 2. + +Note +You may not slant the container. + +Tags Expand +Two Pointers Array +*/ + +/* +Thoughts: +Start from 2 sides with 2 pointers, use those as 2 walls. + +Height of water is limited by the lower wall. For example, left wall < right wall, width = right.x - left.x + Now, if we move right wall: right--, then width = width-1, and the whole block is still limited by the lower left wall. So, this is not a good move. + Instead, when left wall < right wall, we move left++. + On the other hand, if lett wall > right wall, right--. +*/ +public class Solution { + /** + * @param heights: an array of integers + * @return: an integer + */ + public int maxArea(int[] heights) { + if (heights == null || heights.length == 0) { + return 0; + } + int left = 0; + int right = heights.length - 1; + int maxWater = Integer.MIN_VALUE; + while (left < right) { + maxWater = Math.max(maxWater, (right-left) * (heights[left] < heights[right] ? heights[left] : heights[right])); + if (heights[left] < heights[right]) { + left++; + } else { + right--; + } + } + return maxWater; + } +} + +``` \ No newline at end of file diff --git a/Java/Convert Binary Search Tree to Doubly Linked List.java b/Others/old records/LintCode-Backup/Convert Binary Search Tree to Doubly Linked List.java similarity index 100% rename from Java/Convert Binary Search Tree to Doubly Linked List.java rename to Others/old records/LintCode-Backup/Convert Binary Search Tree to Doubly Linked List.java diff --git a/Others/old records/LintCode-Backup/Convert Expression to Polish Notation.java b/Others/old records/LintCode-Backup/Convert Expression to Polish Notation.java new file mode 100644 index 0000000..7046567 --- /dev/null +++ b/Others/old records/LintCode-Backup/Convert Expression to Polish Notation.java @@ -0,0 +1,117 @@ +还是Expression Tree (Min-Tree). +根据题意,Tree出来以后,来个Pre-order-traversal. +``` +/* +Given an expression string array, return the Polish notation of this expression. (remove the parentheses) + +Have you met this question in a real interview? Yes +Example +For the expression [(5 − 6) * 7] (which represented by ["(", "5", "−", "6", ")", "*", "7"]), the corresponding polish notation is [* - 5 6 7] (which the return value should be ["*", "−", "5", "6", "7"]). + +Clarification +Definition of Polish Notation: + +http://en.wikipedia.org/wiki/Polish_notation +http://baike.baidu.com/view/7857952.htm +Tags Expand +LintCode Copyright Stack +*/ + +/* +Thoughts: +Build the expression tree, and do a pre-order-traversal, and record all nodes in the array list. + +Let's practice expression tree build again. +*/ + +public class Solution { + class TreeNode { + String s; + int val; + TreeNode left; + TreeNode right; + public TreeNode(int val, String s) { + this.val = val; + this.s = s; + this.left = null; + this.right = null; + } + } + + public TreeNode build(String[] expression) { + if (expression == null || expression.length == 0) { + return null; + } + Stack stack = new Stack(); + int base = 0; + int val = 0; + + for (int i = 0; i < expression.length; i++) { + if (expression[i].equals("(")) { + base += 10; + continue; + } + if (expression[i].equals(")")) { + base -= 10; + continue; + } + val = getWeight(base, expression[i]); + TreeNode node = new TreeNode(val, expression[i]); + while (!stack.isEmpty() && node.val <= stack.peek().val) { + node.left = stack.pop(); + } + if (!stack.isEmpty()) { + stack.peek().right = node; + } + stack.push(node); + } + if (stack.isEmpty()) { + return null; + } + TreeNode rst = stack.pop(); + while (!stack.isEmpty()) { + rst = stack.pop(); + } + return rst; + } + + public int getWeight(int base, String s) { + if (s.equals("+") || s.equals("-")) { + return base + 1; + } + if (s.equals("*") || s.equals("/")) { + return base + 2; + } + return Integer.MAX_VALUE; + } + + /** + * @param expression: A string array + * @return: The Polish notation of this expression + */ + public ArrayList convertToPN(String[] expression) { + ArrayList rst = new ArrayList(); + if (expression == null || expression.length == 0) { + return rst; + } + TreeNode root = build(expression); + preTraversal(rst, root); + + return rst; + } + + public void preTraversal(ArrayList rst, TreeNode node){ + if (node == null) { + return; + } + if (node.left == null && node.right == null) { + rst.add(node.s); + return; + } + rst.add(node.s); + preTraversal(rst, node.left); + preTraversal(rst, node.right); + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Convert Expression to Reverse Polish Notation.java b/Others/old records/LintCode-Backup/Convert Expression to Reverse Polish Notation.java new file mode 100644 index 0000000..6b0df9f --- /dev/null +++ b/Others/old records/LintCode-Backup/Convert Expression to Reverse Polish Notation.java @@ -0,0 +1,117 @@ +用build expression tree开头。 +这个里面把TreeNode就当做成我们需要的node,里面扩展成有left/right child的node. +这题,目的是建造tree,然后来个post-traversal就行了。 +``` +/* +Given an expression string array, return the Reverse Polish notation of this expression. (remove the parentheses) + +Example +For the expression [3 - 4 + 5] (which denote by ["3", "-", "4", "+", "5"]), return [3 4 - 5 +] (which denote by ["3", "4", "-", "5", "+"]) + +Tags: +LintCode Copyright Stack +*/ + +/* +Thought: +Reverse Polish notation: +Put operator after operands. + +First build the tree, then do post-traversal. +*/ + + +public class Solution { + /***** build expression tree******/ + class TreeNode { + int val; + String s; + TreeNode left; + TreeNode right; + public TreeNode(int val, String s) { + this.val = val; + this.s = s; + this.left = null; + this.right = null; + } + } + public TreeNode build(String[] expression) { + Stack stack = new Stack(); + int base = 0; + int val = 0; + + for (int i = 0; i < expression.length; i++) { + if (expression[i].equals("(")) { + base += 10; + continue; + } + if (expression[i].equals(")")) { + base -= 10; + continue; + } + val = getWeight(base, expression[i]); + + TreeNode node = new TreeNode(val, expression[i]); + while (!stack.isEmpty() && node.val <= stack.peek().val) { + node.left = stack.pop(); + } + if (!stack.isEmpty()) { + stack.peek().right = node; + } + stack.push(node); + + } + if (stack.isEmpty()) { + return null; + } + TreeNode rst = stack.pop(); + while (!stack.isEmpty()) { + rst = stack.pop(); + } + return rst; + } + //Calculate weight for characters + public int getWeight(int base, String s) { + if (s.equals("+") || s.equals("-")) { + return base + 1; + } + if (s.equals("*") || s.equals("/")) { + return base + 2; + } + return Integer.MAX_VALUE; + } + /***** build expression tree******/ + + + /** + * @param expression: A string array + * @return: The Reverse Polish notation of this expression + */ + public ArrayList convertToRPN(String[] expression) { + ArrayList rst = new ArrayList(); + if (expression == null || expression.length == 0) { + return rst; + } + TreeNode root = build(expression); + if (root == null) { + return rst; + } + postTraversal(rst, root); + return rst; + } + + public void postTraversal(ArrayList rst, TreeNode node){ + if (node == null) { + return; + } + if (node.left != null) { + postTraversal(rst, node.left); + } + if (node.right != null) { + postTraversal(rst, node.right); + } + rst.add(node.s); + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Convert Integer A to Integer B.java b/Others/old records/LintCode-Backup/Convert Integer A to Integer B.java new file mode 100644 index 0000000..97b3974 --- /dev/null +++ b/Others/old records/LintCode-Backup/Convert Integer A to Integer B.java @@ -0,0 +1,34 @@ +/* +Determine the number of bits required to convert integer A to integer B + +Example +Given n = 31, m = 14,return 2 + +(31)10=(11111)2 + +(14)10=(01110)2 + +Tags Expand +Cracking The Coding Interview Bit Manipulation Binary Representation + +Thinking process: +Assume the integer is 32 bit. +XOR a and b, shift by 1 bit everytime -> want to check the XORed value at index 0 : just & 1 will do. +Count the above calculated result: how many bit difference do a and b have. +*/ + +class Solution { + /** + *@param a, b: Two integer + *return: An integer + */ + public static int bitSwapRequired(int a, int b) { + int count = 0; + for (int i = 0; i < 32; i++) { + count += (a ^ b) >> i & 1; + } + return count; + } +}; + + diff --git a/Java/Convert Sorted Array to Binary Search Tree With Minimal Height.java b/Others/old records/LintCode-Backup/Convert Sorted Array to Binary Search Tree With Minimal Height.java similarity index 100% rename from Java/Convert Sorted Array to Binary Search Tree With Minimal Height.java rename to Others/old records/LintCode-Backup/Convert Sorted Array to Binary Search Tree With Minimal Height.java diff --git a/Others/old records/LintCode-Backup/Convert Sorted List to Binary Search Tree.java b/Others/old records/LintCode-Backup/Convert Sorted List to Binary Search Tree.java new file mode 100644 index 0000000..3295e98 --- /dev/null +++ b/Others/old records/LintCode-Backup/Convert Sorted List to Binary Search Tree.java @@ -0,0 +1,84 @@ +Divide and Conquer + +找到mid。 +然后把root = mid.next + +然后开始sortedListToBST(mid.next.next); //后半段 +mid.next = null;//非常重要,要把后面拍过序的断掉 +sortedListToBST(head); //从头开始的前半段 + + +最后root.left, root.right merge一下。 + +``` +/* +Given a singly linked list where elements are sorted in ascending order, convert it to a height balanced BST. + +Example +Tags Expand +Recursion Linked List + +Thinking Process: +Find the middle point of the list. +Left of the mid will be left-tree, right of the mid node will be right-tree. + +*/ + +/** + * Definition for ListNode. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int val) { + * this.val = val; + * this.next = null; + * } + * } + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + * @param head: The first node of linked list. + * @return: a tree node + */ + public TreeNode sortedListToBST(ListNode head) { + if (head == null) { + return null; + } else if (head.next == null) { + return new TreeNode(head.val); + } + + ListNode mid = findMiddle(head); + TreeNode root = new TreeNode(mid.next.val); + TreeNode right = sortedListToBST(mid.next.next); + mid.next = null; + TreeNode left = sortedListToBST(head); + + root.left = left; + root.right = right; + return root; + } + + + public ListNode findMiddle(ListNode head) { + ListNode slow = head; + ListNode fast = head.next; + while (fast.next != null && fast.next.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } +} + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Copy List with Random Pointer.java b/Others/old records/LintCode-Backup/Copy List with Random Pointer.java new file mode 100644 index 0000000..481895f --- /dev/null +++ b/Others/old records/LintCode-Backup/Copy List with Random Pointer.java @@ -0,0 +1,120 @@ +/* +31% Accepted +A linked list is given such that each node contains an additional random pointer +which could point to any node in the list or null. + +Return a deep copy of the list. + +Example +Tags Expand +Hash Table Linked List + +*/ + +/** + * Definition for singly-linked list with a random pointer. + * class RandomListNode { + * int label; + * RandomListNode next, random; + * RandomListNode(int x) { this.label = x; } + * }; + */ + +/* + Recap: 12.10.2015 + Iterative through the list. + Use a dummyHead and return dummyHead.next at the end. + In each iteration, check if Head is already exist, or make a new one + * use HashMap to mark if a node has been visited. + deep copy the random node of head as well. + + border case: if head == null, return null +*/ + + +public class Solution { + public RandomListNode copyRandomList(RandomListNode head) { + if (head == null) { + return null; + } + //creat node, used to link all nodes + RandomListNode dummy = new RandomListNode(0); + RandomListNode node = dummy; + RandomListNode newNode; + + //HashMap to mark node + HashMap map = new HashMap(); + + while(head != null) { + //process head. (we already know head!=null) + if (!map.containsKey(head)) { + map.put(head, new RandomListNode(head.label)); + } + newNode = map.get(head); + node.next = newNode; + //process head.random + if (head.random != null) { + if(!map.containsKey(head.random)) { + map.put(head.random, new RandomListNode(head.random.label)); + } + newNode = map.get(head.random); + node.next.random = newNode; + } + node = node.next; + head = head.next; + } + return dummy.next; + } +} + +/* +Thinking process: +1. Loop through the original list +2. Use a HashMap. User the old node as a key and new node as value. +3. Doesn't matter of the order of node that being added into the hashMap. + For example, node1 is added. + node1.random, which is node 99, will be added into hashMap right after node1. +4. During the loop: + If head exist in hashmap, get it; if not existed, create new node using head, add into hashMap + If head.random exist, get it; if not, add a new node using head.random. + +*/ +public class Solution { + /** + * @param head: The head of linked list with a random pointer. + * @return: A new head of a deep copy of the list. + */ + public RandomListNode copyRandomList(RandomListNode head) { + if (head == null) { + return null; + } + HashMap map = new HashMap(); + RandomListNode dummy = new RandomListNode(0); + RandomListNode pre = dummy; + RandomListNode newNode; + while (head != null) { + //Add new node + if (map.containsKey(head)) { + newNode = map.get(head); + } else { + newNode = new RandomListNode(head.label); + map.put(head, newNode); + } + //Add new node's random node + if (head.random != null) { + if (map.containsKey(head.random)) { + newNode.random = map.get(head.random); + } else { + newNode.random = new RandomListNode(head.random.label); + map.put(head.random, newNode.random); + } + } + //append and shift + pre.next = newNode; + pre = newNode; + head = head.next; + } + return dummy.next; + } +} + diff --git a/Others/old records/LintCode-Backup/Cosine Similarity.java b/Others/old records/LintCode-Backup/Cosine Similarity.java new file mode 100644 index 0000000..c71919e --- /dev/null +++ b/Others/old records/LintCode-Backup/Cosine Similarity.java @@ -0,0 +1,60 @@ +按题目意思,写出来就好了。 +``` +/* +Cosine similarity is a measure of similarity between two vectors of an inner product space that measures the cosine of the angle between them. +The cosine of 0° is 1, and it is less than 1 for any other angle. + +See wiki: Cosine Similarity + +Here is the formula: +http://www.lintcode.com/en/problem/cosine-similarity/# + +Given two vectors A and B with the same size, calculate the cosine similarity. + +Return 2.0000 if cosine similarity is invalid (for example A = [0] and B = [0]). + +Example +Given A = [1, 2, 3], B = [2, 3 ,4]. + +Return 0.9926. + +Given A = [0], B = [0]. + +Return 2.0000 + +*/ + +/* + Thoughts + Based on the given equation. Write up calculation + Check border. +*/ + +class Solution { + /** + * @param A: An integer array. + * @param B: An integer array. + * @return: Cosine similarity. + */ + public double cosineSimilarity(int[] A, int[] B) { + if (A == null || B == null || A.length == 0 || B.length == 0 || A.length != B.length) { + return 2; + } + + double sumProduct = 0; + double sumASq = 0; + double sumBSq = 0; + for (int i = 0; i < A.length; i++) { + sumProduct += A[i]*B[i]; + sumASq += A[i] * A[i]; + sumBSq += B[i] * B[i]; + } + if (sumASq == 0 && sumBSq == 0) { + return 2.0; + } + return sumProduct / (Math.sqrt(sumASq) * Math.sqrt(sumBSq)); + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Count 1 in Binary.java b/Others/old records/LintCode-Backup/Count 1 in Binary.java new file mode 100644 index 0000000..1059210 --- /dev/null +++ b/Others/old records/LintCode-Backup/Count 1 in Binary.java @@ -0,0 +1,43 @@ +/* +Count how many 1 in binary representation of a 32-bit integer. + +Example +Given 32, return 1 + +Given 5, return 2 + +Given 1023, return 9 + +Challenge +If the integer is n bits with m 1 bits. Can you do it in O(m) time? + +Tags Expand +Binary Bit Manipulation + +Thoughts: +1. break string into char[] +2. convert char[] into integer using Character.getNumericValue() + +*/ + + + + +public class Solution { + /** + * @param num: an integer + * @return: an integer, the number of ones in num + */ + public int countOnes(int num) { + if (num < 0) { + return 0; + } + String bits = Integer.toBinaryString(num); + char[] bitArray = bits.toCharArray(); + int sum = 0; + for (int i = 0; i < bitArray.length; i++) { + sum += Character.getNumericValue(bitArray[i]); + } + return sum; + } +}; diff --git a/Others/old records/LintCode-Backup/Count and Say.java b/Others/old records/LintCode-Backup/Count and Say.java new file mode 100644 index 0000000..2d0fc5b --- /dev/null +++ b/Others/old records/LintCode-Backup/Count and Say.java @@ -0,0 +1,62 @@ +/* +The count-and-say sequence is the sequence of integers beginning as follows: + +1, 11, 21, 1211, 111221, ... + +1 is read off as "one 1" or 11. + +11 is read off as "two 1s" or 21. + +21 is read off as "one 2, then one 1" or 1211. + +Given an integer n, generate the nth sequence. + +Example +Given n = 5, return "111221". + +Note +The sequence of integers will be represented as a string. + +Tags Expand +String + + +1. Set up initial value '11' +2. use while loop to build on past variable +3. In each while loop case, break the string into charArray, count and name mark the type +4. In for loop: when different, append string (count+type); when same, count++. +*/ + + +public class Solution { + /** + * @param n the nth + * @return the nth sequence + */ + public String countAndSay(int n) { + if (n <= 1) { + return n + ""; + } + String str = "11"; + int ind = 2; + while (ind < n) { + StringBuffer sb = new StringBuffer(); + char[] arr = str.toCharArray(); + int count = 1; + int type = Character.getNumericValue(arr[0]); + for (int i = 1; i < arr.length; i++) { + if (arr[i] == arr[i - 1]) { + count++; + } else { + sb.append(count + "" + type); + type = Character.getNumericValue(arr[i]); + count = 1; + } + } + ind++; + sb.append(count + "" + type); + str = sb.toString(); + } + return str; + } +} diff --git a/Others/old records/LintCode-Backup/Count of Smaller Number before itself.java b/Others/old records/LintCode-Backup/Count of Smaller Number before itself.java new file mode 100644 index 0000000..1d0b9a8 --- /dev/null +++ b/Others/old records/LintCode-Backup/Count of Smaller Number before itself.java @@ -0,0 +1,120 @@ +与Count of Smaller Number非常类似。 +Trick: 先Query,再modify. +每次Query时候,A[i]都还没有加入到Segment Tree 里面,而A[i+1...etc]自然也还没有加进去。 +那么就自然是coutning smaller number before itself. +刁钻啊! + +另外注意: +在modify里面:多Check了root.start <= index 和 index <= root.end。 过去都忽略了。以后可以把这个也写上。 +(其实是Make sense的,就是更加严格地check了index再 root.left 或者 root.right里面的站位) +``` +/* +Give you an integer array (index from 0 to n-1, where n is the size of this array, value from 0 to 10000) . For each element Ai in the array, count the number of element before this element Ai is smaller than it and return count number array. + +Example +For array [1,2,7,8,5], return [0,1,2,3,2] + +Note +We suggest you finish problem Segment Tree Build, Segment Tree Query II and Count of Smaller Number before itself I first. + +Tags Expand +LintCode Copyright Binary Tree Segment Tree +*/ + +/* + Thoughts: + Just like Count of Smaller Number (in all given array A): + Create segment tree on index (0 ~ 10000) + Modify it to store count of equal or smaller numbers comparing to itself. + However, do query on every A[i] before calling 'modify'!!!!This is the trick. + Every time, before adding a new count information into the tree, do a query and return result. This way, it's always checking numbers before itself. +*/ +public class Solution { + + public class SegmentTreeNode { + public int start,end; + public int count; + public SegmentTreeNode left, right; + public SegmentTreeNode(int start, int end) { + this.start = start; + this.end = end; + this.count = 0; + this.left = null; + this.right = null; + } + } + + /* Build a empty segment tree based on index*/ + public SegmentTreeNode build(int start, int end) { + if (start > end) { + return null; + } + if (start == end) { + return new SegmentTreeNode(start, end); + } + SegmentTreeNode root = new SegmentTreeNode(start, end); + int mid = start + (end - start) / 2; + root.left = build(start, mid); + root.right = build(mid + 1, end); + return root; + } + + /* Update the tree with 'count': from bottom to this specific tree node, how many integers do we have.*/ + public void modify(SegmentTreeNode root, int index, int count){ + if (root.start == index && root.end == index) { + root.count += count; + return; + } + int mid = root.start + (root.end - root.start)/2; + if (root.start <= index && index <= mid) { + modify(root.left, index, count); + } + if (mid < index && index <= root.end) { + modify(root.right, index, count); + } + root.count = root.left.count + root.right.count; + } + + /* Look for that number based on start&&end*/ + public int query(SegmentTreeNode root, int start, int end) { + if (root.start == start && root.end == end) { + return root.count; + } + int sum = 0; + int mid = root.start + (root.end - root.start)/2; + if (end <= mid) { + sum += query(root.left, start, end); + } else if (start > mid) { + sum += query(root.right, start, end); + } else if (start <= mid && mid < end) { + sum += query(root.left, start, mid); + sum += query(root.right, mid + 1, end); + } + return sum; + } + + + /** + * @param A: An integer array + * @return: Count the number of element before this element 'ai' is + * smaller than it and return count number array + */ + public ArrayList countOfSmallerNumberII(int[] A) { + ArrayList rst = new ArrayList(); + + SegmentTreeNode root = build(0, 10000); + + for (int i = 0; i < A.length; i++) { + int count = 0; + if (A[i] > 0) { + count = query(root, 0, A[i] - 1); + } + rst.add(count); + modify(root, A[i], 1); + } + return rst; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Count of Smaller Number.java b/Others/old records/LintCode-Backup/Count of Smaller Number.java new file mode 100644 index 0000000..c8b4083 --- /dev/null +++ b/Others/old records/LintCode-Backup/Count of Smaller Number.java @@ -0,0 +1,216 @@ +和平时的segment tree问题不同。 +这个给了实际的value,而还是造一个based on index的segment tree才行。 +Thought1是失败的,因为虽然省了空间,但是search time还是O(n). +Thought2才是真正的segment tree (based on index interval). + +重要trick: +在query前,给进去的start和end是: 0 ~ value-1. +value-1就是说,找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. +这个trick还挺刁钻的。 +``` +/* +Give you an integer array (index from 0 to n-1, where n is the size of this array, value from 0 to 10000) and an query list. For each query, give you an integer, return the number of element in the array that are smaller than the given integer. + + +Example +For array [1,2,7,8,5], and queries [1,8,5], return [0,4,2] + +Note +We suggest you finish problem Segment Tree Build and Segment Tree Query II first. + +Challenge +Could you use three ways to do it. + +Just loop +Sort and binary search +Build Segment Tree and Search. +Tags Expand +Binary Search LintCode Copyright Segment Tree + +*/ +/* + Thought2: http://www.jiuzhang.com/solutions/count-of-smaller-number/ + Build a tree based on index 0 ~ 10000. Then use modify to update the tree with proper 'count' value + Use query method to search for final results. + Each A[i] will be stored at index value of A[i]. + Count: how many numbers do we have from bottom till this level, including the A[i] itself. + For example, at the lowest A[i] spot, SegmentTreeNode(i,i), the count == 1. + + Note:Again, be careful on calculating the mid. It's usually based on root.start and root.end, instead of the target start,end interval. +*/ + +public class Solution { + + public class SegmentTreeNode { + public int start,end; + public int count; + public SegmentTreeNode left, right; + public SegmentTreeNode(int start, int end) { + this.start = start; + this.end = end; + this.count = 0; + this.left = null; + this.right = null; + } + } + + /* Build a empty segment tree based on index*/ + public SegmentTreeNode build(int start, int end) { + if (start > end) { + return null; + } + if (start == end) { + return new SegmentTreeNode(start, end); + } + SegmentTreeNode root = new SegmentTreeNode(start, end); + int mid = start + (end - start) / 2; + root.left = build(start, mid); + root.right = build(mid + 1, end); + return root; + } + + /* Update the tree with 'count': from bottom to this specific tree node, how many integers do we have.*/ + public void modify(SegmentTreeNode root, int index, int count){ + if (root.start == index && root.end == index) { + root.count += count; + return; + } + int mid = root.start + (root.end - root.start)/2; + if (index <= mid) { + modify(root.left, index, count); + } + if (index > mid) { + modify(root.right, index, count); + } + root.count = root.left.count + root.right.count; + } + + /* Look for that number based on start&&end*/ + public int query(SegmentTreeNode root, int start, int end) { + if (root.start == start && root.end == end) { + return root.count; + } + int sum = 0; + int mid = root.start + (root.end - root.start)/2; + if (end <= mid) { + sum += query(root.left, start, end); + } else if (start > mid) { + sum += query(root.right, start, end); + } else if (start <= mid && mid < end) { + sum += query(root.left, start, mid); + sum += query(root.right, mid + 1, end); + } + return sum; + } + + + /** + * @param A: An integer array + * @return: The number of element in the array that + * are smaller that the given integer + */ + public ArrayList countOfSmallerNumber(int[] A, int[] queries) { + ArrayList rst = new ArrayList(); + + SegmentTreeNode root = build(0, 10000); + for (int value : A) { + modify(root, value, 1); + } + for (int value : queries) { + int count = 0; + if (value > 0) { + count = query(root, 0, value - 1); + } + rst.add(count); + } + return rst; + } +} + + + + + + +/* + Time limit exceeded... + Because: If we build the tree based on given index 0~n, and build 'query' method based on its max values. It will work for small scale, but when it gets larger, we could be doing O(n)*m all the time. Everytime we search root.left and root.right, which is not binary search style : ) + + Thoughts: + Build SegmentTree, store max + 1st attempt: time exceeds. Because the 'query part' is actually not segment tree search. Doing search based on max value can find answer, but it's O(n) search. + Note: + This segment tree problem gives queries of actaul value, rather than a range(start,end) that we can directly use in segment tree. So we need some sort of conversion, that still provide (start,end) to search +*/ + +public class Solution { + public class SegmentTreeNode { + int start, end, max; + SegmentTreeNode left,right; + public SegmentTreeNode(int start, int end, int max){ + this.start = start; + this.end = end; + this.max = max; + this.left = null; + this.right = null; + } + } + + public SegmentTreeNode build(int[] A, int start, int end){ + if (A == null || A.length == 0) { + return null; + } + if (start == end) { + return new SegmentTreeNode(start, end, A[start]); + } + + int mid = (start + end)/2; + SegmentTreeNode left = build(A, start, mid); + SegmentTreeNode right = build(A, mid + 1, end); + SegmentTreeNode node = new SegmentTreeNode(start, end, Math.max(left.max, right.max)); + node.left = left; + node.right = right; + + return node; + } + + public int query(SegmentTreeNode root, int val) { + if (root == null) { + return 0; + } + if (root.start == root.end && root.max >= val) {//leaf, stll >= val + return 0; + } + if (root.max < val) { + return root.end - root.start + 1; + } + //root.max > val, but unknown if root.children could < val + return query(root.left, val) + query(root.right, val); + } + + + /** + * @param A: An integer array + * @return: The number of element in the array that + * are smaller that the given integer + */ + public ArrayList countOfSmallerNumber(int[] A, int[] queries) { + ArrayList rst = new ArrayList(); + if (queries == null || queries.length == 0) { + return rst; + } + + SegmentTreeNode root = build(A, 0, A.length - 1); + for (int num : queries) { + int count = 0; + if (root != null) { + count = query(root, num); + } + rst.add(count); + } + return rst; + } +} + + +``` \ No newline at end of file diff --git a/Java/Data Stream Median.java b/Others/old records/LintCode-Backup/Data Stream Median.java similarity index 100% rename from Java/Data Stream Median.java rename to Others/old records/LintCode-Backup/Data Stream Median.java diff --git a/Others/old records/LintCode-Backup/Delete Digits.java b/Others/old records/LintCode-Backup/Delete Digits.java new file mode 100644 index 0000000..a9eaa58 --- /dev/null +++ b/Others/old records/LintCode-Backup/Delete Digits.java @@ -0,0 +1,114 @@ +/* +Given string A representative a positive integer which has N digits, remove any k digits of the number, the remaining digits are arranged according to the original order to become a new positive integer. + +Find the smallest integer after remove k digits. + +N <= 240 and k <= N, + +Example +Given an integer A = "178542", k = 4 + +return a string "12" + +Tags Expand +Greedy LintCode Copyright + +Attempt2,Thoughts: +loop k times: each interation, find one digit to remove +Rules: want to remove whatever digit at A[i] that's A[i] > A[i+1]. +Reason: Higher position (left side of the string) is always stronger/high number, and remove the strong/high digit will always be right option. +Well... thinking straight (attempt2) seems much easier to understand and to code up than my attempt1 + +Note: +remember to remove the prefixing 0's +*/ + +public class Solution { + /** + *@param A: A positive integer which has N digits, A is a string. + *@param k: Remove k digits. + *@return: A string + */ + public String DeleteDigits(String A, int k) { + if (A == null || A.length() == 0 || k == 0) { + return A; + } + for (int i = 0; i < k; i++) { + for (int j = 0; j < A.length(); j++) { + if (j == A.length() - 1) { + A = A.substring(0, j); + break; + } else if (A.charAt(j) > A.charAt(j + 1)) { + A = A.substring(0, j) + A.substring(j + 1); + break; + } + } + } + //remote prefixing-0's + int i = 0; + while(i < A.length() && A.charAt(i) == '0') { + i++; + } + return A.substring(i); + } +} + + + + +/* +Attempt1: Lintcode 83% correct, but Does not work for : [9876141516171818818181890001988181700198181778786761256512651653145345143, 55] +my output: 1111111134143 +expect: 1111111345143 + +Not sure where went wrong. + +Thoughts: +This seems to be: Pick (N - k) digits and make a smallest number, without changing the order of digits. +Create an array with length == (N - k): digits +Starting from i = 0, digits[0] = A.charAt[0] - '0' +if A[i] < digits[i] , replace digits[i] with A[i] + Note: here loop through (N - k) and see if the A[i] can be put anywhere + +Note: handle prefix '0' in string +*/ +public class Solution { + public static String DeleteDigits(String A, int k) { + if (A == null || A.length() == 0 || k < 0) { + return A; + } + int n = A.length() - k; + //System.out.println(A.length() + " " + n); + int[] digits = new int[n]; + for (int j = 0; j < n; j++) { + digits[j] = -1; + } + int[] backup = Arrays.copyOf(digits, digits.length); + for (int i = 0; i < A.length(); i++) { + int digit = (int)(A.charAt(i) - '0'); + + for (int j = 0; j < n; j++) { + if ((digit < digits[j] || digits[j] < 0) && (A.length() - i) >= (n - j)) { + //System.out.println(j + " " + digit + " | " + (A.length() - i) + " " + (n - j)); + if (j == 0) { + digits = Arrays.copyOf(backup, backup.length); + } + digits[j] = digit; + break; + } + } + } + //System.out.println(Arrays.toString(digits)); + String rst = ""; + for (int j = 0; j < n; j++) { + if (rst.length() == 0 && digits[j] == 0) { + continue; + } else { + rst += digits[j]; + } + } + + return rst; + } + +} diff --git a/Java/Delete Node in the Middle of Singly Linked List.java b/Others/old records/LintCode-Backup/Delete Node in the Middle of Singly Linked List.java similarity index 100% rename from Java/Delete Node in the Middle of Singly Linked List.java rename to Others/old records/LintCode-Backup/Delete Node in the Middle of Singly Linked List.java diff --git a/Others/old records/LintCode-Backup/Distinct Subsequences.java b/Others/old records/LintCode-Backup/Distinct Subsequences.java new file mode 100644 index 0000000..35cf479 --- /dev/null +++ b/Others/old records/LintCode-Backup/Distinct Subsequences.java @@ -0,0 +1,110 @@ +/* +Given a string S and a string T, count the number of distinct subsequences of T in S. + +A subsequence of a string is a new string which is formed from the original string by deleting some (can be none) of the characters without disturbing the relative positions of the remaining characters. (ie, "ACE" is a subsequence of "ABCDE" while "AEC" is not). + +Example +Given S = "rabbbit", T = "rabbit", return 3. + +Challenge +Do it in O(n2) time and O(n) memory. + +O(n2) memory is also acceptable if you do not know how to optimize memory. + +Tags Expand +String Dynamic Programming +*/ +/* +Attempt2: +Use DP. Okay, first I had no idea how to start, but here is a reference: http://blog.csdn.net/abcbc/article/details/8978146 +First of all, Map out the number of existance of T in S in a 2D map: + 0 1 2 3 4 5 6 7 + --------------- + r a b b b i t +0| 1 1 1 1 1 1 1 1 +1| r 0 1 1 1 1 1 1 1 +2| a 0 0 1 1 1 1 1 1 +3| b 0 0 0 1 2 3 3 3 +4| b 0 0 0 0 1 3 3 3 +5| i 0 0 0 0 0 0 3 3 +6| t 0 0 0 0 0 0 0 3 + +Use DP[T][S]. We realize: +1.DP[0][0] == 1; //Both null can be a match +2.DP[0][1 ~ S.length - 1] = 1;//First fow, when T=="", whatever S will have 1 subsequence: "" +3.DP[1 ~ T.length][0] = 0;// First column, when S=="", whatever T will not be subsequence of S == "" +4.When looking at each row and filling out the pixels, we realize when T exist in S[a~b], it will surely exist in S[a~b+1], taht is: + Step1: DP[i][j] is at least equal to DP[i][j - 1];//DP[i][j] is always based on DP[i][j-1], so DP[i][j] = DP[i][j+1] + something + Step2: So, what's that 'something' in step1? For example, look at T[3] == 'b' against S[0 ~ 3]: + S[0 ~ 3] has 1 'b' at S[3], and also, T[0~3] == S[0~3], that's a perfect match. SO DP[3][3] = 1 + S[0 ~ 4] has 2 'b' at S[3] and S[4]. Now imagine we pick either S[3] or S[4] to genreate T[0~3] out of S[0~4]: we have 2 possibilities.D[3][4] = 2 + Consider: D[i][j] means we picked S[j]; in our S[0 ~ 4] case, that means we picked S[4] but skipped S[3], though S[3] still counts towards another situation where we skipped S[4]. + After all, we will count whatever that we skipped into our current DP[i][j], that is DP[i][j] += T[i - 1] == S[j - 1] ? DP[i - 1][j - 1] : 0; + Conclusion: while we for-looping through each row, if we find out S[j] and S[j - 1] both equals to T[i - 1], we want to make sure we count D[i - 1][j -1]'s previous records in! + +Note: +In double for loop, set i,j <= xxxx.length(), since we've increased the 2D array by 1 block on row and col. +*/ + + +public class Solution { + /** + * @param S, T: Two string. + * @return: Count the number of distinct subsequences + */ + public int numDistinct(String S, String T) { + int[][] DP = new int[T.length() + 1][S.length() + 1]; + DP[0][0] = 1; + for(int i = 1; i < S.length(); i++) { + DP[0][i] = 1; + } + for (int i = 1; i < T.length(); i++) { + DP[i][0] = 0; + } + for (int i = 1; i <= T.length(); i++) { + for (int j = 1; j <= S.length(); j++){ + DP[i][j] = DP[i][j - 1]; + if (T.charAt(i - 1) == S.charAt(j - 1)) { + DP[i][j] += DP[i - 1][j - 1]; + } + } + } + return DP[T.length()][S.length()]; + } +} + + +/* +Attemp1: +recursive on substring of S, accumulate total count +However, exceed time limit +*/ +public class Solution { + public int numDistinct(String S, String T) { + if (S.length() == 0) { + return T.length() == 0 ? 1 : 0; + } + if (T.length() == 0) { + return 1; + } + int count = 0; + for (int i = 0; i < S.length(); i++) { + if (S.charAt(i) == T.charAt(0)) { + count += numDistinct(S.substring(i + 1), T.substring(1)); + } + } + return count; + } +} + + + +/* +First Thought: +find the # of ways to get T from S, while having to follow the rules of 'subsequence' +How about: find what chars are missing in T based on S, then find the number of ways to insert the missing chars to make it back to S? +The missing chars: misChars = new ArrayList(); +However, time cost on this: +For example I have n missing chars from S.length == m. so I have (m + 1) places where i can insert the n chars. Then it's a mCn problem. This goes up to m!, too much. Not applicapable. + +*/ \ No newline at end of file diff --git a/Java/Easy Reverse Linked List.java b/Others/old records/LintCode-Backup/Easy Reverse Linked List.java similarity index 100% rename from Java/Easy Reverse Linked List.java rename to Others/old records/LintCode-Backup/Easy Reverse Linked List.java diff --git a/Others/old records/LintCode-Backup/Edit Distance.java b/Others/old records/LintCode-Backup/Edit Distance.java new file mode 100644 index 0000000..916b78f --- /dev/null +++ b/Others/old records/LintCode-Backup/Edit Distance.java @@ -0,0 +1,58 @@ +/* +Given two words word1 and word2, find the minimum number of steps required to convert word1 to word2. (each operation is counted as 1 step.) + +You have the following 3 operations permitted on a word: + +Insert a character +Delete a character +Replace a character + +Example +Given word1 = "mart" and word2 = "karma", return 3. + +Tags Expand +String Dynamic Programming + +Thoughts: +Draw a 2D array, consider rows as word1 and cols as word2. +DP[i][j] means the steps (edit distance) to take to transfer word1[0 ~ i] to word2[0 ~ j] +And, we have 3 different calculations for the 3 methods: +1. Replace: DP[i][j] = word1[i-1] == word2[j-1] ? DP[i - 1][j - 1] : DP[i-1][j-1] + 1; +2. Insert: DP[i][j] = word1[i - 1][j] + 1; // missing 1 char in word1 +3. Delete: DP[i][j] = word1[i][j - 1] + 1; // extra char in word1 + +Note: just remember to start from i=1,j=1, because we are using DP[i-1][j-1], becareful with border case +*/ + + + +public class Solution { + /** + * @param word1 & word2: Two string. + * @return: The minimum number of steps. + */ + public int minDistance(String word1, String word2) { + if (word1 == null && word2 != null) { + return word2.length(); + } else if (word1 != null && word2 == null) { + return word1.length(); + } else if (word1 == null && word2 == null) { + return 0; + } + int[][] DP = new int[word1.length() + 1][word2.length() + 1]; + for (int i = 1; i <= word1.length(); i++) { + DP[i][0] = i; + } + for (int j = 1; j <= word2.length(); j++) { + DP[0][j] = j; + } + + for (int i = 1; i <= word1.length(); i++) { + for (int j = 1; j <= word2.length(); j++) { + DP[i][j] = Math.min(Math.min(DP[i - 1][j] + 1, DP[i][j - 1] + 1), word1.charAt(i - 1) == word2.charAt(j - 1) ? DP[i - 1][j - 1] : DP[i - 1][j - 1] + 1); + } + } + + return DP[word1.length()][word2.length()]; + } +} diff --git a/Others/old records/LintCode-Backup/Expression Evaluation.java b/Others/old records/LintCode-Backup/Expression Evaluation.java new file mode 100644 index 0000000..47df6b5 --- /dev/null +++ b/Others/old records/LintCode-Backup/Expression Evaluation.java @@ -0,0 +1,146 @@ +Build Expression Tree的另外一个变形。 +做的还是PostTraversal。先eval left, right, 然后eval符号。 + +注意Handle数字时,若左右Child全Null,那必定是我们weight最大的数字node了。 +若有个child是null,那就return另外一个node。 +还要注意: +过程中用个Long吧,最后结局在cast back to int. +``` +/* +Given an expression string array, return the final result of this expression + +Example +For the expression 2*6-(23+7)/(1+2), input is + +[ + "2", "*", "6", "-", "(", + "23", "+", "7", ")", "/", + (", "1", "+", "2", ")" +], +return 2 + +Note +The expression contains only integer, +, -, *, /, (, ). + +Tags Expand +LintCode Copyright Stack +*/ + +/* +Thoughts: +Build expression tree, then traverse it in post-traversal order. +Tricky point: Whenever that's a operator, do operation. return final result. +*/ + + +public class Solution { + /***** build expression tree******/ + class TreeNode { + int val; + String s; + TreeNode left; + TreeNode right; + public TreeNode(int val, String s) { + this.val = val; + this.s = s; + this.left = null; + this.right = null; + } + } + public TreeNode build(String[] expression) { + Stack stack = new Stack(); + int base = 0; + int val = 0; + + for (int i = 0; i < expression.length; i++) { + if (expression[i].equals("(")) { + base += 10; + continue; + } + if (expression[i].equals(")")) { + base -= 10; + continue; + } + val = getWeight(base, expression[i]); + + TreeNode node = new TreeNode(val, expression[i]); + while (!stack.isEmpty() && node.val <= stack.peek().val) { + node.left = stack.pop(); + } + if (!stack.isEmpty()) { + stack.peek().right = node; + } + stack.push(node); + + } + if (stack.isEmpty()) { + return null; + } + TreeNode rst = stack.pop(); + while (!stack.isEmpty()) { + rst = stack.pop(); + } + return rst; + } + //Calculate weight for characters + public int getWeight(int base, String s) { + if (s.equals("+") || s.equals("-")) { + return base + 1; + } + if (s.equals("*") || s.equals("/")) { + return base + 2; + } + return Integer.MAX_VALUE; + } + /***** build expression tree******/ + + /** + * @param expression: an array of strings; + * @return: an integer + */ + public int evaluateExpression(String[] expression) { + if (expression == null || expression.length == 0) { + return 0; + } + TreeNode root = build(expression); + if (root == null) { + return 0; + } + long rst = postTraversalEval(root); + return (int)rst; + } + + public long postTraversalEval(TreeNode node) { + if (node == null) { + return 0; + } + if (node.left == null && node.right == null) { + return Long.parseLong(node.s); + } + long left = postTraversalEval(node.left); + long right = postTraversalEval(node.right); + + if (node.left == null || node.right == null) { + return node.left == null ? right : left; + } + long rst = 0; + switch (node.s) { + case "*": + rst = left * right; + break; + case "/": + rst = left / right; + break; + case "+": + rst = left + right; + break; + case "-": + rst= left - right; + break; + } + return rst; + } +}; + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Expression Tree Build.java b/Others/old records/LintCode-Backup/Expression Tree Build.java new file mode 100644 index 0000000..795d3be --- /dev/null +++ b/Others/old records/LintCode-Backup/Expression Tree Build.java @@ -0,0 +1,69 @@ +和Max-tree一样,感谢http://blog.welkinlan.com/2015/06/29/max-tree-lintcode-java/ +这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙。 +注意虚拟的形态:treeNode,作用就是为了有个weight,好排序。 +要想想,Java这个strict mom,如果换做JavaScript, 直接在expressionTreeNode上面附加一个object就完了,哪还用什么另外一个TreeNode class. +O(n) +``` +public class Solution { + class TreeNode { + int val; + ExpressionTreeNode eNode; + public TreeNode(int val, String s) { + this.val = val; + eNode = new ExpressionTreeNode(s); + } + } + /** + * @param expression: A string array + * @return: The root of expression tree + */ + public ExpressionTreeNode build(String[] expression) { + if (expression == null || expression.length == 0) { + return null; + } + Stack stack = new Stack(); + int base = 0; + int val = 0; + + for (int i = 0; i < expression.length; i++) { + if (expression[i].equals("(")) { + base += 10; + continue; + } + if (expression[i].equals(")")) { + base -= 10; + continue; + } + val = getWeight(base, expression[i]); + TreeNode node = new TreeNode(val, expression[i]); + while (!stack.isEmpty() && node.val <= stack.peek().val) { + node.eNode.left = stack.pop().eNode; + } + if (!stack.isEmpty()) { + stack.peek().eNode.right = node.eNode; + } + stack.push(node); + } + if (stack.isEmpty()) { + return null; + } + TreeNode rst = stack.pop(); + while (!stack.isEmpty()) { + rst = stack.pop(); + } + return rst.eNode; + } + //Calculate weight for characters + public int getWeight(int base, String s) { + if (s.equals("+") || s.equals("-")) { + return base + 1; + } + if (s.equals("*") || s.equals("/")) { + return base + 2; + } + return Integer.MAX_VALUE; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Fast Power.java b/Others/old records/LintCode-Backup/Fast Power.java new file mode 100644 index 0000000..dca7b79 --- /dev/null +++ b/Others/old records/LintCode-Backup/Fast Power.java @@ -0,0 +1,45 @@ +/* +Calculate the a^n % b where a, b and n are all 32bit integers. + +Example +For 2^31 % 3 = 2 + +For 100^1000 % 1000 = 0 + +Challenge +O(logn) + +Tags Expand +Divide and Conquer + +Thoughts: +Learn online: +(a * b) % p = (a % p * b % p) % p +Than mean: a ^ n can be divided into a^(n/2) * a^(n/2), that can be used for recursion: divde and conqure. + +Note: when n is odd number, it cannot be evenly divided into n/2 and n/2. This case needs special treatment: n = n/2 + n/2 + 1; +*/ + +class Solution { + /* + * @param a, b, n: 32bit integers + * @return: An integer + */ + public int fastPower(int a, int b, int n) { + if (n == 0) { + return 1 % b; + } + if (n == 1) { + return a % b; + } + + long recurPow = fastPower(a, b, n / 2); + recurPow = (recurPow * recurPow) % b; + + if (n % 2 == 1) { + recurPow = recurPow * a % b; + } + + return (int)recurPow; + } +}; diff --git a/Others/old records/LintCode-Backup/Fibonacci.java b/Others/old records/LintCode-Backup/Fibonacci.java new file mode 100644 index 0000000..adf73eb --- /dev/null +++ b/Others/old records/LintCode-Backup/Fibonacci.java @@ -0,0 +1,49 @@ +/* +Find the Nth number in Fibonacci sequence. + +A Fibonacci sequence is defined as follow: + +The first two numbers are 0 and 1. +The i th number is the sum of i-1 th number and i-2 th number. +The first ten numbers in Fibonacci sequence is: + +0, 1, 1, 2, 3, 5, 8, 13, 21, 34 ... + + +Example +Given 1, return 0 + +Given 2, return 1 + +Given 10, return 34 + +Note +The Nth fibonacci number won't exceed the max value of signed 32-bit integer in the test cases. + +Tags Expand +Enumeration Mathematics Non Recursion + +Thoughts: +1. If non-recursion, do for loop for that n +2. Note: this specfiic problem is not 0-based. it's 1-based. +3. return fib[n] +*/ + +class Solution { + /** + * @param n: an integer + * @return an integer f(n) + */ + public int fibonacci(int n) { + if (n <= 1) { + return 0; + } + int[] fib = new int[n + 1]; + fib[1] = 0; + fib[2] = 1; + for (int i = 3; i <= n; i++) { + fib[i] = fib[i - 1] + fib[i - 2]; + } + return fib[n]; + } +} diff --git a/Others/old records/LintCode-Backup/Find Minimum in Rotated Sorted Array II.java b/Others/old records/LintCode-Backup/Find Minimum in Rotated Sorted Array II.java new file mode 100644 index 0000000..83aa7c8 --- /dev/null +++ b/Others/old records/LintCode-Backup/Find Minimum in Rotated Sorted Array II.java @@ -0,0 +1,41 @@ +/* +Medium Find Minimum in Rotated Sorted Array II My Submissions + +40% Accepted +Suppose a sorted array is rotated at some pivot unknown to you beforehand. + +(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). + +Find the minimum element. + +The array may contain duplicates. + +Example +Given [4,4,5,6,7,0,1,2] return 0 + +Tags Expand +Binary Search Divide and Conqueri + +Thinking process: +It seems using binary search will leads to O(n), so just use a for loop with O(n) +*/ + +public class Solution { + /** + * @param num: a rotated sorted array + * @return: the minimum number in the array + */ + public int findMin(int[] num) { + if (num == null || num.length == 0) { + return -1; + } + int min = Integer.MAX_VALUE; + for (int i = 0; i < num.length; i++) { + if (min > num[i]) { + min = num[i]; + } + } + return min; + } +} + diff --git a/Others/old records/LintCode-Backup/Find Minimum in Rotated Sorted Array.java b/Others/old records/LintCode-Backup/Find Minimum in Rotated Sorted Array.java new file mode 100644 index 0000000..a15ba64 --- /dev/null +++ b/Others/old records/LintCode-Backup/Find Minimum in Rotated Sorted Array.java @@ -0,0 +1,52 @@ +/* +Suppose a sorted array is rotated at some pivot unknown to you beforehand. + +(i.e., 0 1 2 4 5 6 7 might become 4 5 6 7 0 1 2). + +Find the minimum element. + +You may assume no duplicate exists in the array. + +Example +Given [4,5,6,7,0,1,2] return 0 + +Tags Expand +Binary Search + +Thinking process: +Understand how to use binary in this problem: compare the mid point with end point. +In this problem, because the sorted line is cut at one point then rotate, so one of the line is absolutely greater than the other line. +Situation 1: +if mid < end : that means minimum is on the end point's line. Move end to left. end = mid. +Situation 2: +if mid > end: that means there must be a mountain-jump somewhere after mid and before end, which is the minimum point. Now move start to mid. +*/ + +public class Solution { + /** + * @param num: a rotated sorted array + * @return: the minimum number in the array + */ + public int findMin(int[] num) { + if (num == null || num.length == 0) { + return -1; + } + int start = 0; + int end = num.length - 1; + int mid = 0; + while (start + 1 < end) { + mid = start + (end - start) / 2; + if (num[mid] > num[end]) { + start = mid; + } else { + end = mid; + } + } + if (num[start] < num[end]) { + return num[start]; + } else { + return num[end]; + } + } +} + diff --git a/Others/old records/LintCode-Backup/Find Peak Element II.java b/Others/old records/LintCode-Backup/Find Peak Element II.java new file mode 100644 index 0000000..1629c93 --- /dev/null +++ b/Others/old records/LintCode-Backup/Find Peak Element II.java @@ -0,0 +1,52 @@ +/* +There is an integer matrix which has the following features: + +The numbers in adjacent positions are different. +The matrix has n rows and m columns. +For all i < m, A[0][i] < A[1][i] && A[n - 2][i] > A[n - 1][i]. +For all j < n, A[j][0] < A[j][1] && A[j][m - 2] > A[j][m - 1]. +We define a position P is a peek if: + +A[j][i] > A[j+1][i] && A[j][i] > A[j-1][i] && A[j][i] > A[j][i+1] && A[j][i] > A[j][i-1] +Find a peak element in this matrix. Return the index of the peak. + +Have you met this question in a real interview? Yes +Example +Given a matrix: + +[ + [1 ,2 ,3 ,6 ,5], + [16,41,23,22,6], + [15,17,24,21,7], + [14,18,19,20,10], + [13,14,11,10,9] +] +return index of 41 (which is [1,1]) or index of 24 (which is [2,2]) + +Note +The matrix may contains multiple peeks, find any of them. + +Challenge +Solve it in O(n+m) time. + +If you come up with an algorithm that you thought it is O(n log m) or O(m log n), can you prove it is actually O(n+m) or propose a similar but O(n+m) algorithm? + +Tags Expand +Binary Search LintCode Copyright Matrix +*/ + +/* + NOT DONE. Will try if have time +*/ + + +class Solution { + /** + * @param A: An integer matrix + * @return: The index of the peak + */ + public List findPeakII(int[][] A) { + // write your code here + } +} + diff --git a/Others/old records/LintCode-Backup/Find Peak Element.java b/Others/old records/LintCode-Backup/Find Peak Element.java new file mode 100644 index 0000000..0717014 --- /dev/null +++ b/Others/old records/LintCode-Backup/Find Peak Element.java @@ -0,0 +1,65 @@ +还是binary search. +一个特别的check condition, 和特别的move left, move right的case罢了。 +``` +/*There is an integer array which has the following features: + + * The numbers in adjacent positions are different. + + * A[0] < A[1] && A[A.length - 2] > A[A.length - 1]. + +We define a position P is a peek if A[P] > A[P-1] && A[P] > A[P+1]. + +Find a peak in this array. Return the index of the peak. + +Note +The array may contains multiple peeks, find any of them. + +Example +[1, 2, 1, 3, 4, 5, 7, 6] + +return index 1 (which is number 2) or 6 (which is number 7) + +Challenge +Time complexity O(logN) + +Tags Expand +Binary Search Array LintCode Copyright + +Thinking Process: +画图 +*/ + +class Solution { + /** + * @param A: An integers array. + * @return: return any of peek positions. + */ + public int findPeak(int[] A) { + int start = 1; + int end = A.length - 2; + int mid; + + while (start + 1 < end) { + mid = start + (end - start) / 2; + if (A[mid] > A[mid - 1] && A[mid] > A[mid + 1]) { + return mid; + //Tricky: only when start< mid < mid + 1, we can set start = mid; + //This is because we are cilmbing, so going up will finally find a peak + } else if (A[mid] > A[start] && A[mid] < A[mid + 1]) { + start = mid; + } else {// this case A[start] > A[mid], so we climb backwards, all make sense + end = mid; + } + }//while + + if (A[start] > A[start - 1] && A[start] > A[start + 1]) { + return start; + } else { + return end; + } + + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Find the Connected Component in the Undirected Graph.java b/Others/old records/LintCode-Backup/Find the Connected Component in the Undirected Graph.java new file mode 100644 index 0000000..694d25c --- /dev/null +++ b/Others/old records/LintCode-Backup/Find the Connected Component in the Undirected Graph.java @@ -0,0 +1,176 @@ +BFS遍历,把每个node的neighbor都加进来。 + +一定注意要把visit过的node Mark一下。因为curr node也会是别人的neighbor,会无限循环。 + +Component的定义:所有Component内的node必须被串联起来via path (反正这里是undirected, 只要链接上就好) + +这道题:其实component在input里面都已经给好了,所有能一口气visit到的,全部加进queue里面,他们就是一个component里面的了。 + +而我们这里不需要判断他们是不是Component。 +``` + /* +Find the number connected component in the undirected graph. +Each node in the graph contains a label and a list of its neighbors. +(a connected component (or just component) of an undirected graph is a subgraph in which + any two vertices are connected to each other by paths, +and which is connected to no additional vertices in the supergraph.) + +Example +Given graph: + +A------B C + \ | | + \ | | + \ | | + \ | | + D E +Return {A,B,D}, {C,E}. Since there are two connected component which is {A,B,D}, {C,E} + +Note +Tags Expand +Breadth First Search + + */ + +/** + * Definition for Undirected graph. + * class UndirectedGraphNode { + * int label; + * ArrayList neighbors; + * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList(); } + * }; + */ + +/* +OPTS: 11.07.2015 +Try to use ae map to mark the nodes. Then do a BFS with queue +1. Mark each node in map. +2. BFS each node +3. Whenver one node is checked, mark it check +*/ + +public class Solution { + /** + * @param nodes a array of Undirected graph node + * @return a connected set of a Undirected graph + */ + public List> connectedSet(ArrayList nodes) { + List> rst = new ArrayList>(); + if (nodes == null || nodes.size() == 0) { + return rst; + } + + HashMap map = new HashMap(); + for (UndirectedGraphNode node : nodes) { + map.put(node.label, false); + } + + for (UndirectedGraphNode node : nodes) { + if (!map.get(node.label)) { + bfs(rst, node, map); + } + } + return rst; + } + + public void bfs(List> rst, UndirectedGraphNode node, HashMap map) { + Queue queue = new LinkedList(); + List list = new ArrayList(); + queue.add(node); + map.put(node.label, true); + UndirectedGraphNode temp; + while (!queue.isEmpty()) { + temp = queue.poll(); + list.add(temp.label); + for (UndirectedGraphNode neighbor : temp.neighbors) { + if (!map.get(neighbor.label)) { + queue.offer(neighbor); + map.put(neighbor.label, true); + } + } + } + Collections.sort(list); + rst.add(list); + } +} + + +/* +Thoughts: +How do we check for a connected graph (any two nodes are connected)? +Maybe check for each node: each node represents a lead to a subgraph, then check if this subgraph +is valid. + + +1. In real case, need to ask the intervier: can we assume the given nodes are valid, so that we only +need to check for success case? That means, we assume for example a linear list A-B-C does not exist. + +2. Then, we can use a 'set' to mark: we've checked this node. +3. Use a queue for BFS +4. Use a arraylist to save the results. +5. Key point: when the queue is empty(), that means one set of connected component is ready to go +6. Iterate through nodes, when it's not empty. + +More Notes:Have to do Collections.sort()....somehow it want me to sort the results? +Note2: Get rid of a node from nodes, whenever add it to component ... don't forget this. +Note3: Well, there is a chance that compoents are added, queue is cleaned, but nodes are empty as well.. +that means, need to catch the last case of 'remaining component' and add it to rst. + + +Review: +How list, ArrayList, Set, Queue work. +How to do: add, remove, sort + +Collections: Set, List, Queue + +List: ArrayList + +Set methods: add(), contains(?) +Queue methods: offer(E e), add(E e), poll() +ArrayList method: add(E e), isEmpty(), remove(object o) + +*/ + +public class Solution { + /** + * @param nodes a array of Undirected graph node + * @return a connected set of a Undirected graph + */ + public List> connectedSet(ArrayList nodes) { + List> rst = new ArrayList<>(); + if (nodes == null || nodes.size() == 0) { + return rst; + } + //Init: + Set checked = new HashSet(); + Queue queue = new LinkedList(); + ArrayList component = new ArrayList(); + + queue.offer(nodes.get(0)); + + while (!nodes.isEmpty()) { + if (queue.isEmpty()) { + Collections.sort(component); + rst.add(component); + queue.offer(nodes.get(0)); + component = new ArrayList(); + } else { + UndirectedGraphNode curr = queue.poll(); + if (!checked.contains(curr)) { + checked.add(curr); + component.add(curr.label); + nodes.remove(curr); + for (UndirectedGraphNode node : curr.neighbors) { + queue.add(node); + } + } + } + } + if (!component.isEmpty()) { + rst.add(component); + } + return rst; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Find the Weak Connected Component in the Directed Graph.java b/Others/old records/LintCode-Backup/Find the Weak Connected Component in the Directed Graph.java new file mode 100644 index 0000000..34f0384 --- /dev/null +++ b/Others/old records/LintCode-Backup/Find the Weak Connected Component in the Directed Graph.java @@ -0,0 +1,182 @@ +Identify这是个union-find问题还挺巧妙。 +看到了weak component的形式: 一个点指向所有,那么所有的点都有一个公共的parent,然后就是要找出这些点。 + +为何不能从一个点出发,比如A,直接print它所有的neighbors呢? + 不行,如果轮到了B点,那因为是directed,它也不知道A的情况,也不知道改如何继续加,或者下手。 + +所以,要把所有跟A有关系的点,或者接下去和A的neighbor有关系的点,都放进union-find里面,让这些点有Common parents. + +最后output的想法: +做一个 map 。 +之前我们不是给每个num都存好了parent了嘛。 +每个num都有个parent, 然后不同的parent就创造一个不同的list。 +最后,把Map里面所有的list拿出来就好了。 +``` +/* +Find the number Weak Connected Component in the directed graph. +Each node in the graph contains a label and a list of its neighbors. +(a connected set of a directed graph is a subgraph in which + any two vertices are connected by direct edge path.) + +Example +Given graph: + +A----->B C + \ | | + \ | | + \ | | + \ v v + ->D E <- F +Return {A,B,D}, {C,E,F}. Since there are two connected component which are {A,B,D} and {C,E,F} + +Note +Sort the element in the set in increasing order + +Tags Expand +Union Find +*/ + +/* +Thoughts: +When constructing the dataset before running the method, I guess DirectedGraphNode is contructed in a +way that one node shots to neighbors, but may not have neibors shooting back. +Then, there is a parent-child relationship, where we can use union-find + +[ idea is correct. Need to implement with proper union-find methods. + (Implementation here: http://www.jiuzhang.com/solutions/find-the-weak-connected-component-in-the-directed-graph/) + 1. for loop to construct: Map + 2. Create Map>. + 3. Find(node) return root, and add this node to the rootNode's list +] + +In NineChapter's definition: +I. UnionFind class takes HashSet, and makes maps of relatioinship. + 1. However, in UnionFind constructor, first step is just init + 2. Find method on a target element's root parent. If itself is root parent, then parent should = map.get(parent) + 3. Union method, if find(x) and find(y) are different, map them as child vs. parent. + +II. In main method: + 1. Create that HashSet for UnionFind. + 2. Use Find methods to tacke all parent vs neighbors + 3. Use union to map out the relationship between parent's root and each neighbor's root. + OKAY, so now the map should be done, saved within UnionFind. + +III. Generate results + For each element in HashSet, find their root, and add to that root list + +Note: +Be careful about the in generateRst method: looking for the root +*/ + +/** + * Definition for Directed graph. + * class DirectedGraphNode { + * int label; + * ArrayList neighbors; + * DirectedGraphNode(int x) { label = x; neighbors = new ArrayList(); } + * }; + */ +public class Solution { + class UnionFind { + HashMap map; + //Constructor: + UnionFind(HashSet set) { + map = new HashMap(); + for (int num : set) { + map.put(num, num); + } + } + //Find: + //Root parent should have itself as child in map + int find(int x) { + int parent = map.get(x); + while (parent != map.get(parent)) { + parent = map.get(parent); + } + return parent; + } + void union(int x, int y) { + int findX = find(x); + int findY = find(y); + if (findX != findY) { + map.put(findX, findY); + } + } + } + public List> generateRst (List> rst, UnionFind uf, HashSet set) { + + HashMap> listMap = new HashMap>(); + for (int num : set) { + int rootParent = uf.find(num);//uf.map.get(num); + if (!listMap.containsKey(rootParent)) { + listMap.put(rootParent, new ArrayList()); + } + //Add num into its correct set (meaning its root ancestor) + listMap.get(rootParent).add(num); + } + + for (List list: listMap.values()) { + Collections.sort(list); + rst.add(list); + } + return rst; + } + + public List> connectedSet2(ArrayList nodes) { + List> rst = new ArrayList>(); + if (nodes == null || nodes.size() == 0) { + return rst; + } + HashSet set = new HashSet(); + for (DirectedGraphNode node : nodes) { + set.add(node.label); + for (DirectedGraphNode neighbor : node.neighbors) { + set.add(neighbor.label); + } + } + UnionFind uf = new UnionFind(set); + + //find and union: construct the map structure + for (DirectedGraphNode node : nodes) { + for (DirectedGraphNode neighbor : node.neighbors) { + uf.union(node.label, neighbor.label); + } + } + return generateRst(rst, uf, set); + } + + +} + + + + + + + + + + + +/* + Can we do the following for find() ? Inspired by the union-find implemented with int[] + Sort of recurisvely trying to get the parent orign, instead of using a while loop? + I guess it's doable. +*/ +//Root parent should have itself as child in map +int find(int x) { + int parent = map.get(x); + if (parent != map.get(parent)) { + parent = map.get(parent); + map.put(x, parent); + } + return parent; +} + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/First Bad Version.java b/Others/old records/LintCode-Backup/First Bad Version.java new file mode 100644 index 0000000..3a13b36 --- /dev/null +++ b/Others/old records/LintCode-Backup/First Bad Version.java @@ -0,0 +1,113 @@ +根据isBadVersion的性质,判断还如何end=mid or start=mid. +isBadVersion 是有方向的嘛,一个点错了,后面全错。 +``` +/* +The code base version is an integer start from 1 to n. +One day, someone committed a bad version in the code case, +so it caused this version and the following versions are all failed in the unit tests. +Find the first bad version. + +You can call isBadVersion to help you determine which version is the first bad one. +The details interface can be found in the code's annotation part. + +Have you met this question in a real interview? Yes +Example +Given n = 5: + +isBadVersion(3) -> false +isBadVersion(5) -> true +isBadVersion(4) -> true +Here we are 100% sure that the 4th version is the first bad version. + +Note +Please read the annotation in code area to get the correct way to call isBadVersion in different language. +For example, Java is SVNRepo.isBadVersion(v) + +Challenge +You should call isBadVersion as few as possible. + +Tags Expand +Binary Search LintCode Copyright Facebook + +*/ + +/* + Recap: 12.07.2015. + Feels like to find the 1st occurance of the match. going left all the way. +*/ +/** + * public class SVNRepo { + * public static boolean isBadVersion(int k); + * } + * you can use SVNRepo.isBadVersion(k) to judge whether + * the kth code version is bad or not. +*/ +class Solution { + /** + * @param n: An integers. + * @return: An integer which is the first bad version. + */ + public int findFirstBadVersion(int n) { + if (n < 1) { + return 0; + } + int start = 1; + int end = n; + int mid; + while (start + 1 < end) { + mid = start + (end - start)/2; + if (SVNRepo.isBadVersion(mid)) { + if (mid - 1 >= 1 && SVNRepo.isBadVersion(mid - 1)) { + end = mid; + } else { + return mid; + } + } else { + start = mid; + } + } + if (SVNRepo.isBadVersion(start)) { + return start; + } + return end; + } +} + + + + + + + +class Solution { + /** + * @param n: An integers. + * @return: An integer which is the first bad version. + */ + public int findFirstBadVersion(int n) { + int start = 1; + int end = n; + int mid; + + while (start + 1 < end) { + mid = start + (end - start) / 2; + if (VersionControl.isBadVersion(mid)) { + end = mid; + } else { + start = mid; + } + }//end while + + if (VersionControl.isBadVersion(start)) { + return start; + } else if (VersionControl.isBadVersion(end)) { + return end; + } else { + return 0; + } + + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/First Missing Positive.java b/Others/old records/LintCode-Backup/First Missing Positive.java new file mode 100644 index 0000000..b2dc9c2 --- /dev/null +++ b/Others/old records/LintCode-Backup/First Missing Positive.java @@ -0,0 +1,59 @@ +/* +Given an unsorted integer array, find the first missing positive integer. + +Example +Given [1,2,0] return 3, and [3,4,-1,1] return 2. + +Challenge +Your algorithm should run in O(n) time and uses constant space. + +Tags Expand +Array + +Thoughts: +It means: after it's sorted, what's the first missing postive int counted from 1 ---> more + +1. Arrays.sort(); +2. count = first non-zero element in A. +3. count +1, and see if maches the current A[i]? + +NOTE: +Deal with negative and positive number separately +Watch out for redundant number: ask if the list has duplicated elements +*/ + + +public class Solution { + /** + * @param A: an array of integers + * @return: an integer + */ + public int firstMissingPositive(int[] A) { + if (A == null || A.length == 0) { + return 1; + } + Arrays.sort(A); + int count = -1; + for (int i = 0; i < A.length; i++) { + if (A[i] > 0) { + if (count < 0) {//process 1st positive element + count = A[i]; + if (count != 1) { + return 1; + } + } + else if (A[i] == A[i - 1]) {//watch out for duplicates + count--; + } + else if(A[i] != count) {//if not match, kick out + return count; + } + count++; + } + } + if (count < 0) {//if all negative, return 1 + return 1; + } + return count; + } +} \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Flatten Binary Tree to Linked List.java b/Others/old records/LintCode-Backup/Flatten Binary Tree to Linked List.java new file mode 100644 index 0000000..6ff61e1 --- /dev/null +++ b/Others/old records/LintCode-Backup/Flatten Binary Tree to Linked List.java @@ -0,0 +1,82 @@ +/* +Flatten Binary Tree to Linked List + +Flatten a binary tree to a fake "linked list" in pre-order traversal. + +Here we use the right pointer in TreeNode as the next pointer in ListNode. + +Example + 1 + \ + 1 2 + / \ \ + 2 5 => 3 + / \ \ \ + 3 4 6 4 + \ + 5 + \ + 6 +Note +Don't forget to mark the left child of each node to null. +Or you will get Time Limit Exceeded or Memory Limit Exceeded. + +Challenge +Do it in-place without any extra memory. + +Tags Expand +Binary Tree Depth First Search +*/ + + + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + * @param root: a TreeNode, the root of the binary tree + * @return: nothing + */ + public TreeNode parentNode = null; + public void flatten(TreeNode root) { + if (root == null) { + return; + } + + if (parentNode != null) { + parentNode.left = null; + parentNode.right = root; + } + + parentNode = root; + TreeNode right = root.right; + flatten(root.left); + flatten(right); + } +} + + + + + + + + + + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Gas Station.java b/Others/old records/LintCode-Backup/Gas Station.java new file mode 100644 index 0000000..90cc375 --- /dev/null +++ b/Others/old records/LintCode-Backup/Gas Station.java @@ -0,0 +1,56 @@ +/* +There are N gas stations along a circular route, where the amount of gas at station i is gas[i]. + +You have a car with an unlimited gas tank and it costs cost[i] of gas to travel from station i to its next station (i+1). You begin the journey with an empty tank at one of the gas stations. + +Return the starting gas station's index if you can travel around the circuit once, otherwise return -1. + +Example +Given 4 gas stations with gas[i]=[1,1,3,1], and the cost[i]=[2,2,1,1]. The starting gas station's index is 2. + +Note +The solution is guaranteed to be unique. + +Challenge +O(n) time and O(1) extra space + +Tags Expand +Greedy + +Thoughts: +Loop through the gas station, and track the possible starting index. +Start from i = 0 ~ gas.length, and use a second pointer move to track how far we are travelling + calculate: remain += gas[i] - cost[i]. (remain + gas[i] - cost[i]: the remaining gas plus i's gas, can we make it to i+1 gas station?) + if remain < 0, fail. Note: if from i ~ j can't work, even it's possible that i can make it to i+1's station, but i+1 ~ j won't work still. + Thus, once i's station failed to get to x, set index = x + 1: we are moving on to next possible starting point. + +'total':simply indicates if we can make it a circle +*/ + +public class Solution { + /** + * @param gas: an array of integers + * @param cost: an array of integers + * @return: an integer + */ + public int canCompleteCircuit(int[] gas, int[] cost) { + if (gas == null || cost == null || gas.length == 0 || cost.length == 0) { + return -1; + } + int start = 0; + int remain = 0; + int total = 0; + for (int i = 0; i < gas.length; i++) { + remain += gas[i] - cost[i]; + if (remain < 0) { + remain = 0; + start = i + 1; + } + total += gas[i] - cost[i]; + } + if (total < 0) { + return -1; + } + return start; + } +} diff --git a/Others/old records/LintCode-Backup/Generate Parentheses.java b/Others/old records/LintCode-Backup/Generate Parentheses.java new file mode 100644 index 0000000..e14d824 --- /dev/null +++ b/Others/old records/LintCode-Backup/Generate Parentheses.java @@ -0,0 +1,134 @@ +递归。 +看thought.取或者不取(, ) +``` +/* +Generate Parentheses + +Given n pairs of parentheses, +write a function to generate all combinations of well-formed parentheses. + +Example +Given n = 3, a solution set is: + +"((()))", "(()())", "(())()", "()(())", "()()()" + +Tags Expand +String Backtracking Recursion Zenefits Google + +*/ + +/* + Thoughts: + //http://fisherlei.blogspot.com/2012/12/leetcode-generate-parentheses.html + Either put ( or ) + can only put ( when # of ( < n + can only put ) when # of ) < # of ( + If # of single '(' > 0, then we can put ')' + If n > 0, we can split: 1. close it with ')'; or 2. add '(' + when n-- becomese = 0 and #p = 0, rst.add + +*/ +public class Solution { + /** + * @param n n pairs + * @return All combinations of well-formed parentheses + */ + ArrayList rst = new ArrayList(); + public ArrayList generateParenthesis(int n) { + if (n <= 0) { + return rst; + } + ArrayList list = new ArrayList(); + helper(list, 0, 0, n); + return rst; + } + + public void helper(ArrayList list, int left, int right, int n) { + if (left == n && right == n) { + StringBuffer sb = new StringBuffer(); + for (String s : list) { + sb.append(s); + } + rst.add(sb.toString()); + return; + } + if (left < n) { + list.add("("); + helper(list, left + 1, right, n); + list.remove(list.size() - 1); + } + if (right < left) { + list.add(")"); + helper(list, left, right + 1, n); + list.remove(list.size() - 1); + } + } +} + + +/* + // + 1st attempt, timeout. + Thoughts: + n = 0, null + n = 1, trivial: () + Do i-- from n. For each i >= 2 + it can choose: close a paren + open another paren + front = ( + end = ) + helper(front, end, int n) + if (n == 1) { + front + "()" + end + rst.add // check duplicate + } + +*/ +public class Solution { + /** + * @param n n pairs + * @return All combinations of well-formed parentheses + */ + public ArrayList rst = new ArrayList(); + public ArrayList generateParenthesis(int n) { + if (n <= 0) { + return rst; + } else if (n == 1){ + rst.add("()"); + return rst; + } + helper("", "", n); + Collections.sort(rst); + return rst; + } + //3 + public void helper(String front, String end, int n) { + if (n == 1) { + String rt = front + "()" + end; + if (!rst.contains(rt)){ + rst.add(rt); + } + return; + } + n--; + + helper(front + "(", ")" + end, n); + helper(front + "()", end, n); + helper(front, "()" + end, n); + helper(front + end + "(", ")", n); + helper(front + end, "()", n); + helper(front + end + "()", "", n); + helper("(", ")" + front + end, n); + helper("()", front + end, n); + helper("","()" + front + end, n); + helper("(",front+end+")",n); + helper("(" + front+end,")",n); + helper("(" + front, end + ")",n); + helper("("+front+end+")", "", n); + helper("", "("+front+end+")", n); + + } + +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Graph Valid Tree.java b/Others/old records/LintCode-Backup/Graph Valid Tree.java new file mode 100644 index 0000000..18288cc --- /dev/null +++ b/Others/old records/LintCode-Backup/Graph Valid Tree.java @@ -0,0 +1,183 @@ +复习Union-Find的另外一个种形式。 +题目类型:查找2个元素是不是在一个set里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +存储的关键都是:元素相对的index上存着他的root parent. + +另一个union-find, 用hashmap的:http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +``` +/* +Given n nodes labeled from 0 to n - 1 and a list of undirected edges (each edge is a pair of nodes), +write a function to check whether these edges make up a valid tree. + +Example +Given n = 5 and edges = [[0, 1], [0, 2], [0, 3], [1, 4]], return true. + +Given n = 5 and edges = [[0, 1], [1, 2], [2, 3], [1, 3], [1, 4]], return false. + +Note +You can assume that no duplicate edges will appear in edges. +Since all edges are undirected, [0, 1] is the same as [1, 0] and thus will not appear together in edges. + +Tags Expand +Depth First Search Breadth First Search Union Find Facebook Zenefits Google + +*/ + +/* + Thoughts: do union-find. //http://www.jiuzhang.com/solutions/graph-valid-tree/ + How to check if we have disconnected pair? Think about it: + A valid tree has n-1 edges. If we have 1 disconnected pair, that means, + we lost 1 edge, like n-2 edgets now. + OR, keep thinking: if we have a cycle, which is a extra edge, that becomes n edgets. + Therefore, a first check (assume if with n-1 edgets is valid), just check if edges.lenght == n -1. + + Then, natually we can think of : what if missing a couple edges and have a couple cycles, + which makes edges.length == n - 1? + So if in this complex case, there must be >=1 cycles. Now just explicitly check for cycle. + How to use unin-find to check no cycle: +*** if a pair of node has same parent. If they do, that makes an triangle. False. + + What does Union-Find do? + Union-Find is a data structure (this problem implemented as a array), that union 2 sets and + checks if 2 elements are in the same set. + In another problem, it can be implemented with HashMap as well. + +*/ + +public class Solution { + int[] parents; + public boolean validTree(int n, int[][] edges) { + if (n - 1 != edges.length) { + return false; + } + parents = new int[n]; + //Init + for (int i = 0; i < parents.length; i++) { + parents[i] = i; + } + //Use union-find to detect if pair has common parents, and merge then to 1 set + for (int i = 0; i < edges.length; i++){ + if (find(edges[i][0]) == find(edges[i][1])) { + return false; + } + union(edges[i][0], edges[i][1]); + } + + return true; + } + + /* + Not only find parent, also update the spot parents[node] with parent node, recursively. + + *** The fact is, at all levels, if any curr != its parent, it'll trigger the find() method, + Then it makes sure parent node will be assigned to this curr node index. + + Goal: Mark curr node: who is your ancestor parent; and that indicates if other nodes are + in the same union as curr. + */ + public int find(int node) { + if (parents[node] == node) {//If curr node == its parent, return curr node. + return node; + } + //If curr node != its parent, we will attempt to find its grandparents, and assign to curr node. + parents[node] = find(parents[node]); + return parents[node]; + } + /* + Either union x into y, or the other way + */ + public void union(int x, int y) { + int findX = parents[x]; + int findY = parents[y]; + if (findX != findY) { + parents[findX] = findY; + } + } +} + + + + + + + + + + + +/* + Attempt failed: + check:No cycle.Connected. + Sort the input based on edges[i][0]. + However, this is wrong; both 0 and 1 index of an edge can be used a root. +*/ + +//Just use priorityqueue +public class Solution { + /** + * @param n an integer + * @param edges a list of undirected edges + * @return true if it's a valid tree, or false + */ + class Pair { + int from, to; + public Pair(int f, int t) { + this.from = f; + this.to = t; + } + } + public boolean validTree(int n, int[][] edges) { + if (n == 1) { + return true; + } + if (edges == null || edges.length == 0 || edges[0].length == 0 || n <= 0) { + return false; + } + HashSet set = new HashSet(); + PriorityQueue queue = new PriorityQueue(2, new Comparator(){ + public int compare(Pair A, Pair B){ + return A.from - B.from; + } + }); + + //add into queue, format it like pair(small, large) + for (int i = 0; i < edges.length; i++) { + if (edges[i][0] < edges[i][1]) { + queue.offer(new Pair(edges[i][0], edges[i][1])); + } else { + queue.offer(new Pair(edges[i][1], edges[i][0])); + } + } + //check + set.add(queue.peek().from);//0 + while (!queue.isEmpty()) { + Pair p = queue.poll(); + //check node existance && cycle + if (!set.contains(p.from) || set.contains(p.to)) { + return false; + } + set.add(p.to); + } + return true; + } +} + + + + + + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Happy Number.java b/Others/old records/LintCode-Backup/Happy Number.java new file mode 100644 index 0000000..cd29bbb --- /dev/null +++ b/Others/old records/LintCode-Backup/Happy Number.java @@ -0,0 +1,51 @@ +/* +Write an algorithm to determine if a number is happy. + +A happy number is a number defined by the following process: +Starting with any positive integer, replace the number by the sum of the squares of its digits, +and repeat the process until the number equals 1 (where it will stay), +or it loops endlessly in a cycle which does not include 1. +Those numbers for which this process ends in 1 are happy numbers. + +Example +19 is a happy number + +1^2 + 9^2 = 82 +8^2 + 2^2 = 68 +6^2 + 8^2 = 100 +1^2 + 0^2 + 0^2 = 1 +Tags Expand +Hash Table Mathematics +*/ + +/* + Thoughts: + Try some examples then find out: if it's not happy number, the 'sum of square of its digits' will + repeatedly occur. Use hashset to track existance. +*/ +public class Solution { + public boolean isHappy(int n) { + if (n <= 0) { + return false; + } + long sum = n; + HashSet set = new HashSet(); + while (sum != 1) { + String s = String.valueOf(sum); + sum = 0; + for (char c : s.toCharArray()){ + sum += (c-'0')*(c-'0'); + } + if (set.contains(sum)) { + return false; + } else { + set.add(sum); + } + } + return true; + } +} + + + + diff --git a/Others/old records/LintCode-Backup/Hash Function.java b/Others/old records/LintCode-Backup/Hash Function.java new file mode 100644 index 0000000..ba32154 --- /dev/null +++ b/Others/old records/LintCode-Backup/Hash Function.java @@ -0,0 +1,56 @@ +/* +In data structure Hash, hash function is used to convert a string(or any other type) +into an integer smaller than hash size and bigger or equal to zero. The objective of +designing a hash function is to "hash" the key as unreasonable as possible. +A good hash function can avoid collision as less as possible. +A widely used hash function algorithm is using a magic number 33, +consider any string as a 33 based big integer like follow: + +hashcode("abcd") = (ascii(a) * 33^3 + ascii(b) * 33^2 + ascii(c) *33^1 + ascii(d)*33^0) % HASH_SIZE + + = (97* 333 + 98 * 332 + 99 * 33 +100) % HASH_SIZE + + = 3595978 % HASH_SIZE + +here HASH_SIZE is the capacity of the hash table +(you can assume a hash table is like an array with index 0 ~ HASH_SIZE-1). + +Given a string as a key and the size of hash table, return the hash value of this key. + + + +Example +For key="abcd" and size=100, return 78 + +Clarification +For this problem, you are not necessary to design your own hash algorithm +or consider any collision issue, you just need to implement the algorithm as described. + +Tags Expand +Hash Table + +Thinking process: +Use given hash function. +However, need to consider integer overflow. +A simple way: save it as a long during calculation. Then return a (int). +*/ + +class Solution { + /** + * @param key: A String you should hash + * @param HASH_SIZE: An integer + * @return an integer + */ + public int hashCode(char[] key, int HASH_SIZE) { + if (key.length == 0) { + return 0; + } + long hashRst = 0; + for (int i = 0; i < key.length ; i++) { + hashRst = hashRst * 33 + (int)(key[i]); + hashRst = hashRst % HASH_SIZE; + } + return (int)hashRst; + } +}; + diff --git a/Others/old records/LintCode-Backup/HashWithArray.java b/Others/old records/LintCode-Backup/HashWithArray.java new file mode 100644 index 0000000..5b03fa9 --- /dev/null +++ b/Others/old records/LintCode-Backup/HashWithArray.java @@ -0,0 +1,69 @@ +/* + Self Test: + Implement HashTable with just array and integer. + + Thoughts: + A simple approach is to % size of the array, if the key exist, move 1 slot over. + + A bug will be: when exceeds the size of array, there will be no avialable space, + and it'll run into error. + + Inspired here :http://www.algolist.net/Data_structures/Hash_table/Simple_example + 1. create a entry class. + 2. hash the key, and put Entry into that hased index. +*/ + +Class Entry{ + int key; + int value; + public Entry(int key, int value) { + this.key = key; + this.value = value; + } + + public getKey(){ + return this.key; + } + + public getValue() { + return this.value; + } +} + +Class HashMap { + int[] table; + int SIZE = 128; + public HashMap(){ + table = new int[SIZE]; + for (int i = 0; i < SIZE; i++) { + table[i] = null; + } + } + + public void put(int key, int value){ + int hash = key % SIZE; + while (table[hash] != null && table[hash].getKey() != key) { + hash = (hash + 1) % SIZE; + } + table[key] = new Entry(key, value); + } + + public int get(int key) { + int hash = key % SIZE; + while (table[hash] != null && table[hash].getKey() != key) { + hash = (hash + 1) % SIZE; + } + if (table[hash] == null) { + return -1; + } + return table[hash].getValue(); + } +} + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Heapify.java b/Others/old records/LintCode-Backup/Heapify.java new file mode 100644 index 0000000..f4f17fe --- /dev/null +++ b/Others/old records/LintCode-Backup/Heapify.java @@ -0,0 +1,112 @@ +Heapify里面的siftdown的部分: + 只能从for(i = n/2-1 ~ 0), 而不能从for(i = 0 ~ n/2 -1) + 这是不是因为siftdown每次只顺脚下的孩子,所以必须中间开花,向上跑的时候才能确保脚下是符合heap规则的 + +``` +/* +Given an integer array, heapify it into a min-heap array. + +For a heap array A, A[0] is the root of heap, and for each A[i], A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i]. +Have you met this question in a real interview? Yes +Example +Given [3,2,1,4,5], return [1,2,3,4,5] or any legal heap array. + +Challenge +O(n) time complexity + +Clarification +What is heap? + +Heap is a data structure, which usually have three methods: push, pop and top. where "push" add a new element the heap, "pop" delete the minimum/maximum element in the heap, "top" return the minimum/maximum element. + +What is heapify? +Convert an unordered integer array into a heap array. If it is min-heap, for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. + +What if there is a lot of solutions? +Return any of them. +Tags Expand +LintCode Copyright Heap +*/ + + +/* +Thoughts: +Based on the knowledge of Hash Heap: http://www.jiuzhang.com/solutions/hash-heap/ +Try to implement part of the Heap basis, heapify. + +In this problem, re-organize the input array to fit heap basis + +1. Compare next value with head. +2. If smaller than head, do a siftdown + +siftdown: +always swap with the smaller child +As long as left.child.i < array length, continue while: + If no right child, or left.val < right.val, + son = left. + else + son = right +Check if curr.val < son.val + if so, break, we are good. + If not, swap(curr,son) +curr = son, and move on the next round of while + + +NOTE: +The for loop start from i = n/2 -1, which makes the right-most index = 2*(n/2-1) + 1 = n - 2 + 1 = n-1. +*/ + + +public class Solution { + /** + * @param A: Given an integer array + * @return: void + */ + public void heapify(int[] A) { + if (A == null || A.length == 0) { + return; + } + int son = 0; + int currId = 0; + int leftId = 0; + int rightId = 0; + int n = A.length; + for (int i = n/2 - 1; i >= 0; i--) { + currId = i; + while (currId * 2 + 1 < n) { + leftId = currId * 2 + 1; + rightId = currId * 2 + 2; + if (rightId >= n || A[leftId] <= A[rightId]) { + son = leftId; + } else { + son = rightId; + } + if (A[currId] <= A[son]) { + break; + } else { + int temp = A[currId]; + A[currId] = A[son]; + A[son] = temp; + } + currId = son; + }//end while + + }//end for + } +} + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/House Robber.java b/Others/old records/LintCode-Backup/House Robber.java new file mode 100644 index 0000000..e021fce --- /dev/null +++ b/Others/old records/LintCode-Backup/House Robber.java @@ -0,0 +1,104 @@ +最基本的dp。 +看前一个或前两个的情况,再总和考虑当下的。 +思考的适合搞清楚当下的和之前的情况的关系。 +滚动数组的优化,就是确定了是这类“只和前一两个位子“相关的Fn而推出的。 +``` +/* +You are a professional robber planning to rob houses along a street. +Each house has a certain amount of money stashed, +the only constraint stopping you from robbing each of them is that +adjacent houses have security system connected and it will automatically +contact the police if two adjacent houses were broken into on the same night. + +Given a list of non-negative integers representing the amount of money of each house, +determine the maximum amount of money you can rob tonight without alerting the police. + +Have you met this question in a real interview? Yes +Example +Given [3, 8, 4], return 8. + +Challenge +O(n) time and O(1) memory. + +Tags Expand +Dynamic Programming + +*/ +/* + Thoughts: + dp[i]: the best we can rob by i. + If I'm at house i, I'll either pick i or not pick i. + Pick i: dp[i-2] + A[i] + Not pick i: dp[i+1] + fn: + dp[i] = Math.max(dp[i-1], dp[i-2] + A[i]) + Init: + dp[0] = A[0] + dp[1] = Math.max(A[0], A[1]) + Return: + dp[n-1] +*/ + +//O(n) space +public class Solution { + /** + * @param A: An array of non-negative integers. + * return: The maximum amount of money you can rob tonight + */ + public long houseRobber(int[] A) { + if (A == null || A.length == 0) { + return 0; + } else if (A.length == 1) { + return A[0]; + } + int n = A.length; + long[] dp = new long[n]; + dp[0] = A[0]; + dp[1] = Math.max(A[0], A[1]); + + for (int i = 2; i < n; i++) { + dp[i] = Math.max(dp[i-1], dp[i-2] + A[i]); + } + + return dp[n - 1]; + } +} + + +//O(1) space, 滚动数组。 +public class Solution { + public long houseRobber(int[] A) { + if (A == null || A.length == 0) { + return 0; + } else if (A.length == 1) { + return A[0]; + } + int n = A.length; + long[] dp = new long[2]; + dp[0] = A[0]; + dp[1] = Math.max(A[0], A[1]); + + for (int i = 2; i < n; i++) { + dp[i%2] = Math.max(dp[(i-1)%2], dp[(i-2)%2] + A[i]); + } + + return dp[(n - 1)%2]; + } +} + + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Java/Identical Binary Tree.java b/Others/old records/LintCode-Backup/Identical Binary Tree.java similarity index 100% rename from Java/Identical Binary Tree.java rename to Others/old records/LintCode-Backup/Identical Binary Tree.java diff --git a/Java/Implement Queue by Two Stacks.java b/Others/old records/LintCode-Backup/Implement Queue by Two Stacks.java similarity index 100% rename from Java/Implement Queue by Two Stacks.java rename to Others/old records/LintCode-Backup/Implement Queue by Two Stacks.java diff --git a/Others/old records/LintCode-Backup/Implement Stack by Two Queues.java b/Others/old records/LintCode-Backup/Implement Stack by Two Queues.java new file mode 100644 index 0000000..686b8c9 --- /dev/null +++ b/Others/old records/LintCode-Backup/Implement Stack by Two Queues.java @@ -0,0 +1,80 @@ +两个Queue,交互倒水 +用一个Temp做swap + +做法1: +逻辑在top()/pop()里, 每次换水,查看末尾项. + +做法2: +逻辑在push里面: +1. x 放q2。 +2. q1全部offer/append到q2. +3. 用一个Temp做swap q1, q2. +q1的头,就一直是最后加进去的值. +``` +/* +Implement Stack by Two Queues + +Implement a stack by two queues. The queue is first in first out (FIFO). +That means you can not directly pop the last element in a queue. + +Have you met this question in a real interview? Yes +Example +push(1) +pop() +push(2) +isEmpty() // return false +top() // return 2 +pop() +isEmpty() // return true +Tags Expand +Stack Queue + +*/ + +/* + Thoughts: + 2 queue are like two cups. We are fliping water into/out between q1 and q2. + pop and top are fliping water. + Use p1 as the base. +*/ + +class Stack { + private Queue q1 = new LinkedList(); + private Queue q2 = new LinkedList(); + // Push a new item into the stack + public void push(int x) { + q1.offer(x); + } + + // Pop the top of the stack + public void pop() { + while (q1.size() > 1) { + q2.offer(q1.poll()); + } + q1.poll(); + swap(); + } + + // Return the top of the stack + public int top() { + while (q1.size() > 1) { + q2.offer(q1.poll()); + } + int rst = q1.poll(); + q2.offer(rst); + swap(); + return rst; + } + + public void swap(){ + Queue temp = q1; + q1 = q2; + q2 = temp; + } + + // Check the stack is empty or not. + public boolean isEmpty() { + return q1.isEmpty(); + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Implement Stack.java b/Others/old records/LintCode-Backup/Implement Stack.java new file mode 100644 index 0000000..7c4a3dd --- /dev/null +++ b/Others/old records/LintCode-Backup/Implement Stack.java @@ -0,0 +1,61 @@ +stack 后入先出. +Data Structure: ArrayList +return/remove ArrayList的末尾项。 + +``` +/* + Implement Stack + +Implement a stack. You can use any data structure inside a stack except stack itself to implement it. + + +Example +push(1) +pop() +push(2) +top() // return 2 +pop() +isEmpty() // return true +push(3) +isEmpty() // return false +Tags Expand +Array Stack +*/ + +/* +Thoughts: +use arraylist and a index tracker - leng +push: add to end +pop: remove end +top: get end. +isEmpty: return length +*/ + +class Stack { + private ArrayList list = new ArrayList(); + // Push a new item into the stack + public void push(int x) { + list.add(x); + } + + // Pop the top of the stack + public void pop() { + if (list.size() > 0) { + list.remove(list.size() - 1); + } + } + + // Return the top of the stack + public int top() { + if (list.size() > 0) { + return list.get(list.size() - 1); + } + return -1; + } + + // Check the stack is empty or not. + public boolean isEmpty() { + return list.size() == 0; + } +} +``` \ No newline at end of file diff --git a/Java/Implement Trie.java b/Others/old records/LintCode-Backup/Implement Trie.java similarity index 100% rename from Java/Implement Trie.java rename to Others/old records/LintCode-Backup/Implement Trie.java diff --git a/Others/old records/LintCode-Backup/Insert Interval.java b/Others/old records/LintCode-Backup/Insert Interval.java new file mode 100644 index 0000000..8498a3e --- /dev/null +++ b/Others/old records/LintCode-Backup/Insert Interval.java @@ -0,0 +1,75 @@ +/* +Given a non-overlapping interval list which is sorted by start point. + +Insert a new interval into it, make sure the list is still in order and non-overlapping (merge intervals if necessary). + +Have you met this question in a real interview? Yes +Example +Insert [2, 5] into [[1,2], [5,9]], we get [[1,9]]. + +Insert [3, 4] into [[1,2], [5,9]], we get [[1,2], [3,4], [5,9]]. + +Tags Expand +Basic Implementation + +Thoughts: +1. Find right position to insert: find the last start position that's <= newInterval.start +2. After insertion, merge. +3. How to merge? Look at merge inerval question +*/ + + + +/** + * Definition of Interval: + * public classs Interval { + * int start, end; + * Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + */ +class Solution { + /** + * Insert newInterval into intervals. + * @param intervals: Sorted interval list. + * @param newInterval: A new interval. + * @return: A new sorted interval list. + */ + public ArrayList insert(ArrayList intervals, Interval newInterval) { + if (intervals == null || intervals.size() == 0 || newInterval == null) { + if (newInterval != null) { + intervals.add(newInterval); + } + return intervals; + } + //Insert + int start = newInterval.start; + int front = -1; + for (int i = 0; i < intervals.size(); i++) { + if (intervals.get(i).start <= start) { + front = i; + } + } + if (front == -1) { + intervals.add(0, newInterval); + } + intervals.add(front + 1, newInterval); + + //Merge + Interval pre = intervals.get(0); + Interval curr = null; + for (int i = 1; i < intervals.size(); i++) { + curr = intervals.get(i); + if (pre.end >= curr.start) { + pre.end = pre.end > curr.end ? pre.end : curr.end; + intervals.remove(i); + i--; + } else { + pre = curr; + } + } + + return intervals; + } +} \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Insert Node in a Binary Search Tree .java b/Others/old records/LintCode-Backup/Insert Node in a Binary Search Tree .java new file mode 100644 index 0000000..d44d293 --- /dev/null +++ b/Others/old records/LintCode-Backup/Insert Node in a Binary Search Tree .java @@ -0,0 +1,76 @@ +/* +43% Accepted +Given a binary search tree and a new tree node, insert the node into the tree. You should keep the tree still be a valid binary search tree. + +Example +Given binary search tree as follow: + + 2 + + / \ + +1 4 + + / + + 3 + +after Insert node 6, the tree should be: + + 2 + + / \ + +1 4 + + / \ + + 3 6 + +Challenge +Do it without recursion + +Tags Expand +Binary Search Tree LintCode Copyright + +Thinking process: +Binary Search Tree: +parent must < left node +parent must > right node +use a dummy node runNode to flow around on the binary search tree, compare with target node. +Find the leaf node and add into appropriate pos. +*/ + +public class Solution { + /** + * @param root: The root of the binary search tree. + * @param node: insert this node into the binary search tree + * @return: The root of the new binary search tree. + */ + public TreeNode insertNode(TreeNode root, TreeNode node) { + if (root == null) { + root = node; + return root; + } + TreeNode runNode = root; + TreeNode parentNode = null; + while (runNode != null) { + parentNode = runNode; + if (runNode.val > node.val) { + runNode = runNode.left; + } else { + runNode = runNode.right; + } + }//while + + if (parentNode != null) { + if (parentNode.val > node.val) { + parentNode.left = node; + } else { + parentNode.right = node; + } + } + return root; + } +} + diff --git a/Others/old records/LintCode-Backup/Insertion Sort List.java b/Others/old records/LintCode-Backup/Insertion Sort List.java new file mode 100644 index 0000000..b9d1b74 --- /dev/null +++ b/Others/old records/LintCode-Backup/Insertion Sort List.java @@ -0,0 +1,110 @@ +想明白原理就好做了: +基本上就是正常的想法:已经有个sorted list, insert一个element进去。怎么做? + while 里面每个元素都小于 curr, keep going + 一旦curr在某个点小了,加进去当下这个空隙。 +这个题目也就是:把list里面每个元素都拿出来,scan and insert一遍! + +``` +/* +Sort a linked list using insertion sort. + +Example +Given 1->3->2->0->null, return 0->1->2->3->null. + +Tags Expand +Sort Linked List +*/ + + +/* + Recap. 12.10.2015 + http://www.cnblogs.com/springfor/p/3862468.html + + Assumed we have a sorted list: now we pick each element and insert into that sorted list. + This is insertion. + + If we are constly picking from (o ~ n) of this list itself, it becomes Insertion sort. + + 1. make a sortedList, Now we need to have pre,curr,next for wapping, whenever we find the correct curr + 2. a pre node pointer [used to store the list each time to check where to insert curr], + 3. curr is the node being check very round + 4. next is simply used for wapping, not much other usage +*/ +public class Solution { + /** + * @param head: The first node of linked list. + * @return: The head of linked list. + */ + public ListNode insertionSortList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + + ListNode sortedListHead = new ListNode(0);//dummy head + ListNode pre,curr,next; + curr = head; + + while (curr != null) {// insert every single curr into sorted list + next = curr.next; //prepare for insertion && swapping. + pre = sortedListHead;//the list to scan + while (pre.next != null && pre.next.val <= curr.val) { + //as long as pre and its front are sorted ascending, keep going + pre = pre.next; + } + //when pre.next == null , or curr is less than a node in pre.next, we want to insert curr before that pre.next node + curr.next = pre.next; + pre.next = curr; + + curr = next;//use the original next, instead of the new curr.next + }//end while + + return sortedListHead.next; + } +} + + + +/* +Thoughts: +Look at head pointer, which is the current element we focus on. +If it's greater than the next pointer value, we move on. Use a while loop to check the entire +list every time with a new head. +If the head.val is less/equal than the next.val, we stop the at this next pointer, then cut it, +insert head. +*/ +/** + * Definition for ListNode. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int val) { + * this.val = val; + * this.next = null; + * } + * } + */ +public class Solution { + /** + * @param head: The first node of linked list. + * @return: The head of linked list. + */ + public ListNode insertionSortList(ListNode head) { + if (head == null) { + return null; + } + ListNode dummy = new ListNode(0); + while (head != null) { + ListNode node = dummy; + while (node.next != null && node.next.val < head.val) { + node = node.next; + } + ListNode temp = head.next; + head.next = node.next; + node.next = head; + head = temp; + } + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Interleaving Positive and Negative Numbers.java b/Others/old records/LintCode-Backup/Interleaving Positive and Negative Numbers.java new file mode 100644 index 0000000..4463fb2 --- /dev/null +++ b/Others/old records/LintCode-Backup/Interleaving Positive and Negative Numbers.java @@ -0,0 +1,63 @@ +不管遇到啥,先排个序。 +这里主要要特别考虑,正数多还是负数多的问题。 +count一下,然后举两个小栗子就看出来端倪了。 +然后Two Pointer +``` +/* +Given an array with positive and negative integers. Re-range it to interleaving with positive and negative integers. + +Example +Given [-1, -2, -3, 4, 5, 6], after re-range, it will be [-1, 5, -2, 4, -3, 6] or any other reasonable answer. + +Note +You are not necessary to keep the original order of positive integers or negative integers. + +Challenge +Do it in-place and without extra memory. + +Tags Expand +Two Pointers +*/ + +/* + Thoughts: + Sort, so it becomes [-1,-2,-3,-4,4,5,6,7] + Two pointer start,end. + Every round, start +=2, end -= 2; + Note: have to find out how many negative/positive integers first. +*/ + +class Solution { + /** + * @param A: An integer array. + * @return: void + */ + public void rerange(int[] A) { + if (A == null || A.length == 0) { + return; + } + Arrays.sort(A); + int count = 0; + for (int num : A){ + count += num >= 0 ? 1 : -1; + } + int start = 0; + int end = A.length - 1; + if (count < 0) { + start++; + } else if (count > 0){ + end--; + } + + while (start < end) { + if (A[start] < 0 && A[end] >= 0) { + int temp = A[start]; + A[start] = A[end]; + A[end] = temp; + } + start += 2; + end -= 2; + } + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Interleaving String.java b/Others/old records/LintCode-Backup/Interleaving String.java new file mode 100644 index 0000000..a1b4c87 --- /dev/null +++ b/Others/old records/LintCode-Backup/Interleaving String.java @@ -0,0 +1,101 @@ +/* +Given three strings: s1, s2, s3, determine whether s3 is formed by the interleaving of s1 and s2. + +Example +For s1 = "aabcc", s2 = "dbbca" + +When s3 = "aadbbcbcac", return true. +When s3 = "aadbbbaccc", return false. +Challenge +O(n2) time or better + +Tags Expand +Longest Common Subsequence Dynamic Programming + +Attempt2: DP[i][j]: boolean that if first S1(i) chars and first S2(j) chars can interleavign first S3(i + j) +Match one char by one char. We have 2 conditions: match s1 or s2 char, Let's do double-for-loop on s1 and s2 +1. match s1: s3.charAt(i + j -1) == s1.charAt(i - 1) && DP[i - 1][j]; // makes sure DP[i-1][j] also works before adding s1[i-1] onto the match list +2. match s2: s3.charAt(i + j -1) == s2.charAt(j - 1) && DP[i][j - 1]// similar as above + +Note: +Need to initiate the starting conditions with just s1, or just s2 +Note2: +DP ususally start i == 1, and always use (i - 1) in the loop... this is all because we are trying to get DP[i][j], which are 1 index more than length +*/ +public class Solution { + public boolean isInterleave(String s1, String s2, String s3) { + if (s3 == null || (s1 == null && s2 == null) || s1.length() + s2.length() != s3.length()) { + return false; + } + boolean[][] DP = new boolean[s1.length() + 1][s2.length() + 1]; + DP[0][0] = true; // empty s1 and s2 would be a working case + + //with just s1: + for (int i = 1; i <= s1.length(); i++) { + if (s3.charAt(i - 1) == s1.charAt(i - 1) && DP[i - 1][0]) { + DP[i][0] = true; + } + } + + //with just s2: + for (int j = 1; j <= s2.length(); j++) { + if (s3.charAt(j - 1) == s2.charAt(j - 1) && DP[0][j - 1]) { + DP[0][j] = true; + } + } + + for (int i = 1; i <= s1.length(); i++) { + for (int j = 1; j <= s2.length(); j++) { + if ((s3.charAt(i + j - 1) == s1.charAt(i - 1) && DP[i - 1][j]) + || (s3.charAt(i + j - 1) == s2.charAt(j - 1) && DP[i][j - 1])) { + DP[i][j] = true; + } + } + } + + return DP[s1.length()][s2.length()]; + } +} + + + + +/* + +Attempt1, Incorrect: tho, magically passed 91% of lintcode, by coincidence +This solution could goes on and on with s1, and failed at certain point when j == 0 does not fit in. +s1 = "sdfjas;dfjoisdu" +s2 = "dfnakd" +s3 = "sdfjas;dfjoisdf..." // Failed at that 'f' in s3 + +Thoughts: +DP[mxn]: loop through S1.length and S2.length, record DP[k] = true or false. +DP[k] = (S1(0~i) + S2(0 ~ j)) is leading S3: index of (xxx) == 0. + +*/ +public class Solution { + + public boolean isInterleave(String s1, String s2, String s3) { + if (s3 == null || (s1 == null && s2 == null) || s1.length() + s2.length() != s3.length()) { + return false; + } + + int i = 0; + int j = 0; + String base = ""; + for (int k = 0; k < s1.length()*s2.length() - 1; k++) { + if (i < s1.length() || j < s2.length()) { + if (i < s1.length() && s3.indexOf(base + s1.charAt(i)) == 0) { + base += s1.charAt(i); + i++; + } else if (j < s2.length() && s3.indexOf(base + s2.charAt(j)) == 0) { + base += s2.charAt(j); + j++; + } else { + return false; + } + } + } + return true; + } +} diff --git a/Others/old records/LintCode-Backup/Intersection of Two Linked Lists.java b/Others/old records/LintCode-Backup/Intersection of Two Linked Lists.java new file mode 100644 index 0000000..7363445 --- /dev/null +++ b/Others/old records/LintCode-Backup/Intersection of Two Linked Lists.java @@ -0,0 +1,95 @@ +E + +长短list,找重合点。 +长度不同的话,切掉长的list那个的extra length。 那么起点一样后,重合点就会同时到达。 + + +``` +/* +Write a program to find the node at which the intersection of two singly linked lists begins. + +Example +The following two linked lists: + +A: a1 → a2 + ↘ + c1 → c2 → c3 + ↗ +B: b1 → b2 → b3 +begin to intersect at node c1. + +Note +If the two linked lists have no intersection at all, return null. +The linked lists must retain their original structure after the function returns. +You may assume there are no cycles anywhere in the entire linked structure. +Challenge +Your code should preferably run in O(n) time and use only O(1) memory. + +Tags Expand +Linked List +*/ + +/* + Thoughts: + If a and b share some part, if cut off the extra header of b (seen in above example), they should start at same index and touch c1 at same time. + So traverse a, b, and calculate countA, countB => dif = countB - countA. + cut off the extra, then start iterating to find c1. + +*/ + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ + +public class Solution { + /** + * @param headA: the first list + * @param headB: the second list + * @return: a ListNode + */ + public ListNode getIntersectionNode(ListNode headA, ListNode headB) { + if (headA == null || headB == null) { + return null; + } + int countA = 0; + int countB = 0; + int diff = 0; + ListNode node = headA; + while (node != null) { + countA++; + node = node.next; + } + node = headB; + while (node != null) { + countB++; + node = node.next; + } + diff = Math.abs(countA - countB); + node = (countA > countB) ? headA : headB; + while (diff != 0) { + diff--; + node = node.next; + } + ListNode nodeA = (countA > countB) ? node : headA; + ListNode nodeB = (countA > countB) ? headB : node; + while (nodeA != null && nodeB != null) { + if (nodeA == nodeB) { + return nodeA; + } + nodeA = nodeA.next; + nodeB = nodeB.next; + } + + return null; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Interval Minimum Number.java b/Others/old records/LintCode-Backup/Interval Minimum Number.java new file mode 100644 index 0000000..13b5047 --- /dev/null +++ b/Others/old records/LintCode-Backup/Interval Minimum Number.java @@ -0,0 +1,118 @@ +又一个Segment tree的例子。 +把min number存在区间里面。 + +类似的有存:max, sum, min, count + +如果考到的几率不高。那么这一系列题目就是练习写代码的能力,和举一反三的心态。 +``` +/* +Given an integer array (index from 0 to n-1, where n is the size of this array), and an query list. Each query has two integers [start, end]. For each query, calculate the minimum number between index start and end in the given array, return the result list. + +Example +For array [1,2,7,8,5], and queries [(1,2),(0,4),(2,4)], return [2,1,5] + +Note +We suggest you finish problem Segment Tree Build, Segment Tree Query and Segment Tree Modify first. + +Challenge +O(logN) time for each query + +Tags Expand +LintCode Copyright Binary Tree Segment Tree +*/ + +/* + Thoughts: + Build a SegmentMinTree. + Do search using the interval +*/ + +/** + * Definition of Interval: + * public classs Interval { + * int start, end; + * Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + */ +public class Solution { + class SegmentMinTreeNode { + int start,end,min; + SegmentMinTreeNode left, right; + public SegmentMinTreeNode(int start, int end, int min) { + this.start = start; + this.end = end; + this.min = min; + this.left = null; + this.right = null; + } + } + + public SegmentMinTreeNode build(int start, int end, int[] A) { + if (start == end) { + return new SegmentMinTreeNode(start, end, A[start]); + } + int min = (start + end) / 2; + SegmentMinTreeNode left = build(start, min, A); + SegmentMinTreeNode right = build(min + 1, end, A); + SegmentMinTreeNode node = new SegmentMinTreeNode(start, end, Math.min(left.min, right.min)); + node.left = left; + node.right = right; + + return node; + } + + public int search(SegmentMinTreeNode root, int start, int end){ + if (root.start == start && root.end == end) { + return root.min; + } + + int mid = (root.start + root.end) / 2; + if (end <= mid) { + return search(root.left, start, end); + } + if (start > mid) { + return search(root.right, start, end); + } + + return Math.min(search(root.left, start, root.left.end), search(root.right, root.right.start, end)); + } + + + /** + *@param A, queries: Given an integer array and an query list + *@return: The result list + */ + public ArrayList intervalMinNumber(int[] A, ArrayList queries) { + ArrayList rst = new ArrayList(); + if (A == null || A.length == 0 || queries == null || queries.size() == 0) { + return rst; + } + SegmentMinTreeNode root = build(0, A.length - 1, A); + for (Interval range : queries) { + int min = search(root, range.start, range.end); + rst.add(min); + } + return rst; + } +} + + + + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Interval Sum II.java b/Others/old records/LintCode-Backup/Interval Sum II.java new file mode 100644 index 0000000..32e5a9d --- /dev/null +++ b/Others/old records/LintCode-Backup/Interval Sum II.java @@ -0,0 +1,123 @@ +这个题如果一上来就是做,拿的确烧脑,原因只有一个:太长了呀。就像Expression Tree一样。一旦知道了怎么做,就简单了,可如果生疏,也就难。 +这题是好几个SegmentTree的结合,但是没什么创新的。 +``` +/* +Given an integer array in the construct method, implement two methods query(start, end) and modify(index, value): + +For query(start, end), return the sum from index start to index end in the given array. +For modify(index, value), modify the number in the given index to value + +Example +Given array A = [1,2,7,8,5]. + +query(0, 2), return 10. +modify(0, 4), change A[0] from 1 to 4. +query(0, 1), return 6. +modify(2, 1), change A[2] from 7 to 1. +query(2, 4), return 14. +Note +We suggest you finish problem Segment Tree Build, Segment Tree Query and Segment Tree Modify first. + +Challenge +O(logN) time for query and modify. + +Tags Expand +LintCode Copyright Binary Tree Segment Tree +*/ + +/* + Thought: + This is doing the SegmentSumTree all over again, and also the Segment Tree Modify method. + 1. Create SegmentSumTreeNode + 2. Build it by segment tree definition + 3. query: binary search and calcualte sum + 4. modify: binary search, and re-compare the max at each level. +*/ +public class Solution { + /* you may need to use some attributes here */ + class SegmentSumTreeNode { + int start,end; + long sum; + SegmentSumTreeNode left,right; + public SegmentSumTreeNode(int start, int end, long sum){ + this.start = start; + this.end = end; + this.sum = sum; + this.left = null; + this.right = null; + } + } + public SegmentSumTreeNode build(int start, int end, int[] A) { + if (start == end) { + return new SegmentSumTreeNode(start, end, A[start]); + } + int mid = (start + end)/2; + SegmentSumTreeNode left = build(start, mid, A); + SegmentSumTreeNode right = build(mid + 1, end, A); + + SegmentSumTreeNode node = new SegmentSumTreeNode(start, end, left.sum + right.sum); + node.left = left; + node.right = right; + return node; + } + + SegmentSumTreeNode root = null; + /** + * @param A: An integer array + */ + public Solution(int[] A) { + if (A == null || A.length == 0) { + return; + } + root = build(0, A.length - 1, A); + } + + /** + * @param start, end: Indices + * @return: The sum from start to end + */ + public long query(int start, int end) { + return queryHelper(root, start, end); + } + + public long queryHelper(SegmentSumTreeNode root, int start, int end){ + if (start > end) { + return 0; + } else if (root.start == start && root.end == end) { + return root.sum; + } + int mid = (root.start + root.end)/2; + if (end <= mid) { + return queryHelper(root.left, start, end); + } else if (start > mid) { + return queryHelper(root.right, start, end); + } + return queryHelper(root.left, start, root.left.end) + queryHelper(root.right, root.right.start, end); + } + + /** + * @param index, value: modify A[index] to value. + */ + public void modify(int index, int value) { + modifyHelper(root, index, value); + } + + public void modifyHelper(SegmentSumTreeNode node, int index, int value) { + if (node.start == index && node.end == index) { + node.sum = value; + return; + } + int mid = (node.start + node.end)/2; + if (index <= mid) { + modifyHelper(node.left, index, value); + } else { + modifyHelper(node.right, index, value); + } + node.sum = node.left.sum + node.right.sum; + } + + +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Interval Sum.java b/Others/old records/LintCode-Backup/Interval Sum.java new file mode 100644 index 0000000..0e18dfe --- /dev/null +++ b/Others/old records/LintCode-Backup/Interval Sum.java @@ -0,0 +1,109 @@ +其实是segment tree 每个node上面加个sum。 +构建tree记得怎么弄就好了。很简单的。 +但是,我犯了错..在search的时候求mid时,忘记了以root为基准(我用interval.start and interval.end为基准,当然错了) +但实际上search起来就是binary search的想法,在interval/segment tree上面跑。顺顺哒。 +``` +/* + +Given an integer array (index from 0 to n-1, where n is the size of this array), and an query list. Each query has two integers [start, end]. For each query, calculate the sum number between index start and end in the given array, return the result list. + +Example +For array [1,2,7,8,5], and queries [(0,4),(1,2),(2,4)], return [23,9,20] + +Note +We suggest you finish problem Segment Tree Build, Segment Tree Query and Segment Tree Modify first. + +Challenge +O(logN) time for each query +*/ + +/* + Thoughts: + Feels like constructing segment tree, and attach 'interval sum' to each node, after conquer its left and right child's sum. +*/ + +/** + * Definition of Interval: + * public classs Interval { + * int start, end; + * Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + */ +public class Solution { + public class SegmentSumTreeNode { + public int start, end; + public long sum; + public SegmentSumTreeNode left,right; + public SegmentSumTreeNode(int start, int end, long sum) { + this.start = start; + this.end = end; + this.sum = sum; + this.left = null; + this.right = null; + } + } + + public SegmentSumTreeNode buildTree(int[] A, int start, int end) { + if (start == end) { + return new SegmentSumTreeNode(start, end, A[start]); + } + int mid = (start + end)/2; + SegmentSumTreeNode leftChild = buildTree(A, start, mid); + SegmentSumTreeNode rightChid = buildTree(A, mid + 1, end); + + SegmentSumTreeNode node = new SegmentSumTreeNode(start, end, leftChild.sum + rightChid.sum); + node.left = leftChild; + node.right = rightChid; + + return node; + } + + public long searchTree(SegmentSumTreeNode root, int start, int end) { + if (root.start == start && root.end == end) { + return root.sum; + } + int mid = (root.start + root.end)/2; + if (end <= mid) { + return searchTree(root.left, start, end); + } else if (start > mid) { + return searchTree(root.right, start, end); + } + //start <= mid < end + return searchTree(root.left, start, root.left.end) + searchTree(root.right, root.right.start, end); + } + + /** + *@param A, queries: Given an integer array and an query list + *@return: The result list + */ + public ArrayList intervalSum(int[] A, ArrayList queries) { + ArrayList rst = new ArrayList(); + if (A == null || A.length == 0 || queries == null || queries.size() == 0) { + return rst; + } + SegmentSumTreeNode root = buildTree(A, 0, A.length - 1); + + for (Interval range : queries) { + long sum = 0; + /* + //Check for errors, but don't have to do these checks + //Well, it's being checked in segment query II + if (range.start < root.start && range.end > root.end) { + sum = root.sum; + } else if (range.start < root.start && range.end <= root.end) { + sum = searchTree(root, root.start, range.end); + } else if (range.start >= root.start && range.end > root.end) { + sum = searchTree(root, range.start, root.end); + } else { + sum = searchTree(root, range.start, range.end); + }*/ + sum = searchTree(root, range.start, range.end); + rst.add(sum); + } + return rst; + } + +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Invert Binary Tree.java b/Others/old records/LintCode-Backup/Invert Binary Tree.java new file mode 100644 index 0000000..181aa0d --- /dev/null +++ b/Others/old records/LintCode-Backup/Invert Binary Tree.java @@ -0,0 +1,82 @@ +/* +Invert a binary tree. + +Example + 1 1 + / \ / \ +2 3 => 3 2 + / \ + 4 4 +Challenge +Do it in recursion is acceptable, can you do it without recursion? + +Tags Expand +Binary Tree + +Thoughts: +1. Swap every node's left and right child. Recursion seems good. + +2. If not recursion, can use a queue to keep track of nodes. Keep swapping until the queue +is processed. +*/ + + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + * @param root: a TreeNode, the root of the binary tree + * @return: nothing + */ + public void invertBinaryTree(TreeNode root) { + if (root == null) { + return; + } + Queue queue = new LinkedList(); + queue.offer(root); + while(!queue.isEmpty()) { + TreeNode node = queue.poll(); + TreeNode temp = node.left; + node.left = node.right; + node.right = temp; + if (node.left != null) { + queue.offer(node.left); + } + if (node.right != null) { + queue.offer(node.right); + } + } + } +} + + +//Now, solution 2, try recursion. +public class Solution { + /** + * @param root: a TreeNode, the root of the binary tree + * @return: nothing + */ + public void invertBinaryTree(TreeNode root) { + if (root == null) { + return; + } + TreeNode temp = root.left; + root.left = root.right; + root.right = temp; + + invertBinaryTree(root.left); + invertBinaryTree(root.right); + } +} + + + diff --git a/Others/old records/LintCode-Backup/Jump Game II.java b/Others/old records/LintCode-Backup/Jump Game II.java new file mode 100644 index 0000000..26720f0 --- /dev/null +++ b/Others/old records/LintCode-Backup/Jump Game II.java @@ -0,0 +1,57 @@ +/* +Given an array of non-negative integers, you are initially positioned at the first index of the array. + +Each element in the array represents your maximum jump length at that position. + +Your goal is to reach the last index in the minimum number of jumps. + +Example +Given array A = [2,3,1,1,4] + +The minimum number of jumps to reach the last index is 2. (Jump 1 step from index 0 to 1, then 3 steps to the last index.) + +Tags Expand +Greedy Array + +Thanks to Yu’s Garden blog +Thinking process: +0. Use two pointers pStart and pEnd to track the potential locations we can move to. +Consider a range from current spot to the farthest spot: try to find a max value from this range, and see if the max can reach the tail of array. +If no max can read the tail of array, that means we need to move on. At this point, let pStart = pEnd + 1. At same time, move pEnd to the max spot we can go to. Since pEnd moves forward, we could step++ +If max reach the tail of array, return the steps. +*/ + +public class Solution { + /** + * @param A: A list of lists of integers + * @return: An integer + */ + public int jump(int[] A) { + if (A == null || A.length == 0) { + return 0; + } + int pStart = 0; + int pEnd = 0; + int steps = 0; + while (pEnd < A.length - 1) { + steps++; //Cound step everytime when pEnd is moving to the farthest. + int farthest = 0; + //Find farest possible and see if reach the tail + for (int i = pStart; i <= pEnd; i++) { + farthest = Math.max(farthest, i + A[i]); + if (farthest >= A.length - 1) { + return steps; + } + } + //Re-select pointer position for start and end + pStart = pEnd + 1; + pEnd = farthest; + } + return -1; //This is the case where no solution can be found. + } +} + + +//Also DP from nineChapter: +http://www.ninechapter.com/solutions/jump-game-ii/ + diff --git a/Others/old records/LintCode-Backup/Jump Game.java b/Others/old records/LintCode-Backup/Jump Game.java new file mode 100644 index 0000000..53664b9 --- /dev/null +++ b/Others/old records/LintCode-Backup/Jump Game.java @@ -0,0 +1,82 @@ +/* +Given an array of non-negative integers, you are initially positioned at the first index of the array. + +Each element in the array represents your maximum jump length at that position. + +Determine if you are able to reach the last index. + +Example +A = [2,3,1,1,4], return true. + +A = [3,2,1,0,4], return false. + +This can be done using DP. However, greedy algorithm is fast in this particular problem. Consider both solutions. + +DP +Thinking Process: +We have array A, that stores the # of steps for each index. +State: f[i] means if previous steps can reach to i. True/False +Function: f[i] = f[j] && (j + A[j] >= i) +Init: f[0] = true +Answer: f[n-1], if n is the length of A +*/ + +public class Solution { + /** + * @param A: A list of integers + * @return: The boolean answer + **/ + //DP + public boolean canJump(int[] A) { + if (A == null || A.length == 0) { + return false; + } + //By default, boolean[] can is all false + boolean[] can = new boolean[A.length]; + can[0] = true; + for (int i = 1; i < A.length; i++) { + for (int j = 0; j < i; j++) { + if (A[j] && (j + A[j] >= i)) { + can[i] = true; + break; + } + } + } + return can[A.length - 1]; + } +} + + + +/* + +Greedy. Ideas from Yu’s Garden +At each index, check how far we can jump, store this forest-can-jump position in variable ‘farest’. Take max of current farest and (index + A[index]), store is in farest +At each index, compare if ‘farest’ is greater than the end of array, if so, found solution, return true. +At each index, also check if ‘farest == current index’, that means the farest we can move is to current index and we cannot move forward. Then return false. +*/ + +public class Solution { + /** + * @param A: A list of integers + * @return: The boolean answer + **/ + + public boolean canJump(int[] A) { + if (A == null || A.length == 0) { + return false; + } + int farest = 0; + for (int i = 0; i < A.length; i++) { + farest = Math.max(farest, i + A[i]); + if (farest > A.length - 1) { + return true; + } + if (farest == i) { + return false; + } + } + return true; + } +} + diff --git a/Java/Kth Largest Element.java b/Others/old records/LintCode-Backup/Kth Largest Element.java similarity index 100% rename from Java/Kth Largest Element.java rename to Others/old records/LintCode-Backup/Kth Largest Element.java diff --git a/Others/old records/LintCode-Backup/Kth Smallest Sum In Two Sorted Arrays.java b/Others/old records/LintCode-Backup/Kth Smallest Sum In Two Sorted Arrays.java new file mode 100644 index 0000000..1f14c84 --- /dev/null +++ b/Others/old records/LintCode-Backup/Kth Smallest Sum In Two Sorted Arrays.java @@ -0,0 +1,94 @@ +挺勇猛. 还好网上有个题目是找kth最大。 +用priority queue. 每次把最小的展开,移位。分别x+1,或者y+1。 +因为当下的Min里面x,y都是最小的。所以下一个最小的不是(x+1,y),就是(x,y+1)。当然,放在PriorityQueue里面的原因就是,很可能跟之前在queue里面的pair产生比较。 +每次就poll()一个,放新candidate进去就好了。 +注意,这样的做法会用重复,比如例子(7,4)会出现两次。用一个HashSet挡一下。 + +学会用priorityqueue. + +注意,HashSet的唯一性,用一个"x,y"的string就可以代为解决。 +``` +/* +Given two integer arrays sorted in ascending order and an integer k. Define sum = a + b, where a is an element from the first array and b is an element from the second one. Find the kth smallest sum out of all possible sums. + +Example +Given [1, 7, 11] and [2, 4, 6]. + +For k = 3, return 7. + +For k = 4, return 9. + +For k = 8, return 15. + +Challenge +Do it in either of the following time complexity: + +1. O(k log min(n, m, k)). where n is the size of A, and m is the size of B. +2. O( (m + n) log maxValue). where maxValue is the max number in A and B. +Tags Expand +Heap Priority Queue Sorted Matrix + +*/ +//NOT DONE +/* + Thoughts: + Inspired by:http://stackoverflow.com/questions/5212037/find-the-pair-across-2-arrays-with-kth-largest-sum + + User a priority queue and sort based on the smallest sum + Add k-1 times into the heap. Each time poll the smallest and expand. + Finally poll the top of the heap, which will be the smallest + + Note: There will be duplicates,so use a hashstet to mark duplicates. Becareful with what we put int hashset. +*/ +public class Solution { + public class Point{ + int x,y, val; + public Point(int x, int y, int val) { + this.x = x; + this.y = y; + this.val = val; + } + } + + public int kthSmallestSum(int[] A, int[] B, int k) { + if (A == null || B == null || A.length == 0 || B.length == 0 || k < 0) { + return -1; + } + PriorityQueue queue = new PriorityQueue(2, new Comparator(){ + public int compare(Point p1, Point p2) { + return p1.val - p2.val; + } + }); + HashSet set = new HashSet(); + Point min = new Point(0, 0, A[0] + B[0]); + queue.offer(min); + set.add(min.x + "," + min.y); + + int n = A.length; + int m = B.length; + + for (int i = 0; i < k - 1; i++) { + min = queue.poll(); + + if (min.x + 1 < n) { + Point newP = new Point(min.x + 1, min.y, A[min.x + 1] + B[min.y]); + if (!set.contains(newP.x + "," + newP.y)) { + set.add(newP.x + "," + newP.y); + queue.offer(newP); + } + } + if (min.y + 1 < m) { + Point newP = new Point(min.x, min.y + 1, A[min.x] + B[min.y + 1]); + if (!set.contains(newP.x + "," + newP.y)) { + set.add(newP.x + "," + newP.y); + queue.offer(newP); + } + } + } + + min = queue.poll(); + return min.val; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Largest Number.java b/Others/old records/LintCode-Backup/Largest Number.java new file mode 100644 index 0000000..c88247c --- /dev/null +++ b/Others/old records/LintCode-Backup/Largest Number.java @@ -0,0 +1,64 @@ +/* +Given a list of non negative integers, arrange them such that they form the largest number. + +Example +Given [1, 20, 23, 4, 8], the largest formed number is 8423201. + +Note + The result may be very large, so you need to return a string instead of an integer. + +Tags Expand +Sort + +Thoughts: +Use a comparator with String.comareTo, then uset Arrays.sort(...) + +*/ + +class CustomComparator implements Comparator { + public int compare(String s1, String s2) { + return (s2 + s1).compareTo(s1 + s2); + } +} +public class Solution { + /** + *@param num: A list of non negative integers + *@return: A string + */ + public String largestNumber(int[] num) { + if (num == null || num.length == 0) { + return ""; + } + String[] strs = new String[num.length]; + for (int i = 0; i < num.length; i++) { + strs[i] = num[i] + ""; + } + Arrays.sort(strs, new CustomComparator()); + StringBuffer sb= new StringBuffer(); + for (int i = 0; i < num.length; i++) { + sb.append(strs[i]); + } + String rst = sb.toString(); + if (rst.charAt(0) == '0') { + return "0"; + } + return rst; + } +} + + + + + + + + + + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Largest Rectangle in Histogram.java b/Others/old records/LintCode-Backup/Largest Rectangle in Histogram.java new file mode 100644 index 0000000..4f2f6a8 --- /dev/null +++ b/Others/old records/LintCode-Backup/Largest Rectangle in Histogram.java @@ -0,0 +1,37 @@ +/* +Example +Given height = [2,1,5,6,2,3], +return 10. + +Tags Expand +Array Stack + +Thinking Process: +///TODO: missing thinking process for Largest Rectangle in Histogram + +*/ + +public class Solution { + /** + * @param height: A list of integer + * @return: The area of largest rectangle in the histogram + */ + public int largestRectangleArea(int[] height) { + if (height == null || height.length == 0) { + return 0; + } + Stack stack = new Stack(); + int max = 0; + for (int i = 0; i <= height.length; i++) { + int current = (i == height.length) ? -1 : height[i]; + while (!stack.empty() && current <= height[stack.peek()]) { + int h = height[stack.pop()]; + int w = stack.empty() ? i : i - stack.peek() - 1; + max = Math.max(max, w * h); + } + stack.push(i); + } + return max; + } +} + diff --git a/Others/old records/LintCode-Backup/Last Position of Target.java b/Others/old records/LintCode-Backup/Last Position of Target.java new file mode 100644 index 0000000..236c6f0 --- /dev/null +++ b/Others/old records/LintCode-Backup/Last Position of Target.java @@ -0,0 +1,64 @@ +``` +有重复,不是末尾点,继续binary search +/* +Find the last position of a target number in a sorted array. Return -1 if target does not exist. + +Example +Given [1, 2, 2, 4, 5, 5]. + +For target = 2, return 2. + +For target = 5, return 5. + +For target = 6, return -1. + +Tags Expand +Binary Search +*/ + +/* + Thoughts: + Regular binary search for it. + found condition: A[mid] == target && A[mid + 1] != target + +*/ +public class Solution { + /** + * @param A an integer array sorted in ascending order + * @param target an integer + * @return an integer + */ + public int lastPosition(int[] A, int target) { + if (A == null || A.length == 0) { + return -1; + } + int start = 0; + int end = A.length - 1; + int mid; + + while(start + 1 < end) { + mid = start + (end - start)/2; + if (A[mid] == target) { + if (mid + 1 < A.length && A[mid + 1] == target) { + start = mid; + } else { + return mid; + } + } else if (A[mid] < target) { + start = mid; + } else { + end = mid; + } + } + + if (A[end] == target) { + return end; + } else if (A[start] == target) { + return start; + } + + return -1; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Length of Last Word.java b/Others/old records/LintCode-Backup/Length of Last Word.java new file mode 100644 index 0000000..4f478bb --- /dev/null +++ b/Others/old records/LintCode-Backup/Length of Last Word.java @@ -0,0 +1,38 @@ +/* +Given a string s consists of upper/lower-case alphabets and empty space characters ' ', return the length of last word in the string. + +If the last word does not exist, return 0. + +Example +Given s = "Hello World", return 5. + +Note +A word is defined as a character sequence consists of non-space characters only. + +Tags Expand +String + +Thoughts: +1. Split by space +2. return last word's length + +Note: Java split: have to add '\\' in order to pass the key word. +*/ + + + +public class Solution { + /** + * @param s A string + * @return the length of last word + */ + public int lengthOfLastWord(String s) { + if (s == null || s.length() == 0) { + return 0; + } + String[] arr = s.split("\\ "); + String lastWord = arr[arr.length - 1]; + + return lastWord.length(); + } +} diff --git a/Others/old records/LintCode-Backup/Letter Combinations of a Phone Number.java b/Others/old records/LintCode-Backup/Letter Combinations of a Phone Number.java new file mode 100644 index 0000000..bbda83e --- /dev/null +++ b/Others/old records/LintCode-Backup/Letter Combinations of a Phone Number.java @@ -0,0 +1,150 @@ +/* +Given a digit string, return all possible letter combinations that the number could represent. + +A mapping of digit to letters (just like on the telephone buttons) is given below. + +Cellphone. Picture:http://www.lintcode.com/en/problem/letter-combinations-of-a-phone-number/ + +Example +Given "23" + +Return ["ad", "ae", "af", "bd", "be", "bf", "cd", "ce", "cf"] + +Note +Although the above answer is in lexicographical order, your answer could be in any order you want. + +Tags Expand +String Backtracking Recursion Facebook Uber +*/ + +/* + Thoughts: have done this on Leetcode. + map integer to letters + combination of existing letters (by pressing fist number) with next number's letters. + put combinations into queue, reuse the queue. + finally, output into arraylist + + NON-recursive/iterative: use a queue. (Done this one Leetcode) + + This time, use recursive: + pass along rst, list, level number, digits, + for (combine list with all next level's candidates, map) + when level number == digits.length(), return the candidate into rst. +*/ +public class Solution { + /** + * @param digits A digital string + * @return all posible letter combinations + */ + public ArrayList letterCombinations(String digits) { + ArrayList rst = new ArrayList(); + if (digits == null || digits.length() == 0) { + return rst; + } + ArrayList map = new ArrayList(); + map.add(new String[]{});//key 0: nothing + map.add(new String[]{});//key 1: nothing + map.add(new String[]{"a","b","c"}); + map.add(new String[]{"d","e","f"}); + map.add(new String[]{"g","h","i"}); + map.add(new String[]{"j","k","l"}); + map.add(new String[]{"m","n","o"}); + map.add(new String[]{"p","q","r","s"}); + map.add(new String[]{"t","u","v"}); + map.add(new String[]{"w","x","y","z"}); + + ArrayList list = new ArrayList(); + helper(rst, list, map, digits, 0); + + return rst; + } + + public void helper(ArrayList rst, ArrayList list, + ArrayList map, String digits, int level){ + //If level is finished, compress into string + if (level == digits.length()) { + StringBuffer sb = new StringBuffer(); + for (String s : list) { + sb.append(s); + } + rst.add(sb.toString()); + return; + } + //For a specific list of candidates, face the level of chars + int num = Integer.parseInt(digits.substring(level, level + 1)); + String[] strs = map.get(num); + + for (int i = 0; i < strs.length; i++) { + list.add(strs[i]); + helper(rst, list, map, digits, level + 1); + list.remove(list.size() - 1); + } + } + +} + + + + + + + +//Iterative: +//Use 1 queue +// and optimize a bit +public class Solution { + public List letterCombinations(String digits) { + List rst = new ArrayList(); + if (digits == null || digits.length() == 0) { + return rst; + } + HashMap map = new HashMap(); + map.put(2, "abc");map.put(3, "def"); + map.put(4, "ghi");map.put(5, "jkl");map.put(6, "mno"); + map.put(7, "pqrs");map.put(8,"tuv");map.put(9,"wxyz"); + + Queue queue = new LinkedList(); + + //init + int index = 0; + int digit = Integer.parseInt(digits.substring(index, index + 1)); + String keys = map.get(digit); + index++; + + for (int i = 0; i < keys.length(); i++) { + queue.offer(keys.substring(i,i+1)); + } + int size = queue.size(); + + while (index < digits.length() && !queue.isEmpty()) { + String str = queue.poll(); + digit = Integer.parseInt(digits.substring(index, index + 1)); + keys = map.get(digit); + for (int i = 0; i < keys.length(); i++) { + queue.offer(str + keys.substring(i,i+1)); + } + size--; + if (size == 0 && index < digits.length() - 1) { + index++; + size = queue.size(); + } + }//end while + + while (!queue.isEmpty()) { + rst.add(queue.poll()); + } + + return rst; + } +} + + + + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Linked List Cycle II.java b/Others/old records/LintCode-Backup/Linked List Cycle II.java new file mode 100644 index 0000000..a4501f5 --- /dev/null +++ b/Others/old records/LintCode-Backup/Linked List Cycle II.java @@ -0,0 +1,115 @@ +HashMap很简单就做了。 + + +O(1)要首先break while loop when there is a slow==fast +然后,然后就有个我不懂得地方: + +当head == slow.next时候, head就是cycle starting point. +也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +这个可能要写一写,装一装,证明证明才行...不是特别清楚。 +``` +/* +Given a linked list, return the node where the cycle begins. + +If there is no cycle, return null. + +Example +Given -21->10->4->5, tail connects to node index 1,return 10 + +Challenge +Follow up: + +Can you solve it without using extra space? + +Tags Expand +Two Pointers Linked List + +*/ + +/** + * Definition for ListNode. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int val) { + * this.val = val; + * this.next = null; + * } + * } + */ +/* + + Thoughts: 12.10.2015 + Note sure why Lintcode says it's hard. Now try to do both: + 1. HashMap + 2. Slow/Fast pointer + + Note: The linkNode is not necessary unique. They can have diffeent values, but as long as they are + not on same physical address, no cycle. + That is: it's all about index, rather than the value. + + SO: if we do hashmap, we are hasing the entire ListNode object, so whenever there is duplicate, + that is a match. + + Rather: if we do slow/fast pointer, +*/ + +//HashMap +public class Solution { + public ListNode detectCycle(ListNode head) { + if (head == null) { + return null; + } + + HashMap map = new HashMap();//Does not matter of the value + while (head != null) { + if (map.containsKey(head)) { + return head; + } else { + map.put(head, head.val); + } + head = head.next; + } + + return null; + } +} + + +//Slow/Fast Pointer +//http://www.jiuzhang.com/solutions/linked-list-cycle-ii/ +/* + 1. just like in Linked List Cycle. Keep looking. If found a slow==fast, break the 1st while loop. + + 2. At that moment, the slow is not the cycle starting point. We need to look for it + There must be some proof within the 2nd step, which i dont know. SO, need sort of remember it: + When head == slow.next, then head is the cycle starting point .... + (not exactly sure why. I guess this is why it's a hard question) + +*/ +public class Solution { + public ListNode detectCycle(ListNode head) { + if (head == null) { + return null; + } + ListNode slow = head; + ListNode fast = head.next; + while (slow != fast) { + if (fast == null || fast.next == null) { + return null; + } + slow = slow.next; + fast = fast.next.next; + } + + while (head != slow.next) { + slow = slow.next; + head = head.next; + } + + return head; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Linked List Cycle.java b/Others/old records/LintCode-Backup/Linked List Cycle.java new file mode 100644 index 0000000..5a87d8d --- /dev/null +++ b/Others/old records/LintCode-Backup/Linked List Cycle.java @@ -0,0 +1,96 @@ +O(1) sapce: 用快慢指针。一个跑.next, 一个跑.next.next。 总有一次,fast会因为cycle而追上slow。 +那个时候其实slow.val = fast.val. + +O(n):用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle le +``` +/* +50% Accepted +Given a linked list, determine if it has a cycle in it. + + + +Example +Challenge +Follow up: +Can you solve it without using extra space? + +Tags Expand +Linked List Two Pointers + +*/ + + +/** + * Definition for ListNode. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int val) { + * this.val = val; + * this.next = null; + * } + * } + */ + +/* + REDO 12.10.2015 + Rulese for fast/slow pointer: + 1. move slow and fast, + 2. See if the object address matches + + IMPORTANT: do not compare the value, because they didn't say value has to be unique. + Even with same value, the ListNode object can be different at different index. +*/ + +public class Solution { + public boolean hasCycle(ListNode head) { + if (head == null || head.next == null) { + return false; + } + ListNode slow = head; + ListNode fast = head.next; + + while (slow != fast) {//Compare their reference address + if (fast == null || fast.next == null) {//travel till the end + return false;//no cycle + } + slow = slow.next; + fast = fast.next.next; + } + //If slow==fast and breaks the while loop, then there must be a cycle + return true; + } +} + + +/* +Thinking process: +If there is a cycle, then slow node and fast node will meet at some point. + + +However, we cannot simply compare slow.val == fast.val. +This is because, there can be different link node with same value, but they are stored on different index, +so they should not be cycle. + +The below passes the test case in lintcode, which is wrong. + +public class Solution { + public boolean hasCycle(ListNode head) { + if (head == null) { + return false; + } + ListNode slow = head; + ListNode fast = head.next; + while (fast != null && fast.next != null) { + if (slow.val == fast.val) { + return true; + } + slow = slow.next; + fast = fast.next.next; + } + return false; + } +} +*/ + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Longest Common Prefix.java b/Others/old records/LintCode-Backup/Longest Common Prefix.java new file mode 100644 index 0000000..c029d70 --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Common Prefix.java @@ -0,0 +1,65 @@ +/* +Given k strings, find the longest common prefix (LCP). + +Example +For strings "ABCD", "ABEF" and "ACEF", the LCP is "A" + +For strings "ABCDEFG", "ABCEFG" and "ABCEFA", the LCP is "ABC" + +Tags Expand +Enumeration Basic Implementation String LintCode Copyright + +Thoughts: +1. Continuous while loop until something breaks out. +2. Str to record the longest prefix +3. for loop on each while-loop, based on strs[0] + Check if index exist + check if all other strs have same char at this point. If so, add it. If not, break. + +Note: +Arrays: use strs.length +String: use str.length(). +Odd, right? I know ... dislike. +String seems to be supirior, it's a god damn object, and we have a method for checking string length. +For array, well, looks like it's been mistreated ... we are only reading a length property of the array object. + +Note2: +Ask for border case: when only 1 string, longest prefix turns out it's the strs[0] itself. +*/ + + +public class Solution { + /** + * @param strs: A list of strings + * @return: The longest common prefix + */ + public String longestCommonPrefix(String[] strs) { + if (strs == null || strs.length == 0) { + return ""; + } + if (strs.length == 1) { + return strs[0]; + } + String prefix = ""; + int ind = 0; + while (ind < strs[0].length()) { + char c = strs[0].charAt(ind); + boolean valid = false; + for (int i = 1; i < strs.length; i++) { + if (strs[i].length() > ind && strs[i].charAt(ind) == c) { + valid = true; + } else { + valid = false; + break; + } + } + if (valid) { + prefix += "" + c; + } else { + break; + } + ind++; + }//END WHILE + return prefix; + } +} \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Longest Common Subsequence.java b/Others/old records/LintCode-Backup/Longest Common Subsequence.java new file mode 100644 index 0000000..f423e2f --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Common Subsequence.java @@ -0,0 +1,53 @@ +/* +Given two strings, find the longest comment subsequence (LCS). + +Your code should return the length of LCS. + +Example +For "ABCD" and "EDCA", the LCS is "A" (or D or C), return 1 + +For "ABCD" and "EACB", the LCS is "AC", return 2 + +Clarification +What's the definition of Longest Common Subsequence? + + * The longest common subsequence (LCS) problem is to find the longest subsequence common to all sequences in a set of sequences (often just two). (Note that a subsequence is different from a substring, for the terms of the former need not be consecutive terms of the original sequence.) It is a classic computer science problem, the basis of file comparison programs such as diff, and has applications in bioinformatics. + + * https://en.wikipedia.org/wiki/Longest_common_subsequence_problem + +Tags Expand +LintCode Copyright Longest Common Subsequence Dynamic Programming + +Thinking process: +Using DP. +check[i][j] means: the length of longest common subsequnce between A(0 ~ i) and B(0 ~ j). +Then there are two ways to reach check[i][j]: +1. A(i-1) == B(j - 1), then check[i][j] = check[i - 1][j - 1] + 1; +2. A(i-1) != B(j - 1), then pick the max between (i-1,j) , (i,j-1) and (i, j ) +Note: check[][] is initialized with all 0's. Index (0,0) is used as starting 0. +*/ +public class Solution { + /** + * @param A, B: Two strings. + * @return: The length of longest common subsequence of A and B. + */ + public int longestCommonSubsequence(String A, String B) { + if (A == null || B == null || A.length() == 0 || B.length() == 0) { + return 0; + } + int[][] check = new int[A.length() + 1][B.length() + 1]; + for (int i = 1; i <= A.length(); i++) { + for (int j = 1; j <= B.length(); j++) { + if (A.charAt(i - 1) == B.charAt(j - 1)) { + check[i][j] = check[i - 1][j - 1] + 1; + } else { + check[i][j] = Math.max(check[i][j], check[i - 1][j]); + check[i][j] = Math.max(check[i][j], check[i][j - 1]); + } + } + } + return check[A.length()][B.length()]; + } +} + + diff --git a/Others/old records/LintCode-Backup/Longest Common Substring.java b/Others/old records/LintCode-Backup/Longest Common Substring.java new file mode 100644 index 0000000..62d320c --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Common Substring.java @@ -0,0 +1,56 @@ +/* +Given two strings, find the longest common substring. + +Return the length of it. + +Example +Given A = "ABCD", B = "CBCE", return 2. + +Note +The characters in substring should occur continuously in original string. This is different with subsequence. + +Challenge +O(n x m) time and memory. + +Tags Expand +LintCode Copyright Longest Common Subsequence Dynamic Programming + +Thoughts: +1. Compare all i X j. +2. Use a D[i][j] to mark the amount of common substring based on D[i - 1][j -1]. Could be 0. +3. track max length + +NOTE1: create 2D array that's [N + 1][M + 1] because we want to hold D[n][M] in the 2d array +NOTE2: be carefule with init index 0's + +*/ + + +public class Solution { + /** + * @param A, B: Two string. + * @return: the length of the longest common substring. + */ + public int longestCommonSubstring(String A, String B) { + if (A == null || B == null || A.length() == 0 || B.length() == 0) { + return 0; + } + int [][] D = new int[A.length() + 1][B.length() + 1]; + int max = 0; + for (int i = 0; i <= A.length(); i++) { + for(int j = 0; j <= B.length(); j++) { + if (i == 0 || j == 0) { + D[i][j] = 0; + } else { + if (A.charAt(i - 1) == B.charAt(j - 1)) { + D[i][j] = D[i - 1][j - 1] + 1; + } else { + D[i][j] = 0; + } + max = Math.max(max, D[i][j]); + } + } + } + return max; + } +} diff --git a/Others/old records/LintCode-Backup/Longest Consecutive Sequence.java b/Others/old records/LintCode-Backup/Longest Consecutive Sequence.java new file mode 100644 index 0000000..19f3b02 --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Consecutive Sequence.java @@ -0,0 +1,99 @@ +/* +Given an unsorted array of integers, find the length of the longest consecutive elements sequence. + +For example, +Given [100, 4, 200, 1, 3, 2], +The longest consecutive elements sequence is [1, 2, 3, 4]. Return its length: 4. + +Your algorithm should run in O(n) complexity. + +Hide Tags Array + +Thinking process: +0. This problem can be done using sorting, but time complexity of sorting is O(nlogn). This problem requires O(n). +1. Want to check if a number's left and right is consecutive to itself, but cannot do it due to the given unsorted array: think about a Hashmap. +2. HashMap(Key, Value) = (the number itself, boolean: have been counted or not). If you count a number as a consecutive, you only need to count it once. +3. How HashMap works: + when checking a number's consecutive, look at number--, number++, see if they are in the HashMap. If exist, means consecutive. + If a number exist in the hashmap and its value is 'true', then we need to skip this number beacuse it has been checked. +4. Track the total number consecutives of 1 perticular number, compare it with the maxL. Save the Math.max to maxL. +5. Depending on the problem, we can store a consecutive sequence or simply just its length: maxL. This problem wants the maxL. +*/ + +public class Solution { + public int longestConsecutive(int[] num) { + if (num == null || num.length == 0) { + return 0; + } + int maxL = 1; + HashMap history = new HashMap(); + for (int i : num) { + history.put(i, false); + } + for (int i : num) { + if (history.get(i)) { + continue; + } + //check ++ side + int temp = i; + int total = 1; + while (history.containsKey(temp + 1)) { + total++; + temp++; + history.put(temp, true); + } + //check -- side + temp = i; + while (history.containsKey(temp - 1)) { + total++; + temp--; + history.put(temp, true); + } + maxL = Math.max(maxL, total); + } + return maxL; + } +} + + + +/* +10.19.2015 +Thougths: +1. sort +2. use a 'count' and 'max' to keep track of consecutive elements +3. one-pass + +Note: +Take care of equal numbers: skip/continue those + +*/ + +public class Solution { + /** + * @param nums: A list of integers + * @return an integer + */ + public int longestConsecutive(int[] num) { + if (num == null || num.length == 0) { + return 0; + } + if (num.length == 1) { + return 1; + } + int count = 1; + int max = 1; + Arrays.sort(num); + for (int i = 1; i < num.length; i++) { + if (num[i - 1] == num[i]) { + continue; + } else if (num[i - 1] + 1 == num[i]) { + count++; + max = Math.max(count, max); + } else { + count = 1; + } + } + return max; + } +} diff --git a/Others/old records/LintCode-Backup/Longest Increasing Continuous subsequence II.java b/Others/old records/LintCode-Backup/Longest Increasing Continuous subsequence II.java new file mode 100644 index 0000000..f8b6ecf --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Increasing Continuous subsequence II.java @@ -0,0 +1,84 @@ +O(mn) space for dp and flag. +O(mn) runtime because each spot will be marked once visited. +这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 +``` +/* +Give you an integer matrix (with row size n, column size m),find the longest increasing continuous subsequence in this matrix. (The definition of the longest increasing continuous subsequence here can start at any row or column and go up/down/right/left any direction). + +Example +Given a matrix: + +[ + [1 ,2 ,3 ,4 ,5], + [16,17,24,23,6], + [15,18,25,22,7], + [14,19,20,21,8], + [13,12,11,10,9] +] +return 25 + +Challenge +O(nm) time and memory. + +Tags Expand +Dynamic Programming +*/ + +/* +Thoughts: +Similar to JiuZhang's DP in longest increasing continuous subsequence I. +Imagining values are coming from 4 directions so A[i][j] < A[dx][dy]. +State: +DP[i][j]: coming from 4 different directions, the longest we can get on DP[i][j]. +Use flag[i][j] to mark. 1: checked, return dp[i][j] directly; -1: marked, don't over going that direction +Fn: +Go 4 direcetions dfs. +Init: +Can init during dfs by giving a default value of 1 to each spot. +*/ +public class Solution { + /** + * @param A an integer matrix + * @return an integer + */ + public int longestIncreasingContinuousSubsequenceII(int[][] A) { + if (A == null || A.length == 0 || A[0].length == 0) { + return 0; + } + int n = A.length; + int m = A[0].length; + int[][] dp = new int[n][m]; + int[][] flag = new int[n][m]; + int ans = 0; + for (int i = 0; i < n; i++) { + for (int j = 0; j < m; j++) { + dp[i][j] = dfs(A, dp, flag, i, j, n, m); + ans = Math.max(ans, dp[i][j]); + } + } + return ans; + } + + public int dfs(int[][] A, int[][] dp, int[][] flag, int i, int j, int n, int m) { + if (flag[i][j] == 1) { + return dp[i][j]; + } + int ans = 1; + flag[i][j] = -1; + int[] dx = {0, 0, 1, -1}; + int[] dy = {1, -1, 0, 0}; + //Go 4 directions + for (int k = 0; k < dx.length; k++) { + int id = i + dx[k]; + int jd = j + dy[k]; + if (id >= 0 && id < n && jd >= 0 && jd < m && A[i][j] < A[id][jd]) { + ans = Math.max(ans, dfs(A, dp, flag, id, jd, n, m) + 1); + } + } + flag[i][j] = 1; + dp[i][j] = ans; + return dp[i][j]; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Longest Increasing Continuous subsequence.java b/Others/old records/LintCode-Backup/Longest Increasing Continuous subsequence.java new file mode 100644 index 0000000..24c7255 --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Increasing Continuous subsequence.java @@ -0,0 +1,163 @@ +O(n)跑2遍for. +O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. +特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 +``` +/* +Give you an integer array (index from 0 to n-1, where n is the size of this array),find the longest increasing continuous subsequence in this array. (The definition of the longest increasing continuous subsequence here can be from right to left or from left to right) +Example +For [5, 4, 2, 1, 3], the LICS is [5, 4, 2, 1], return 4. +For [5, 1, 2, 3, 4], the LICS is [1, 2, 3, 4], return 4. +Note +O(n) time and O(1) extra space. +Tags Expand +Dynamic Programming Array +*/ +/* +OPTS: O(n) time and O(1) space +Thoughts: not quite DP yet, but used 1 leftRun/rightRun to store status, sort of DP. +*/ +public class Solution { + /** + * @param A an array of Integer + * @return an integer + */ + public int longestIncreasingContinuousSubsequence(int[] A) { + if (A == null || A.length == 0) { + return 0; + } + int leftRun = 1; + int rightRun = 1; + int ans = 1; + for (int i = 1; i < A.length; i++) { + if (A[i] > A[i - 1]) { + leftRun++; + } else { + leftRun = 1; + } + ans = Math.max(ans, leftRun); + } + for (int i = A.length - 2; i >= 0; i--) { + if (A[i] > A[i + 1]) { + rightRun++; + } else { + rightRun = 1; + } + ans = Math.max(ans, rightRun); + } + return ans; + } +} +``` + + +九章的DP,没有用O(1)space,但是应该是为这道题的followup做准备的模式。 +用dp和dfs. +每次dfs左右时都要mark一下flag,防止重跑 +``` +/* +Thoughts: JiuZhang's DP. +dp[i]: longest increasing continous subsequence on i, regardless if the sequence is incresing from left or right. +Use flag[i] to mark if a position has been visited. If visited (flag == 1), then immediately return dp[i] since we've calculated this before. +Note. Also mark flag[i] == -1 in each dfs interation, to prevent redudant looping (infinite loop) + +Stackover flow at 96%. +*/ +public class Solution { + /** + * @param A an array of Integer + * @return an integer + */ + public int longestIncreasingContinuousSubsequence(int[] A) { + if (A == null || A.length == 0) { + return 0; + } + int[] dp = new int[A.length]; + int[] flag = new int[A.length]; + int ans = 0; + for (int i = 0; i < A.length; i++) { + dp[i] = dfs(A, dp, flag, i); + ans = Math.max(ans, dp[i]); + } + return ans; + } + + public int dfs(int[] A, int[] dp, int[] flag, int i){ + if (flag[i] == 1) { + return dp[i]; + } + int ansLeft = 1; + int ansRight = 1; + flag[i] = -1; + //Increasing from left->right + if (i - 1 >= 0 && A[i - 1] < A[i] && flag[i - 1] != -1) { + ansLeft = dfs(A, dp, flag, i - 1) + 1; + } + //Increasing from right->left + if (i + 1 < A.length && A[i] > A[i + 1] && flag[i + 1] != -1) { + ansRight = dfs(A, dp, flag, i + 1) + 1; + } + flag[i] = 1; + dp[i] = Math.max(ansLeft, ansRight); + return dp[i]; + } +} + + + + +``` + + + + + + + +老码 +``` +/* +Older approach. Longer code :( +Thoughts: +1. Reverse search for the same int[] A. + In 1,2 keep track of maxLength1. maxLength2 +2. Compare maxLength, and use the largest +Note: +After for loop, need to catch the very last comparison, to get result of track ;) + +*/ +//[5,4,2,1,3] +public class Solution { + /** + * @param A an array of Integer + * @return an integer + */ + public int longestIncreasingContinuousSubsequence(int[] A) { + if (A == null || A.length == 0) { + return -1; + } + int maxLength1 = 0; + int maxLength2 = 0; + int track = 1; + for (int i = 1; i < A.length; i++) { + if (A[i] > A[i - 1]) { + track++; + } else { + maxLength1 = track > maxLength1 ? track : maxLength1; + track = 1; + } + } + maxLength1 = track > maxLength1 ? track : maxLength1; + track = 1; + for (int i = A.length - 2; i >= 0; i--) { + if (A[i] > A[i + 1]) { + track++; + } else { + maxLength2 = track > maxLength2 ? track : maxLength2; + track = 1; + } + } + maxLength2 = track > maxLength2 ? track : maxLength2; + return maxLength1 > maxLength2 ? maxLength1 : maxLength2; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Longest Increasing Subsequence.java b/Others/old records/LintCode-Backup/Longest Increasing Subsequence.java new file mode 100644 index 0000000..1299675 --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Increasing Subsequence.java @@ -0,0 +1,90 @@ +i位和之前的0~i-1 都远关系。复杂一点。 +每次都考虑o~i的所有情况。所以double for loop +``` +/* +Given a sequence of integers, find the longest increasing subsequence (LIS). + +You code should return the length of the LIS. + +Example +For [5, 4, 1, 2, 3], the LIS is [1, 2, 3], return 3 + +For [4, 2, 4, 5, 3, 7], the LIS is [4, 4, 5, 7], return 4 + +Challenge +Time complexity O(n^2) or O(nlogn) + +Clarification +What's the definition of longest increasing subsequence? + + * The longest increasing subsequence problem is to find a subsequence of a given sequence in which the subsequence's elements are in sorted order, lowest to highest, and in which the subsequence is as long as possible. This subsequence is not necessarily contiguous, or unique. + + * https://en.wikipedia.org/wiki/Longest_common_subsequence_problem + +Tags Expand +Binary Search LintCode Copyright Dynamic Programming +*/ + +/* + Thoughts: + dp[i] depends on not only dp[i-1], but also [i-1] ...[0]. + So it has to be double-for loop. + Each sub-for loop on i, traverse 0 ~ j(j<=i) to find largest number to put on dp[i] + fn: + dp[i] = dp[i] > dp[j] + 1 ? dp[i] : dp[j] + 1 + init: + dp[i] initally all = 1. (i = 0 ~ n). If no other number meets the requirement, at least it has itself. + Result: + dp[n - 1] + + Note: nums[j] <= nums[i] is the 'increasing' requirement + dp[j] + 1 means: best we can do at dp[j] + 1, is this better than what we already have on dp[i]? + +*/ + + +public class Solution { + /** + * @param nums: The integer array + * @return: The length of LIS (longest increasing subsequence) + */ + public int longestIncreasingSubsequence(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int n = nums.length; + int[] dp = new int[n]; + int max = 0; + for (int i = 0; i < n; i++) { + dp[i] = 1; + for (int j = 0; j < i; j++) { + if (nums[j] <= nums[i]){ + dp[i] = dp[i] > dp[j] + 1 ? dp[i] : dp[j] + 1; + } + } + if (dp[i] > max) { + max = dp[i]; + } + } + return max; + } +} + + + + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Longest Palindromic Substring.java b/Others/old records/LintCode-Backup/Longest Palindromic Substring.java new file mode 100644 index 0000000..9093384 --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Palindromic Substring.java @@ -0,0 +1,46 @@ +/* +Given a string S, find the longest palindromic substring in S. You may assume that the maximum length of S is 1000, and there exists one unique longest palindromic substring. + +Example +Given the string = "abcdzdcab", return "cdzdc". + +Challenge +O(n2) time is acceptable. Can you do it in O(n) time. + +Tags Expand +String +*/ +/* + O(n) way, not done yet +*/ + + +/* + O(n^2) + Thoughts: + Like Palindrome Partioning II, try to use isPal[i][j] to verify each string (i,j). + If string(i,j) is valid, note down the (i,j) portion and find the longest. + This is a standard O(n^2) process +*/ +public class Solution { + /** + * @param s input string + * @return the longest palindromic substring + */ + public String longestPalindrome(String s) { + if (s == null || s.length() == 0) { + return s; + } + boolean isPal[][] = new boolean[s.length()][s.length()]; + String maxStr = ""; + for (int j = 0; j < s.length(); j++) { + for (int i = 0; i <= j; i++) { + if (s.charAt(i) == s.charAt(j) && (j - i <= 1 || isPal[i + 1][j - 1])) { + isPal[i][j] = true; + maxStr = maxStr.length() > s.substring(i, j + 1).length() ? maxStr : s.substring(i, j + 1); + } + } + }//end for j + return maxStr; + } +} diff --git a/Others/old records/LintCode-Backup/Longest Substring Without Repeating Characters.java b/Others/old records/LintCode-Backup/Longest Substring Without Repeating Characters.java new file mode 100755 index 0000000..bed8632 --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Substring Without Repeating Characters.java @@ -0,0 +1,97 @@ +M + +方法1:只要有non-existing char就count++. 一旦有重复char: + i = 新出现重复Char的位置. + 重新init HashMap, count. + +这个方法每次都把map打碎重来, 是可以的,也没什么不好。就是在for里面改i,自己觉得不太顺.方法二可能顺一点。 + +方法2:用两个pointer, head和i. + head从index 0 开始。若没有重复char, 每次只有for loop的i++。每次取substring[head,i]作为最新的string. + 一旦有重复,那么意味着,从重复的老的那个index要往后加一格开始。所以head = map.get(i) +1. + +注意:head很可能被退回到很早的地方,比如abbbbbba,当遇到第二个a,head竟然变成了 head = 0+1 = 1. 当然这是不对的,所以head要确保一直增长,不回溯。 + + +``` +/* +Given a string, find the length of the longest substring without repeating characters. + +Example +For example, the longest substring without repeating letters for "abcabcbb" is "abc", which the length is 3. + +For "bbbbb" the longest substring is "b", with the length of 1. + +Challenge +O(n) time + +Tags Expand +String Two Pointers Hash Table +*/ + +/* + 02.02.2016 + Attempt2, Thoughts: + HashMap map + int head. + When char re-appear in map, 1. move head to repeating char's index + 1, 2. renew map with current index + + Note: head could repeat in earlier index, so make sure head does not travel back +*/ + +public class Solution { + public int lengthOfLongestSubstring(String s) { + if (s == null || s.length() == 0) { + return 0; + } + HashMap map = new HashMap(); + int head = 0; + int max = 0; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (map.containsKey(c)) { + if (map.get(c) + 1 > head) { + head = map.get(c) + 1; + } + } + map.put(c, i); + String str = s.substring(head, i + 1); + max = Math.max(max, str.length()); + } + return max; + } +} + +/* +Attempt1, Thoughts: +Loop the string, HashSet to store the Character, count++, and use a max to store longest length. +Whenever a char exist in hashset, new hashset and count = 0, set i = 1st duplicate char, followed by i++, now start again. +*/ + +public class Solution { + public int lengthOfLongestSubstring(String s) { + if (s == null || s.length() == 0) { + return 0; + } + HashMap map = new HashMap(); + int count = 0; + int max = 0; + for (int i = 0; i < s.length(); i++) { + if (!map.containsKey(s.charAt(i))) { + map.put(s.charAt(i), i); + count++; + } else { + i = map.get(s.charAt(i)); + map = new HashMap(); + count = 0; + } + max = Math.max(max, count); + }//end for + + return max; + } +} + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Longest Substring with At Most K Distinct Characters.java b/Others/old records/LintCode-Backup/Longest Substring with At Most K Distinct Characters.java new file mode 100644 index 0000000..acce12d --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Substring with At Most K Distinct Characters.java @@ -0,0 +1,74 @@ +大清洗。 +map.size一旦超标,要把longest string最开头(marked by pointer:start)的那个char抹掉,而且要把它所有的appearance都抹掉;这样还不够,它最后一次出现以前的其他所有chars,也都要抹掉。 +大清洗的原因是: 一旦某一个char要被清除,由于substring必须是连续的,所以在它之前的所有chars都要被清洗。 +我去,黑帮大哥除龙头啊。 +简直就是要消灭伏地魔的7个魂器。 +``` +/* +Given a string s, find the length of the longest substring T that contains at most k distinct characters. + +Have you met this question in a real interview? Yes +Example +For example, Given s = "eceba", k = 3, + +T is "eceb" which its length is 4. + +Challenge +O(n), n is the size of the string s. + +Tags Expand +String Two Pointers LintCode Copyright Hash Table +*/ + +/* +Thoughts: +HashMap size cannot go over k. +Map. +Use a start pointer to track the begin of current longest substring. +If map.size() > k, then remove: count-= map.get(s.charAt(i)); map.remove(s.charAt(i)); + +Note: +assume we need to remove the first char of current longest string, and that char has multiple apperances later in the string +Do while on map.size>k, and remove all chars before the last appearce of that particular string. +Therefore, we need erase that particular char and all other chars before that particular char's last apperance. +*/ +public class Solution { + /** + * @param s : A string + * @return : The length of the longest substring + * that contains at most k distinct characters. + */ + public int lengthOfLongestSubstringKDistinct(String s, int k) { + if (s == null || s.length() == 0 || k <= 0) { + return 0; + } + HashMap map = new HashMap(); + int count = 0; + int start = 0; + int max = 0; + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (map.containsKey(c)) { + map.put(c, map.get(c) + 1); + } else { + map.put(c, 1); + while (map.size() > k) { + char rm = s.charAt(start); + int tempCount = map.get(rm); + if (tempCount > 1) { + map.put(rm, tempCount - 1); + } else { + map.remove(rm); + } + start++; + count--; + } + } + count++; + max = Math.max(max, count); + } + return max; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Longest Words.java b/Others/old records/LintCode-Backup/Longest Words.java new file mode 100644 index 0000000..d875ab6 --- /dev/null +++ b/Others/old records/LintCode-Backup/Longest Words.java @@ -0,0 +1,71 @@ +/* +Given a dictionary, find all of the longest words in the dictionary. + +Example +Given + +{ + "dog", + "google", + "facebook", + "internationalization", + "blabla" +} +the longest words are(is) ["internationalization"]. + +Given + +{ + "like", + "love", + "hate", + "yes" +} +the longest words are ["like", "love", "hate"]. + +Challenge +It's easy to solve it in two passes, can you do it in one pass? + +Tags Expand +Enumeration String LintCode Copyright + +Thoughts: +Two pass: 1st, get longest length. 2nd pass, get all words. + +One pass: +1. Use hashmap: > +2. keep track of the longest length + +Review: +Map: put, get +ArrayList: add +We can get a value from map, and change directly on it, if that's an object (basically refer to the original object) +*/ + + +class Solution { + /** + * @param dictionary: an array of strings + * @return: an arraylist of strings + */ + ArrayList longestWords(String[] dictionary) { + if (dictionary == null || dictionary.length == 0) { + return null; + } + HashMap> map = new HashMap>(); + int longestLength = 0; + + for (int i = 0; i < dictionary.length; i++) { + int strLength = dictionary[i].length(); + if (map.containsKey(strLength)) { + map.get(strLength).add(dictionary[i]); + } else { + ArrayList list = new ArrayList(); + list.add(dictionary[i]); + map.put(strLength, list); + } + longestLength = strLength > longestLength ? strLength : longestLength; + } + return map.get(longestLength); + } +}; diff --git a/Others/old records/LintCode-Backup/Lowest Common Ancestor II.java b/Others/old records/LintCode-Backup/Lowest Common Ancestor II.java new file mode 100644 index 0000000..a497eff --- /dev/null +++ b/Others/old records/LintCode-Backup/Lowest Common Ancestor II.java @@ -0,0 +1,138 @@ +1. 曾经做的hashset的优化,找到的都存hashset. exist就return那个duplicate + + +2. 正常做法:2 lists +``` +/* +Lowest Common Ancestor II + +Given the root and two nodes in a Binary Tree. +Find the lowest common ancestor(LCA) of the two nodes. + +The lowest common ancestor is the node with largest depth which is the ancestor of both nodes. + +The node has an extra attribute parent which point to the father of itself. +The root's parent is null. + +Example +For the following binary tree: + + 4 + / \ +3 7 + / \ + 5 6 +LCA(3, 5) = 4 + +LCA(5, 6) = 7 + +LCA(6, 7) = 7 + +Tags Expand +LintCode Copyright Binary Tree +*/ + +/* + Thoughts: + 我之前的做法也是蛮彪悍的,HashSet只存所需要的parent, 其实算是一个优化,更节省空间。 + 12.11.2015. + 今天这个再来实现一个普通的做法,存在两个list里面。有parent的题目本身比没parent更简单。 + 从头遍历两个list. + 1. 一旦分叉,分叉口的parent就是要找的。 + 2. 如果两个list一直相等,那他们就是同一个node + + border case: if both null, just return null. + if only 1 is null, let one of the node be ancestor; since null can be anywhere. +*/ + +public class Solution { + /** + * @param root: The root of the tree + * @param A, B: Two node in the tree + * @return: The lowest common ancestor of A and B + */ + public ParentTreeNode lowestCommonAncestorII(ParentTreeNode root, + ParentTreeNode A,ParentTreeNode B) { + if (root == null || (A == null && B == null)) { + return null; + } else if (A == null || B == null) { + return A == null ? B : A; + } + //Populate listA, listB + ArrayList listA = new ArrayList(); + ArrayList listB = new ArrayList(); + + while (A != root) { + listA.add(0, A); + A = A.parent; + } + listA.add(0, A); + while (B != root) { + listB.add(0, B); + B = B.parent; + } + listB.add(0, B); + + int size = listA.size() > listB.size() ? listB.size() : listA.size(); + + for (int i = 0; i < size; i++) { + if (listA.get(i) != listB.get(i)) { + return listA.get(i).parent; + } + } + + return listA.get(size - 1); + } +} + + +/* + Thoughts: + Try to get upper-level parent, store in hashMap. + First time when the node duplicate in map, that will be the first common parent. +*/ + +/** + * Definition of ParentTreeNode: + * + * class ParentTreeNode { + * public ParentTreeNode parent, left, right; + * } + */ +public class Solution { + /** + * @param root: The root of the tree + * @param A, B: Two node in the tree + * @return: The lowest common ancestor of A and B + */ + public ParentTreeNode lowestCommonAncestorII(ParentTreeNode root, + ParentTreeNode A, + ParentTreeNode B) { + if (root == null || (A == null && B == null)) { + return null; + } else if (A == null || B == null) { + return A == null ? B : A; + } + + HashSet set = new HashSet(); + while (A != null || B != null) { + if (A != null) { + if (set.contains(A)) { + return A; + } + set.add(A); + A = A.parent; + } + if (B != null) { + if (set.contains(B)) { + return B; + } + set.add(B); + B = B.parent; + } + } + return root; + } +} + +``` \ No newline at end of file diff --git a/Java/Lowest Common Ancestor.java b/Others/old records/LintCode-Backup/Lowest Common Ancestor.java similarity index 68% rename from Java/Lowest Common Ancestor.java rename to Others/old records/LintCode-Backup/Lowest Common Ancestor.java index 33c4e51..b8ebf5b 100644 --- a/Java/Lowest Common Ancestor.java +++ b/Others/old records/LintCode-Backup/Lowest Common Ancestor.java @@ -1,3 +1,18 @@ +E + +方法1:O(n) space O(h) time。把两条线binary search出来。找第一个不同的parent. 代码长。 Iterative + +方法2:O(1) sapce O(h). Recursive. 循环的截点是: +当root == null或者 A B 任何一个在findLCA底部被找到了(root== A || root == B),那么就return 这个root. + +三种情况: +1. A,B都找到,那么这个level的node就是其中一层的parent。其实,最先recursively return到的那个,就是最底的LCA parent. +2. A 或者 B 找到,那就还没有公共parent,return 非null得那个。 +3. A B 都null, 那就找错了没有呗, return null + + +``` + /* 33% Accepted Given the root and two nodes in a Binary Tree. Find the lowest common ancestor(LCA) of the two nodes. @@ -35,7 +50,7 @@ Given the root and two nodes in a Binary Tree. Find the lowest common ancestor(L We divide and coquer (in this case DFS) into 2 branches, and we are actually asking each node to check: Do I have a leaf child of nodeA (could be futher down in the tree)? Do I have a leaf child of nodeB (could be futher down in the tree)? - 1. If I have leaf child of A && B, then i'm the deepest parent! Return. + 1. If I have leaf child of A && B, then i'm the deepest parent in line! Return. 2. If I only have A, or B: mark myself as an ancestor of A or B. 3. If I don't have leaf child of A nor B, I'm not an ancestor, failed, return null. @@ -133,3 +148,52 @@ public TreeNode lowestCommonAncestor(TreeNode root, TreeNode A, TreeNode B) { } } } + + +//Extra storage, not a good method: + + //Find two lists of nodes + //First non-common's previous one from two lists, or the end of one list, is the LCA +public class Solution { + public TreeNode lowestCommonAncestor(TreeNode root, TreeNode p, TreeNode q) { + if (root == null || p == null || q == null) { + return root; + } + ArrayList list1 = new ArrayList(); + ArrayList list2 = new ArrayList(); + TreeNode node = root; + list1.add(node); + while (node.val != p.val) { + if (p.val < node.val) { + node = node.left; + } else { + node = node.right; + } + list1.add(node); + } + node = root; + list2.add(node); + while (node.val != q.val) { + if (q.val < node.val) { + node = node.left; + } else { + node = node.right; + } + list2.add(node); + } + + int size = list1.size() < list2.size() ? list1.size() : list2.size(); + node = root; + for (int i = 0; i < size; i++) { + if (list1.get(i) != list2.get(i)) { + break; + } + node = list1.get(i); + } + + return node; + } +} + + +``` diff --git a/Others/old records/LintCode-Backup/Majority Number II.java b/Others/old records/LintCode-Backup/Majority Number II.java new file mode 100644 index 0000000..91b1d96 --- /dev/null +++ b/Others/old records/LintCode-Backup/Majority Number II.java @@ -0,0 +1,83 @@ +/* +Given an array of integers, the majority number is the number that occurs more than 1/3 of the size of the array. + +Find it. + +Note +There is only one majority number in the array + +Example +For [1, 2, 1, 2, 1, 3, 3] return 1 + +Challenge +O(n) time and O(1) space + +Thinking process: +Need to think the relations of 3 parts of the array: +1. Assume a > 1/3, which is the candidate were are looking for + However, only konwing a appears more than 1/3 of the array, does not mean there is no other element appears more than 1/3, for example, aaaaabcccccc, a = 5/12, b = 6/12. The majority is b. +2. Consider another element b, which is a different element rather than a. Discuss the 2 conditions of b. +3. Consider the rest of the array is in set c, which can contain all different elements. + +Discuss relations between a, b, c +Assume a > 1/3 +Case1: b < 1/3 + given: a > 1/3, means b + c < 2/3, known b < 1/3 + get: c < 1/3 + conclusion: a is the majority +Case2: b > 1/3 + given: a + b ? 2/3 + get: c < 1/3 + conclusion: return the greater element# of a or b + +Implementation: +1. Have valA and valB two pointers to represent a and between +2. Check valA against the array to count duplicates, similar as in Majority Number I +3. Check valB against ..... +4. Note: at each index i, only one of valA or valB is checked. That means, we evaluate a and b individually against the section c. +5. At the end, we found 2 candidates: a and b. Now compare the # of a and b to see which is greater. +*/ + +public class Solution { + /** + * @param nums: A list of integers + * @return: The majority number that occurs more than 1/3 + */ + public int majorityNumber(ArrayList nums) { + if (nums == null || nums.size() == 0) { + return -1; + } + int valA = 0; + int valB = 0; + int countA = 0; + int countB = 0; + for (int i = 0; i < nums.size(); i++) { + if (countA == 0 || nums.get(i) == valA) { + valA = nums.get(i); + countA++; + } else if (countB == 0 || nums.get(i) == valB) { + valB = nums.get(i); + countB++; + } else {//None of a || b matches + countA--; + countB--; + if (countA == 0) { + countA = 1; + valA = nums.get(i); + } else if (countB == 0) { + countB = 1; + valB = nums.get(i); + } + } + }//For + + countA = 0; + countB = 0; + for (int num : nums) { + countA += num == valA ? 1 : 0; + countB += num == valB ? 1 : 0; + } + return countA > countB ? valA : valB; + } +} + diff --git a/Others/old records/LintCode-Backup/Majority Number III.java b/Others/old records/LintCode-Backup/Majority Number III.java new file mode 100644 index 0000000..70f58d0 --- /dev/null +++ b/Others/old records/LintCode-Backup/Majority Number III.java @@ -0,0 +1,65 @@ +/* +Given an array of integers and a number k, the majority number is the number that occurs more than 1/k of the size of the array. Find it. + +Note +There is only one majority number in the array. + +Example +For [3,1,2,3,2,3,3,4,4,4] and k = 3, return 3 + +Challenge +O(n) time and O(k) extra space + +Thinking process: +Very similar to Majority I, II, except we can use a HashMap to store information (value, count). +Having a HashMap we have one advantage: when checking if current value matches any previously recorded val, just do a map.containsKey(val). +When a pair in hashMap has count ==0, remove this pair. +Note, to learn how to use iterator in HashMap. +Note: when map.containsKey(currVal) == false, the code checks map.size() == k before count-- perations. This is because: +We first need to put k candidates into HashMap before we count-- from all of them. If map.size() < k, that means we still have free spot for candidate in the HashMap, so in this case we do: map.put(candidateKey, 1). +*/ + + +public class Solution { + /** + * @param nums: A list of integers + * @param k: As described + * @return: The majority number + */ + public int majorityNumber(ArrayList nums, int k) { + if (nums == null || nums.size() == 0) { + return -1; + } + HashMap map = new HashMap(); + for (Integer num : nums) { + if (map.containsKey(num)) {//Found duplicates, count++ + map.put(num, map.get(num) + 1); + } else { + if (map.size() == k) {//All candidates added, do count-- + Iterator> iter = map.entrySet().iterator(); + while (iter.hasNext()) { + Map.Entry entry = iter.next(); + if (entry.getValue() - 1 == 0) { + iter.remove(); + } else { + map.put(entry.getKey(), entry.getValue() - 1); + } + }//While + } else { + map.put(num, 1); + } + } + }//For + + int result = 0; + int max = 0; + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() > max) { + max = entry.getValue(); + result = entry.getKey(); + } + } + return result; + } +} + diff --git a/Java/Majority Number.java b/Others/old records/LintCode-Backup/Majority Number.java similarity index 100% rename from Java/Majority Number.java rename to Others/old records/LintCode-Backup/Majority Number.java diff --git a/Others/old records/LintCode-Backup/Matrix Zigzag Traversal.java b/Others/old records/LintCode-Backup/Matrix Zigzag Traversal.java new file mode 100644 index 0000000..629772f --- /dev/null +++ b/Others/old records/LintCode-Backup/Matrix Zigzag Traversal.java @@ -0,0 +1,75 @@ +分析4个step. +小心走位。 +``` +/* +Matrix Zigzag Traversal + +Given a matrix of m x n elements (m rows, n columns), return all elements of the matrix in ZigZag-order. + +Example +Given a matrix: + +[ + [1, 2, 3, 4], + [5, 6, 7, 8], + [9,10, 11, 12] +] +return [1, 2, 5, 9, 6, 3, 4, 7, 10, 11, 8, 12] + +Tags Expand +LintCode Copyright Matrix +*/ + +/* + Always have the 4 states: right 1 step, left-bottom corner, right 1 step, up-right-corner. + If any of the 4 steps can't proceed because of hitting the wall, just go down 1 step. + +*/ + +public class Solution { + /** + * @param matrix: a matrix of integers + * @return: an array of integers + */ + public int[] printZMatrix(int[][] matrix) { + int[] rst = null; + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) { + return rst; + } + int n = matrix.length; + int m = matrix[0].length; + rst = new int[n * m]; + if (matrix.length == 1) { + return matrix[0]; + } + int i = 0, j = 0; + int ind = 0; + rst[ind] = matrix[i][j]; + ind++; + while (ind < rst.length) { + //Right 1 step, or down + if (j + 1 < m || i + 1 < n) { + if (j + 1 < m) j++; + else if (i + 1 < n) i++; + rst[ind++] = matrix[i][j]; + } + //Move left-bottom: + while (j - 1 >= 0 && i + 1 < n) { + rst[ind++] = matrix[++i][--j]; + } + //Move down, or right + if (j + 1 < m || i + 1 < n) { + if (i + 1 < n) i++; + else if (j + 1 < m) j++; + rst[ind++] = matrix[i][j]; + } + //Move right-up: + while (j + 1 < m && i - 1 >= 0) { + rst[ind++] = matrix[--i][++j]; + } + }//end while + return rst; + } +} + +``` \ No newline at end of file diff --git a/Java/Max Tree.java b/Others/old records/LintCode-Backup/Max Tree.java similarity index 100% rename from Java/Max Tree.java rename to Others/old records/LintCode-Backup/Max Tree.java diff --git a/Others/old records/LintCode-Backup/Maximal Square.java b/Others/old records/LintCode-Backup/Maximal Square.java new file mode 100644 index 0000000..fd858f9 --- /dev/null +++ b/Others/old records/LintCode-Backup/Maximal Square.java @@ -0,0 +1,79 @@ +M + +DP问题 + +从边长为2的正方形看起,看左上角的那个点。 +如何确定是个正方形?首先看左上点是不是1,然后看右边,右下,下面的点是不是1. + +DP就是根据这个特征想出来。dp[i,j]: 从右下往左上推算,包括当前点在内的所能找到的最大边长。 + 注意dp[i,j]被右边,右下,下面三点的最短点所限制。这就是fn. + +Init: + 把右边,下边两个边缘init一遍,存matrix在这两条边上的值,代表的意思也就是dp[i][j]在这些点上的初始值:变成为1 or 0. + +``` +/* +Given a 2D binary matrix filled with 0's and 1's, +find the largest square containing all 1's and return its area. + +Example +For example, given the following matrix: + +1 0 1 0 0 +1 0 1 1 1 +1 1 1 1 1 +1 0 0 1 0 +Return 4. + +Tags Expand +Dynamic Programming +*/ + +/* + Thoughts: Seem that we need to check on right and bottom spots for 2x2 1's. + If all size spots are clean, len++, square = newLen ^ 2. + DP[i,j]: the longest square lengh that this matrix[i][j] reach. + dp[i,j] = Math.min(dp[i][j+1], dp[i+1][j], dp[i+1][j+1]) + init: + dp[n-1][0 ~ m-1] = matrix[n-1][0 ~ m-1] == 0 ? 0 : 1; + dp[0 ~ n-1][m-1] = matrix[0 ~ n-1][m-1] == 0 ? 0 : 1; + for from rigt-bottom conor. + Maintain a max area +*/ + +public class Solution { + /** + * @param matrix: a matrix of 0 and 1 + * @return: an integer + */ + public int maxSquare(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return 0; + } + int n = matrix.length; + int m = matrix[0].length; + int[][] dp = new int[n][m]; + int maxLen = 0; + //Init + for (int i = 0; i < m; i++) { + dp[n - 1][i] = matrix[n - 1][i]; + maxLen = Math.max(maxLen, dp[n - 1][i]); + } + for (int i = 0; i < n; i++) { + dp[i][m - 1] = matrix[i][m - 1]; + maxLen = Math.max(maxLen, dp[i][m - 1]); + } + //function + for (int i = n - 2; i >= 0; i--) { + for (int j = m - 2; j >= 0; j--) { + if (matrix[i][j] == 1) { + dp[i][j] = Math.min(Math.min(dp[i][j + 1], dp[i + 1][j]), dp[i + 1][j + 1]) + 1; + maxLen = Math.max(maxLen, dp[i][j]); + } + } + } + return maxLen * maxLen; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Maximum Depth of Binary Tree.java b/Others/old records/LintCode-Backup/Maximum Depth of Binary Tree.java new file mode 100644 index 0000000..6e01816 --- /dev/null +++ b/Others/old records/LintCode-Backup/Maximum Depth of Binary Tree.java @@ -0,0 +1,63 @@ +DFS +Divide and conquer +``` +/* +71% Accepted +Given a binary tree, find its maximum depth. + +The maximum depth is the number of nodes along the longest path from the root node down to the farthest leaf node. + +Example +Given a binary tree as follow: + + 1 + + / \ + + 2 3 + + / \ + + 4 5 + +The maximum depth is 3 + +Tags Expand +Tree Binary Tree Depth First Search + +Thinking process: +check if root is null, return 0 if so. +Divide and return integer as the depth +Conquer: find the max and return depth + 1. +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + * @param root: The root of binary tree. + * @return: An integer. + */ + public int maxDepth(TreeNode root) { + if (root == null) { + return 0; + } + + int left = maxDepth(root.left); + int right = maxDepth(root.right); + + return Math.max(left, right) + 1; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Maximum Product Subarray.java b/Others/old records/LintCode-Backup/Maximum Product Subarray.java new file mode 100644 index 0000000..7f2409d --- /dev/null +++ b/Others/old records/LintCode-Backup/Maximum Product Subarray.java @@ -0,0 +1,104 @@ +/* +Find the contiguous subarray within an array (containing at least one number) which has the largest product. + +Example +For example, given the array [2,3,-2,4], the contiguous subarray [2,3] has the largest product = 6. + +Tags Expand +Dynamic Programming Subarray +*/ +/* +Attempt2: Use a max array and a min array. (http://www.jiuzhang.com/solutions/maximum-product-subarray/) +This is similar to my original attempt1, but saves a lot memory space. + +0. Max array is always positive, Min array is always negative. Use these 2 arrays to keep track of largest positive number and smallest negative number +1. When current nums[i] > 0, use max[i - 1] * nums[i]. +2. When current nums[i] < 0, user min[i - 1] * nums[i]; +3. Don't for get to calculate both max and min for each i, for next iteration to use. + +In either case, we will produce largest possible product. + +Trick: depending on nums[i] is positive or negative, calculate differently ... +*/ + +public class Solution { + public int maxProduct(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int[] max = new int[nums.length]; + int[] min = new int[nums.length]; + max[0] = nums[0]; + min[0] = nums[0]; + int rst = max[0]; + for (int i = 1; i < nums.length; i++) { + if (nums[i] > 0) { + max[i] = Math.max(nums[i], max[i - 1] * nums[i]);//the nums[i] could just be the best option + min[i] = Math.min(nums[i], min[i - 1] * nums[i]); + } else { + max[i] = Math.max(nums[i], min[i - 1] * nums[i]); + min[i] = Math.min(nums[i], max[i - 1] * nums[i]); + } + rst = Math.max(rst, max[i]); + } + return rst; + } +} + + + +/* +Attempt1 thoughts: +97% correct. However, this exceeds memory, basically the DP[][] is too large. +Draw a 2D array: +Row: Start from a number ROW[i], what contiguous value can we get: + 0 1 2 3 + ------------- + 2 3 -2 4 +0| 2 2 6 -12 -48 +1| 3 x x -6 -24 +2| -2 x x x -8 +3| 4 x x x x + +Look, according to the rules of (contiguous subarray), we can't do Row[i]xRow[i], so we have to do: Row[i]xROW[i+1]xROW[i+2]...etc +Goal: find the max in DP +1. Define DP[0][0] = nums[0]; +2. DP[i][j] = DP[i][j - 1] * nums[j] +3. And we keep track of the max value + +Note: j will always > i, so cases that i >= j are not necessary. +*/ + + +public class Solution { + /** + * @param nums: an array of integers + * @return: an integer + */ + public int maxProduct(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int[][] DP = new int[nums.length][nums.length]; + DP[0][0] = nums[0]; + int max = DP[0][0]; + + for (int i = 0; i < nums.length; i++) { + for (int j = 1; j < nums.length; j++) { + if (i == j) { + DP[i][j] = nums[j]; + } + if (j > i) { + if (DP[i][j - 1] == 0) { + DP[i][j] = nums[j]; + } else { + DP[i][j] = DP[i][j - 1] * nums[j]; + } + max = Math.max(max, DP[i][j]); + } + max = Math.max(max, nums[j]); + } + } + return max; + } +} \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Maximum Subarray III.java b/Others/old records/LintCode-Backup/Maximum Subarray III.java new file mode 100644 index 0000000..941e74b --- /dev/null +++ b/Others/old records/LintCode-Backup/Maximum Subarray III.java @@ -0,0 +1,33 @@ +/* + +Given an array of integers and a number k, find k non-overlapping subarrays which have the largest sum. + +The number in each subarray should be contiguous. + +Return the largest sum. + +Have you met this question in a real interview? Yes +Example +Given [-1,4,-2,3,-2,3], k=2, return 8 + +Note +The subarray should contain at least one number + +Tags Expand +LintCode Copyright Dynamic Programming Subarray Array +*/ + +/* + NOT DONE +*/ +public class Solution { + /** + * @param nums: A list of integers + * @param k: An integer denote to find k non-overlapping subarrays + * @return: An integer denote the sum of max k non-overlapping subarrays + */ + public int maxSubArray(ArrayList nums, int k) { + // write your code + } +} + diff --git a/Java/MaximumSubarray.java b/Others/old records/LintCode-Backup/MaximumSubarray.java similarity index 100% rename from Java/MaximumSubarray.java rename to Others/old records/LintCode-Backup/MaximumSubarray.java diff --git a/Java/MaximumSubarrayII.java b/Others/old records/LintCode-Backup/MaximumSubarrayII.java similarity index 100% rename from Java/MaximumSubarrayII.java rename to Others/old records/LintCode-Backup/MaximumSubarrayII.java diff --git a/Others/old records/LintCode-Backup/Median of two Sorted Arrays.java b/Others/old records/LintCode-Backup/Median of two Sorted Arrays.java new file mode 100644 index 0000000..09b843f --- /dev/null +++ b/Others/old records/LintCode-Backup/Median of two Sorted Arrays.java @@ -0,0 +1,39 @@ +/* + +Median of two Sorted Arrays + +There are two sorted arrays A and B of size m and n respectively. Find the median of the two sorted arrays. + +Example +Given A=[1,2,3,4,5,6] and B=[2,3,4,5], the median is 3.5. + +Given A=[1,2,3] and B=[4,5], the median is 3. + +Challenge +The overall run time complexity should be O(log (m+n)). + +Tags Expand +Sorted Array Divide and Conquer Array Zenefits Uber Google + +*/ + +/* + Thoughts: + Trivial: merge and find median. NOPE: have to be in log(m+n) time + http://www.jiuzhang.com/solutions/median-of-two-sorted-arrays/ + + http://fisherlei.blogspot.com/2012/12/leetcode-median-of-two-sorted-arrays.html + +*/ + +class Solution { + /** + * @param A: An integer array. + * @param B: An integer array. + * @return: a double whose format is *.5 or *.0 + */ + public double findMedianSortedArrays(int[] A, int[] B) { + // write your code here + } +} + diff --git a/Others/old records/LintCode-Backup/Median.java b/Others/old records/LintCode-Backup/Median.java new file mode 100644 index 0000000..2dac899 --- /dev/null +++ b/Others/old records/LintCode-Backup/Median.java @@ -0,0 +1,107 @@ +/* +Given a unsorted array with integers, find the median of it. + +A median is the middle number of the array after it is sorted. + +If there are even numbers in the array, return the N/2-th number after sorted. + +Example +Given [4, 5, 1, 2, 3], return 3 + +Given [7, 9, 4, 5], return 5 + +Challenge +O(n) time. + +Tags Expand +LintCode Copyright Quick Sort Array + + +*/ +/* + Recap 12.09.2015. + O(n) means just run through it. It's similar to Partition array: it tries to split the list into 2 parts, and find the pivot. +*/ + +/* +Thoughts: +Use standard quick sort, but the goal is to look for the middle point. +1. Get middle point: remember to -1 because we are looking for position, rather than length. +2. Increase low pointer until find a point >= piviot +3. Decrease high pointer until find a point < poviot +4. Swap the low and high: this set the first value greather than pivot to the right, and first avlue less than pivot to the left. +5. after low and high pointer meets, swap low with the piviot: simply because pivot should be the break point of low and high +6. at the end, the low sould be the middle point, which is the point we are looking for. return corresponding recursive helper. + +*/ +public class Solution { + /** + * @param nums: A list of integers. + * @return: An integer denotes the middle number of the array. + */ + public int median(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + if (nums.length % 2 == 0) { + return helper(nums, 0, nums.length - 1, nums.length/2 - 1); + } else { + return helper(nums, 0, nums.length - 1, nums.length/2); + } + } + + public void swap(int[] nums, int x, int y){ + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } + + public int helper(int[] nums, int start, int end, int mid) { + int pivot = end; + int num = nums[pivot]; + int low = start; + int high = end; + while (low < high) { + while(low < high && nums[low] < num) { + low++; + } + while(low < high && nums[high] >= num) { + high--; + } + swap(nums, low, high); + } + swap(nums, low, pivot); + if (low == mid) { + return nums[low]; + } else if (low < mid) { + return helper(nums, low + 1, end, mid); + } else { + return helper(nums, start, low - 1, mid); + } + } +} + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Merge Intervals.java b/Others/old records/LintCode-Backup/Merge Intervals.java new file mode 100644 index 0000000..518bf89 --- /dev/null +++ b/Others/old records/LintCode-Backup/Merge Intervals.java @@ -0,0 +1,73 @@ +/* + +Given a collection of intervals, merge all overlapping intervals. + +Example +Given intervals => merged intervals: + +[ [ + [1, 3], [1, 6], + [2, 6], => [8, 10], + [8, 10], [15, 18] + [15, 18] ] +] +Challenge +O(n log n) time and O(1) extra space. + +Tags Expand +Sort Array + +Thoughts: +1. use comparator to sort list. Well, no need to create a new class. Just do it inline. +2. iterate through the list and merge (whenever there is overlap) + + +Review: +List: size(), get(..), remove(..) +Comparator +*/ + + +/** + * Definition of Interval: + * public class Interval { + * int start, end; + * Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + */ + +class Solution { + /** + * @param intervals: Sorted interval list. + * @return: A new sorted interval list. + */ + public List merge(List intervals) { + if (intervals == null || intervals.size() == 0) { + return intervals; + } + + // intervals.sort(new CustomComparator()); + Collections.sort(intervals, new Comparator(){ + public int compare(Interval a, Interval b){ + return a.start - b.start; + } + }); + //Merge + Interval pre = intervals.get(0); + Interval curr = null; + for (int i = 1; i < intervals.size(); i++) { + curr = intervals.get(i); + if (pre.end >= curr.start) { + pre.end = pre.end > curr.end ? pre.end : curr.end; + intervals.remove(i); + i--; + } else { + pre = curr; + } + } + + return intervals; + } +} diff --git a/Others/old records/LintCode-Backup/Merge Sorted Array II.java b/Others/old records/LintCode-Backup/Merge Sorted Array II.java new file mode 100644 index 0000000..24c2efe --- /dev/null +++ b/Others/old records/LintCode-Backup/Merge Sorted Array II.java @@ -0,0 +1,99 @@ +长度已经固定。普通做法。 +``` +/* +33% Accepted +Merge two given sorted integer array A and B into a new sorted integer array. + +Example +A=[1,2,3,4] + +B=[2,4,5,6] + +return [1,2,2,3,4,4,5,6] + +Challenge +How can you optimize your algorithm if one array is very large and the other is very small? + +Tags Expand +Array Sorted Array + +*/ + +/* + 12.07.2015 + Since the 2 list A,B are fixed, just add everyting into it. + Basic implementation +*/ + +class Solution { + /** + * @param A and B: sorted integer array A and B. + * @return: A new sorted integer array + */ + public int[] mergeSortedArray(int[] A, int[] B) { + if (A == null || B == null) { + return A == null ? B : A; + } + int[] rst = new int[A.length + B.length]; + int indA = A.length - 1; + int indB = B.length - 1; + int i = rst.length - 1; + while (indA >= 0 && indB >= 0) { + if (A[indA] <= B[indB]) { + rst[i--] = B[indB--]; + } else { + rst[i--] = A[indA--]; + } + } + while (indA >= 0) { + rst[i--] = A[indA--]; + } + while (indB >= 0) { + rst[i--] = B[indB--]; + } + return rst; + } +} + +/* +Attemp1: Regular O(m+n) approach. Not optimizing anything. +*/ +class Solution { + /** + * @param A and B: sorted integer array A and B. + * @return: A new sorted integer array + */ + public ArrayList mergeSortedArray(ArrayList A, ArrayList B) { + if (A == null || A.size() == 0) { + return B; + } else if (B == null || B.size() == 0) { + return A; + } + ArrayList result = new ArrayList(); + int i = 0; + int j = 0; + while (i + j < A.size() + B.size()) { + if (i == A.size()) { + result.add(B.get(j)); + j++; + } else if (j == B.size()){ + result.add(A.get(i)); + i++; + } else { + if (A.get(i) <= B.get(j)) { + result.add(A.get(i)); + i++; + } else { + result.add(B.get(j)); + j++; + } + } + }//While + + return result; + } +} + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Merge Sorted Array.java b/Others/old records/LintCode-Backup/Merge Sorted Array.java new file mode 100644 index 0000000..2fdf188 --- /dev/null +++ b/Others/old records/LintCode-Backup/Merge Sorted Array.java @@ -0,0 +1,50 @@ +A够长,那么可以从A的尾部开始加新元素。 +注意,从尾部,是大数字优先的。 +``` +/* +Given two sorted integer arrays A and B, merge B into A as one sorted array. + +Note +You may assume that A has enough space (size that is greater or equal to m + n) to hold additional elements from B. The number of elements initialized in A and B are mand n respectively. + +Example +A = [1, 2, 3, empty, empty] B = [4,5] + +After merge, A will be filled as [1,2,3,4,5] + +Tags Expand +Array Sorted Array +*/ + +/* +Thinking process: +1. start from the end, track back. End index = m + n; +2. when ever check a position, need to do index-1, because index=m+n is the size of array. +3. Make sure to clean up the second array B. +*/ +class Solution { + /** + * @param A: sorted integer array A which has m elements, + * but size of A is m+n + * @param B: sorted integer array B which has n elements + * @return: void + */ + public void mergeSortedArray(int[] A, int m, int[] B, int n) { + // write your code here + int index = m + n; + while (m > 0 && n > 0) { + if (A[m - 1] > B[n - 1]) { + A[--index] = A[--m]; + } else { + A[--index] = B[--n]; + } + }//While + + while (n > 0) { + A[--index] = B[--n]; + } + } +} + + +``` \ No newline at end of file diff --git a/Java/Merge Two Sorted List.java b/Others/old records/LintCode-Backup/Merge Two Sorted List.java similarity index 94% rename from Java/Merge Two Sorted List.java rename to Others/old records/LintCode-Backup/Merge Two Sorted List.java index 92363b1..c793817 100644 --- a/Java/Merge Two Sorted List.java +++ b/Others/old records/LintCode-Backup/Merge Two Sorted List.java @@ -1,7 +1,10 @@ -小的放前。每次比head大小。 -while过后,把没完的list一口气接上。 +E + +小的放前。每次比head大小。 +while过后,把没完的list一口气接上。 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + ``` /* Merge two sorted linked lists and return it as a new list. The new list should be made by splicing together the nodes of the first two lists. diff --git a/Others/old records/LintCode-Backup/Merge k Sorted Arrays.java b/Others/old records/LintCode-Backup/Merge k Sorted Arrays.java new file mode 100644 index 0000000..2a64dcc --- /dev/null +++ b/Others/old records/LintCode-Backup/Merge k Sorted Arrays.java @@ -0,0 +1,97 @@ +由Merge k linked list想到,有办法找到每个node的sibling, 而int[]又不行,所以: +自己建立一个class 来存放必要信息 +``` +/* +Given k sorted integer arrays, merge them into one sorted array. + +Have you met this question in a real interview? Yes +Example +Given 3 sorted arrays: + +[ + [1, 3, 5, 7], + [2, 4, 6], + [0, 8, 9, 10, 11] +] +return [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]. + +Challenge +Do it in O(N log k). + +N is the total number of integers. +k is the number of arrays. +Tags Expand +Heap Priority Queue +*/ + +/* + Thoughts: 12.10.2015 + Since we can't know the node's sibiling, as creating the prirority queue, + let's create a Node{val, x, y} + Then it's very similar to merge k sorted lists. + + border case: arrays == null. length == 0 //[]return empty list. +*/ + +public class Solution { + public class Node { + int val, x, y; + public Node(int val, int x, int y) { + this.val = val; + this.x = x; + this.y = y; + } + } + + public List mergekSortedArrays(int[][] arrays) { + List rst = new ArrayList(); + if (arrays == null || arrays.length == 0) { + return rst; + } + + PriorityQueue queue = new PriorityQueue(arrays.length, + new Comparator() { + public int compare(Node a, Node b){ + return a.val - b.val; + } + } + ); + + //init + for (int i = 0; i < arrays.length; i++) { + if (arrays[i].length != 0) { + queue.offer(new Node(arrays[i][0], i, 0)); + } + } + + Node node; + + while (!queue.isEmpty()) { + node = queue.poll(); + rst.add(node.val); + if (node.y < arrays[node.x].length - 1) { + queue.offer(new Node(arrays[node.x][node.y + 1], node.x, node.y + 1)); + } + } + + return rst; + + } +} + + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Merge k Sorted Lists.java b/Others/old records/LintCode-Backup/Merge k Sorted Lists.java new file mode 100644 index 0000000..c1b2e19 --- /dev/null +++ b/Others/old records/LintCode-Backup/Merge k Sorted Lists.java @@ -0,0 +1,66 @@ +用Priorityqueue来排列所有list的头头。 +非常正规的。 + +记得k lists 需要是已经sort好的。 + +时间:n*O(logk) +PriorityQueue: logk +这个题目可以有好几个衍生: + +比如,如果k很大,一个机器上放不下所有的k list怎么办? +比如,如果Merge起来的很长,一个机器上放不下怎么办? +``` +/* +22% 通过 +Merge k sorted linked lists and return it as one sorted list. Analyze and describe its complexity. + +样例 +标签 Expand +Linked List Divide and Conquer Heap + +*/ + +/* + 12.10.2015 recap + Use queue to store the head of k lists. + First init with all heads. + Because the ListNode always has a link to its next sibiling, so it's easy to add that sibling back to queue. +*/ +public class Solution { + public ListNode mergeKLists(List lists) { + if (lists == null || lists.size() == 0) { + return null; + } + PriorityQueue queue = + new PriorityQueue(lists.size(), new Comparator(){ + public int compare(ListNode a, ListNode b){ + return a.val - b.val; + } + }); + + //populate queue with k lists' header + for (int i = 0; i < lists.size(); i++) { + if (lists.get(i) != null) { + queue.offer(lists.get(i)); + } + } + + ListNode dummy = new ListNode(0); + ListNode node = dummy; + while (!queue.isEmpty()) { + ListNode curr = queue.poll(); + node.next = curr; + + if (curr.next != null) { + queue.offer(curr.next); + } + + node = node.next; + } + + return dummy.next; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Middle of Linked List.java b/Others/old records/LintCode-Backup/Middle of Linked List.java new file mode 100644 index 0000000..9ba77bd --- /dev/null +++ b/Others/old records/LintCode-Backup/Middle of Linked List.java @@ -0,0 +1,59 @@ +快慢指针 + +不在乎slow是不是到底,因为fast肯定先到。 +确保fast, fast.next不是Null就好 + + +return slow +``` +/* +Find the middle node of a linked list. + +Have you met this question in a real interview? Yes +Example +Given 1->2->3, return the node with value 2. + +Given 1->2, return the node with value 1. + +Tags Expand +Linked List + +*/ +/* + Thoughts: + Practice LinkedList, fast and slow pointer +*/ + +public class Solution { + /** + * @param head: the head of linked list. + * @return: a middle node of the linked list + */ + public ListNode middleNode(ListNode head) { + if (head == null || head.next == null) { + return head; + } + + ListNode slow = head; + ListNode fast = head.next; + while (fast!= null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } +} + + +/** + * Definition for ListNode + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Min Stack.java b/Others/old records/LintCode-Backup/Min Stack.java new file mode 100644 index 0000000..a04ea92 --- /dev/null +++ b/Others/old records/LintCode-Backup/Min Stack.java @@ -0,0 +1,61 @@ +E + +双Stack:一个正常stack,另一个minStack存当下level最小值. 注意维护minStack的变化 + + +``` +/* +Implement a stack with min() function, which will return the smallest number in the stack. + +It should support push, pop and min operation all in O(1) cost. + +Example +push(1) +pop() // return 1 +push(2) +push(3) +min() // return 2 +push(1) +min() // return 1 +Note +min operation will never be called if there is no number in the stack. + +Tags Expand +Stack + +Thoughts: +using 2 stacks: one regular, the other one trackes min element +MinStack (0 ~ i): for i elements in regular stack, at each ith, the min element is stored at MinStack(i). This means, there can be duplicated mins for different ith. + +Note: remember to check if minStack isEmpty(), empty stack does not have peek() +*/ + +public class MinStack { + private Stack stack; + private Stack minStack; + public MinStack() { + stack = new Stack(); + minStack = new Stack(); + } + + public void push(int number) { + stack.push(number); + if (minStack.isEmpty()) { + minStack.push(number); + } else { + minStack.push(minStack.peek() >= number ? number : minStack.peek()); + } + } + + public int pop() { + minStack.pop(); + return stack.pop(); + } + + public int min() { + return minStack.peek(); + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Minimum Path Sum.java b/Others/old records/LintCode-Backup/Minimum Path Sum.java new file mode 100644 index 0000000..a02831a --- /dev/null +++ b/Others/old records/LintCode-Backup/Minimum Path Sum.java @@ -0,0 +1,55 @@ +/* +Given a m x n grid filled with non-negative numbers, find a path from top left to bottom right which minimizes the sum of all numbers along its path. + +Note +You can only move either down or right at any point in time. + +Example +Tags Expand +Dynamic Programming + +Thinking process: +1. Check null, lenght == 0 +2. Min Sum = sum of array. Initialization is a bit different, for example: each row element is added up from previous elemenet. (Not simple value assign from given grid) + - Assign (0,0) to grid[0][0] + - Row 1st row and 1st col, add up values +3. f(x,y) = sum of path value. f(x,y) = Math.Min(f(x-1,y), f(x, y-1)) +4. return f(r-1)(c-1) + +*/ + + +public class Solution { + /** + * @param grid: a list of lists of integers. + * @return: An integer, minimizes the sum of all numbers along its path + */ + public int minPathSum(int[][] grid) { + + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int row = grid.length; + int col = grid[0].length; + int[][] matrix = new int[row][col]; + matrix[0][0] = grid[0][0]; + //Add up for 1st row && 1st col + for (int i = 1; i < row; i++) { + matrix[i][0] = matrix[i - 1][0] + grid[i][0]; + } + for (int j = 1; j < col; j++) { + matrix[0][j] = matrix[0][j - 1] + grid[0][j]; + } + //Evaluate + for (int i = 1; i < row; i++) { + for (int j = 1; j < col; j++) { + matrix[i][j] = Math.min(matrix[i - 1][j], matrix[i][j - 1]) + + grid[i][j]; + } + } + return matrix[row - 1][col - 1]; + + } +} + + diff --git a/Others/old records/LintCode-Backup/Minimum Size Subarray Sum.java b/Others/old records/LintCode-Backup/Minimum Size Subarray Sum.java new file mode 100644 index 0000000..4e81edd --- /dev/null +++ b/Others/old records/LintCode-Backup/Minimum Size Subarray Sum.java @@ -0,0 +1,57 @@ +2 pointer: +一个做base, 每次动一格:i. +一个做前锋,加到满足条件为止。 +Note: 当sum >= s 条件在while里面满足时,end是多一个index的。所以result里面要处理好边缘情况:(end-1) 才是真的末尾位置,然后计算和开头的间隙: +(end - 1) - start + 1; +``` +/* +Given an array of n positive integers and a positive integer s, find the minimal length of a subarray of which the sum ≥ s. If there isn't one, return -1 instead. + +Example +Given the array [2,3,1,2,4,3] and s = 7, the subarray [4,3] has the minimal length under the problem constraint. + +Challenge +If you have figured out the O(n) solution, try coding another solution of which the time complexity is O(n log n). + +Tags Expand +Two Pointers Array +*/ + +/* +Thoughts: +Create a subarray range: (i,j). Use i as start and j as end. Check if within the range, sum >= s. +Shift the range every time: i += 1. +*/ + +public class Solution { + /** + * @param nums: an array of integers + * @param s: an integer + * @return: an integer representing the minimum size of subarray + */ + public int minimumSize(int[] nums, int s) { + if (nums == null || nums.length == 0) { + return -1; + } + int start = 0; + int end = 0; + int min = Integer.MAX_VALUE; + int sum = 0; + for (; start < nums.length; start++) { + while(end < nums.length && sum < s) { + sum += nums[end]; + end++; + } + if (sum >= s) { + min = Math.min(min, (end-1) - start + 1); + } + sum -= nums[start]; + } + return min == Integer.MAX_VALUE ? -1 : min; + } +} + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Minimum Subarray.java b/Others/old records/LintCode-Backup/Minimum Subarray.java new file mode 100644 index 0000000..10ed51f --- /dev/null +++ b/Others/old records/LintCode-Backup/Minimum Subarray.java @@ -0,0 +1,43 @@ +/* +Given an array of integers, find the subarray with smallest sum. + +Return the sum of the subarray. + +Example +For [1, -1, -2, 1], return -3 + +Note +The subarray should contain at least one integer. + +Tags Expand +Greedy LintCode Copyright Subarray Array + +Thoughts: +Note: sub-array has order. It's not sub-set +1. On each index: decide to add with nums.get(i), to use the new lowest value nums.get(i). That means: + If the new value is negative (it has decresing impact on sum) and the sum is larger than new value, just use the new value. + In another case, if sum has been nagative, so sum + new value will be even smaller, then use sum. +2. Every time compare the currMin with the overall minimum value, call it minRst. + +Note: remember to pre-set init value for curMin, minRst. +*/ + + +public class Solution { + /** + * @param nums: a list of integers + * @return: A integer indicate the sum of minimum subarray + */ + public int minSubArray(ArrayList nums) { + if (nums == null || nums.size() == 0) { + return 0; + } + int curMin = nums.get(0); + int minRst = nums.get(0); + for (int i = 1; i < nums.size(); i++) { + curMin = Math.min(nums.get(i), curMin + nums.get(i)); + minRst = Math.min(curMin, minRst); + } + return minRst; + } +} \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Minimum Window Substring.java b/Others/old records/LintCode-Backup/Minimum Window Substring.java new file mode 100644 index 0000000..dfaabcb --- /dev/null +++ b/Others/old records/LintCode-Backup/Minimum Window Substring.java @@ -0,0 +1,112 @@ +/* +Given a string source and a string target, find the minimum window in source which will contain all the characters in target. + +Example +source = "ADOBECODEBANC" target = "ABC" Minimum window is "BANC". + +Note +If there is no such window in source that covers all characters in target, return the emtpy string "". + +If there are multiple such windows, you are guaranteed that there will always be only one unique minimum window in source. + +Challenge +Can you do it in time complexity O(n) ? + +Clarification +Should the characters in minimum window has the same order in target? + + - Not necessary. + +Tags Expand +Hash Table + +Thoughts: +The idea was from jiuzhang.com. +1. count target Characters: store each Character with HashMap:tCounter +2. Test against the source string. Here create another HashMap to keep records of the window:minWindowCounter +3. For any char appears in both target and source, count++ in the minWindowCounter. +4. As long as the number of a specific Character source.charAt(i) in minWindowCounter is less than that in tCounter, + use a count++ to keep the record. +5. Once count == target.length(), that means there is a candidate macthcing. Now we get into next level and look for the +minimum window. Note, this condition only meets once, and after found the solution, we can return the result, minWindow. +Note, at this point, we confirm target exist in source. Now we just test against target and find minimum window. +6. Now use a leftBound = 0, and loop through the source, as long as leftBound tCounter = new HashMap(); + for (int i = 0; i < target.length(); i++) { + Character c = target.charAt(i); + if (!tCounter.containsKey(c)) { + tCounter.put(c, 1); + } else { + tCounter.put(c, tCounter.get(c) + 1); + } + } + + HashMap minWindowCounter = new HashMap(); + int count = 0; + String rst = ""; + for (int i = 0; i < source.length(); i++) { + Character c = source.charAt(i); + if (!tCounter.containsKey(c)) { + continue; + } + + if (minWindowCounter.containsKey(c)) { + minWindowCounter.put(c, minWindowCounter.get(c) + 1); + } else { + minWindowCounter.put(c, 1); + } + + if (minWindowCounter.get(c) <= tCounter.get(c)) { + count++; + } + + //Once the target exists in soruce: count = target.length(), find the result + if (count == target.length()) { + int leftBound = 0; + while (leftBound < source.length()) { + Character cs = source.charAt(leftBound); + if (!minWindowCounter.containsKey(cs)) {//Not part of window + leftBound++; + continue; + } + if (minWindowCounter.get(cs) > tCounter.get(cs)) {//Can find shorter window + minWindowCounter.put(cs, minWindowCounter.get(cs) - 1); + leftBound++; + continue; + } + break; + } + rst = source.substring(leftBound, i + 1); + return rst; + } + } + return rst; + } + + public static void main(String[] args) { + Solution test = new Solution(); + String rst = test.minWindow("abcd", "ac"); + System.out.println("resutl is : " + rst); + } +} diff --git a/Java/MinimumDepthOfBinaryTree.java b/Others/old records/LintCode-Backup/MinimumDepthOfBinaryTree.java similarity index 100% rename from Java/MinimumDepthOfBinaryTree.java rename to Others/old records/LintCode-Backup/MinimumDepthOfBinaryTree.java diff --git a/Java/NQueens.java b/Others/old records/LintCode-Backup/NQueens.java similarity index 100% rename from Java/NQueens.java rename to Others/old records/LintCode-Backup/NQueens.java diff --git a/Java/NQueensII.java b/Others/old records/LintCode-Backup/NQueensII.java similarity index 100% rename from Java/NQueensII.java rename to Others/old records/LintCode-Backup/NQueensII.java diff --git a/Others/old records/LintCode-Backup/Next Permutation.java b/Others/old records/LintCode-Backup/Next Permutation.java new file mode 100644 index 0000000..3d4c64c --- /dev/null +++ b/Others/old records/LintCode-Backup/Next Permutation.java @@ -0,0 +1,87 @@ +/* +Given a list of integers, which denote a permutation. + +Find the next permutation in ascending order. + +Have you met this question in a real interview? Yes +Example +For [1,3,2,3], the next permutation is [1,3,3,2] + +For [4,3,2,1], the next permutation is [1,2,3,4] + +Note +The list may contains duplicate integers. + +Tags Expand +LintCode Copyright Permutation + +Thoughts: +Not much info are given. Need to ask. It looks like: +We are dong permutation on the given numbers, and find out what's next permutation array based on given order. + +Ascending order: Permutations that permutation(i) < permutation(i + 1) + +Goal: +To find the next smallest permutation. +1. Find the last increasing index (a peek before decresing): k +2. Find the first bigger permutation: Well, it turns out this first bigger index is always on right side of k. + Note: we are trying to get the least significant change on the given permuation. + Next Step: reverse (k+1, end). This is because: before the change, right side of K will be the largest possible combination. After swapping K, we need the right side to be the smallest combination. (Well, this is my understanding....Still a bit confused on why we take these steps in this problem) +*/ + + +public class Solution { + /** + * @param nums: an array of integers + * @return: return nothing (void), do not return anything, modify nums in-place instead + */ + //Revers the given part of a int[] + public int[] reverse(int start, int end, int[] nums) { + for (int i = start, j = end; i < j; i++,j--) { + int temp = nums[i]; + nums[i] = nums[j]; + nums[j] = temp; + } + return nums; + } + + public int[] nextPermutation(int[] nums) { + if (nums == null || nums.length == 0) { + return nums; + } + //Find last increasing point before decreasing. nums[k] < nums[k+1] + int k = -1; + for (int i = nums.length - 2; i >= 0; i--) { + if (nums[i] < nums[i + 1]) { + k = i; + break; + } + } + if (k == -1) { + return reverse(0, nums.length - 1, nums); + } + //Find first bigger point, from right to left + int bigIndex = -1; + for (int i = nums.length - 1; i >= 0; i--) { + if (nums[i] > nums[k]) { + bigIndex = i; + break; + } + } + //1. Swap bigger index with k; 2. Reverse the right side of k. [Try to make the smallest next permutation] + int temp = nums[k]; + nums[k] = nums[bigIndex]; + nums[bigIndex] = temp; + + return reverse(k + 1, nums.length - 1, nums); + } + + + +} + + + + + + diff --git a/Others/old records/LintCode-Backup/Nim Game.java b/Others/old records/LintCode-Backup/Nim Game.java new file mode 100644 index 0000000..11866ab --- /dev/null +++ b/Others/old records/LintCode-Backup/Nim Game.java @@ -0,0 +1,45 @@ +著名Nim游戏。 +写一些,发现n=4,5,6,7,8...etc之后的情况有规律性。 +最终很简单n%4!=0就可以了 +``` +/* +You are playing the following Nim Game with your friend: There is a heap of stones on the table, each time one of you take turns to remove 1 to 3 stones. The one who removes the last stone will be the winner. You will take the first turn to remove the stones. + +Both of you are very clever and have optimal strategies for the game. Write a function to determine whether you can win the game given the number of stones in the heap. + +For example, if there are 4 stones in the heap, then you will never win the game: no matter 1, 2, or 3 stones you remove, the last stone will always be removed by your friend. + +Hint: + +If there are 5 stones in the heap, could you figure out a way to remove the stones such that you will always be the winner? + + +Hide Similar Problems (M) Flip Game II + +*/ + + +/* + Thoughts: + If n = 4, we can do the following: + 1 0 0 0 + 1 1 0 0 + 1 1 1 0 + But we'll fail. + + n = 5, we pick 1, 2nd player gets n = 4. + n = 6, we pick 2, 2nd player gets n = 4. + n = 7, we pick 3, 2nd player gets n = 4. + n = 8, regarless whatever we pick, the opponent can make 1st gets n = 4, we fail. + ... + ... + whenever n % 4 = 0, 1st player fail. + +*/ + +public class Solution { + public boolean canWinNim(int n) { + return n % 4 != 0; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Nth to Last Node in List.java b/Others/old records/LintCode-Backup/Nth to Last Node in List.java new file mode 100644 index 0000000..daa135a --- /dev/null +++ b/Others/old records/LintCode-Backup/Nth to Last Node in List.java @@ -0,0 +1,61 @@ +先找到nth node +然后head开始跑。 + +node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth +``` +/* +Find the nth to last element of a singly linked list. + +The minimum number of nodes in list is n. + +Example +Given a List 3->2->1->5->null and n = 2, return node whose value is 1. + +Tags Expand +Cracking The Coding Interview Linked List + +Thinking process: +1. Find nth node in normal order. +2. Have a head at index0. +3. Move both head and nth node. WHen nth node hit null/end, then the moving head is the nth to last node in list. +*/ + +/** + * Definition for ListNode. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int val) { + * this.val = val; + * this.next = null; + * } + * } + */ +public class Solution { + /** + * @param head: The first node of linked list. + * @param n: An integer. + * @return: Nth to last node of a singly linked list. + */ + ListNode nthToLast(ListNode head, int n) { + if (head == null || n < 0) { + return null; + } + int count = 0; + ListNode node = head; + while (node != null && count < n) { + node = node.next; + count++; + } + while (node != null) { + node = node.next; + head = head.next; + } + return head; + } +} + + + + +``` \ No newline at end of file diff --git a/Java/Number Triangles.java b/Others/old records/LintCode-Backup/Number Triangles.java similarity index 100% rename from Java/Number Triangles.java rename to Others/old records/LintCode-Backup/Number Triangles.java diff --git a/Others/old records/LintCode-Backup/Number of Airplane in the sky.java b/Others/old records/LintCode-Backup/Number of Airplane in the sky.java new file mode 100644 index 0000000..32ffe56 --- /dev/null +++ b/Others/old records/LintCode-Backup/Number of Airplane in the sky.java @@ -0,0 +1,72 @@ +/* +http://www.lintcode.com/en/problem/number-of-airplanes-in-the-sky/ +Given an interval list which are flying and landing time of the flight. How many airplanes are on the sky at most? + +Example +For interval list [[1,10],[2,3],[5,8],[4,7]], return 3 + +Note +If landing and flying happens at the same time, we consider landing should happen at first. + +Tags Expand +LintCode Copyright Array Interval +*/ + + +/* +Thoughts: same as the number of meeting. +Use a Point class {time, flag} and mark all time spot, and use a min-heap(PriorityQueue) to sort it. + +Note: LintCode forces me to put '10' in PriorityQueue constructor? +*/ +/** + * Definition of Interval: + * public classs Interval { + * int start, end; + * Interval(int start, int end) { + * this.start = start; + * this.end = end; + * } + */ + +class Solution { + class Point { + int x; + int flag; + public Point(int x, int flag) { + this.x = x; + this.flag = flag; + } + } + /** + * @param intervals: An interval array + * @return: Count of airplanes are in the sky. + */ + public int countOfAirplanes(List airplanes) { + if (airplanes == null || airplanes.size() == 0) { + return 0; + } + PriorityQueue queue = new PriorityQueue(10, + new Comparator(){ + public int compare(Point a, Point b) { + return a.x - b.x; + } + }); + for (Interval interval : airplanes) { + queue.offer(new Point(interval.start, 1)); + queue.offer(new Point(interval.end, -1)); + } + int count = 0; + int max = 0; + while (!queue.isEmpty()) { + Point p = queue.poll(); + count+= p.flag; + while (!queue.isEmpty() && queue.peek().x == p.x) { + p = queue.poll(); + count += p.flag; + } + max = Math.max(count, max); + } + return max; + } +} diff --git a/Others/old records/LintCode-Backup/Number of Islands II.java b/Others/old records/LintCode-Backup/Number of Islands II.java new file mode 100644 index 0000000..04bf01a --- /dev/null +++ b/Others/old records/LintCode-Backup/Number of Islands II.java @@ -0,0 +1,110 @@ +/* +Given a n,m which means the row and column of the 2D matrix and an array of pair A( size k). Originally, the 2D matrix is all 0 which means there is only sea in the matrix. The list pair has k operator and each operator has two integer A[i].x, A[i].y means that you can change the grid matrix[A[i].x][A[i].y] from sea to island. Return how many island are there in the matrix after each operator. + +Have you met this question in a real interview? Yes +Example +Given n = 3, m = 3, array of pair A = [(0,0),(0,1),(2,2),(2,1)]. + +return [1,1,2,2]. + +Note +0 is represented as the sea, 1 is represented as the island. If two 1 is adjacent, we consider them in the same island. We only consider up/down/left/right adjacent. + +Tags Expand +Union Find +*/ + +/* +Thoughts: +Each pos(x,y) turns that sea spot into a island spot. +Image each isleand spot is a node in the graph, and each island(many island spots) has a root parent. +In for loop, try to add operators into the matrix one after another. + Every time when adding a new island spot, check its sourandings and see if there are islands existed. + If souranding island was land: + To check if the surrouding spot are on common island (use find and union). + Since the operator spot was sea, the it's root parent is itself. Then, souranding spot has different island root, + they will surely have differet root parent, but they will do after they connect, so we do count--. + +On the otherhand, if surrounding was just sea, then count++ is natural + +Note: +1. Know how to write up simple union find class +2. Convert 2D array into 1D +*/ + +/** + * Definition for a point. + * class Point { + * int x; + * int y; + * Point() { x = 0; y = 0; } + * Point(int a, int b) { x = a; y = b; } + * } + */ +public class Solution { + class UnionFind { + HashMap map = new HashMap(); + public UnionFind(int length) { + for (int i = 0; i < length; i++) { + map.put(i,i); + } + } + public int find(int target) { + int parent = map.get(target); + while (parent != map.get(parent)) { + parent = map.get(parent); + } + return parent; + } + + public void union(int x, int y) { + int findX = find(x); + int findY = find(y); + if (findX != findY) { + map.put(findX, findY); + } + } + } + /** + * @param n an integer + * @param m an integer + * @param operators an array of point + * @return an integer array + */ + public List numIslands2(int n, int m, Point[] operators) { + List rst = new ArrayList(); + if (operators == null || operators.length == 0) { + return rst; + } + int count = 0; + int[] island = new int[m*n]; + UnionFind uf = new UnionFind(m*n); + int[] xs = {-1, 1, 0, 0}; + int[] ys = {0, 0, -1, 1}; + for (int i = 0; i < operators.length; i++) { + int x = operators[i].x; + int y = operators[i].y; + int pos = x * m + y; + count++; + if (island[pos] != 1) { + island[pos] = 1; + for (int j = 0; j < 4; j++) { + int nx = x + xs[j]; + int ny = y + ys[j]; + int newPos = nx * m + ny; + if (nx >= 0 && nx < n && ny >= 0 && ny < m && island[newPos] == 1) {//when new position is land + int findA = uf.find(pos); + int findB = uf.find(newPos); + if (findA != findB) { + count--; + uf.union(pos, newPos); + } + } + } + } + rst.add(count); + } + + return rst; + } +} diff --git a/Others/old records/LintCode-Backup/Number of Islands.java b/Others/old records/LintCode-Backup/Number of Islands.java new file mode 100644 index 0000000..dd4dcbe --- /dev/null +++ b/Others/old records/LintCode-Backup/Number of Islands.java @@ -0,0 +1,118 @@ +M + +方法1: 两个for loop brutle force。 DFS把每个跟1相关的都Mark一遍.生成一个island. + +方法2: (暂时没有写union-find的解) +可以用union-find, 就像Number of island II 一样。 +只不过这个不Return list, 而只是# of islands + +``` +/* +Given a boolean 2D matrix, find the number of islands. + +Example +Given graph: + +[ + [1, 1, 0, 0, 0], + [0, 1, 0, 0, 1], + [0, 0, 0, 1, 1], + [0, 0, 0, 0, 0], + [0, 0, 0, 0, 1] +] +return 3. + +Note +0 is represented as the sea, 1 is represented as the island. +If two 1 is adjacent, we consider them in the same island. We only consider up/down/left/right adjacent. + +*/ + +/* + 12.12.2015 recap + We are checking if a sets of adjacent nodes are int the same set + We union all neighbors + Generate the list of islands: actually return the # of islands + + AND yes, this is doable. Number of island II is using union-find +*/ + +/* + +Thoughts: +1. Use a 2D integer matrix to map the islands, by default the 2D [][] is all 0. +2. When the matrix point is 0, means it has not been checked. Check it against the grid. +3. Whenever there is a match, assign mark to that point. +4. Only increase mark when all the recursion finishes. +5. return mark - 1 + +*/ +public class Solution { + int[][] matrix; + int mark; + /** + * @param grid a boolean 2D matrix + * @return an integer + */ + public int numIslands(boolean[][] grid) { + if (grid == null || grid.length ==0 || grid[0].length == 0) { + return 0; + } + matrix = new int[grid.length][grid[0].length]; + mark = 1; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + mark = check(i, j, mark, grid) ? (mark + 1) : mark; + } + } + + return mark - 1; + } + + public boolean check(int x, int y, int mark, boolean[][] grid) { + if (x >= 0 && x < matrix.length && y >= 0 && y < matrix[0].length) { + if (matrix[x][y] == 0 && grid[x][y]) { + matrix[x][y] = mark; + check(x + 1, y, mark, grid); + check(x - 1, y, mark, grid); + check(x, y + 1, mark, grid); + check(x, y - 1, mark, grid); + return true; + } + } + return false; + } +} + + +//from leetcode: +public class Solution { + + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) { + return 0; + } + int count = 0; + for(int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + count = mark(i, j, grid)? (count + 1) : count; + } + } + return count; + } + + public boolean mark(int i, int j, char[][] grid) { + if (i >= 0 && i < grid.length && j >= 0 && j < grid[0].length) { + if (grid[i][j] == '1') { + grid[i][j] = 'M'; + mark(i - 1, j, grid); + mark(i + 1, j, grid); + mark(i, j - 1, grid); + mark(i, j + 1, grid); + return true; + } + } + return false; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/O(1) Check Power of 2.java b/Others/old records/LintCode-Backup/O(1) Check Power of 2.java new file mode 100644 index 0000000..8da73e3 --- /dev/null +++ b/Others/old records/LintCode-Backup/O(1) Check Power of 2.java @@ -0,0 +1,34 @@ +/* +Using O(1) time to check whether an integer n is a power of 2. +Example +For n=4, return true + +For n=5, return false + +Challenge +O(1) time + +Tags Expand +Binary + +Thinking process: +Any integer that's power of 2, follows one pattern. They are all: 1000000000....000 format. +so (n - 1) becomes: 01111111111...111. +If bit-and them togeter, it will be 0. + +*/ + +class Solution { + /* + * @param n: An integer + * @return: True or false + */ + public boolean checkPowerOf2(int n) { + if (n <= 0) { + return false; + } + return (n & (n - 1)) == 0; + } +}; + + diff --git a/Others/old records/LintCode-Backup/Palindrome Linked List.java b/Others/old records/LintCode-Backup/Palindrome Linked List.java new file mode 100644 index 0000000..418294b --- /dev/null +++ b/Others/old records/LintCode-Backup/Palindrome Linked List.java @@ -0,0 +1,115 @@ +Palindrome都是要两边回溯相等。 +linkedlist不能reverse iterating, 那么就reverse the list, 从中间开花作比较。 +``` +/* +Implement a function to check if a linked list is a palindrome. + +Example +Given 1->2->1, return true + +Challenge +Could you do it in O(n) time and O(1) space? + +Tags Expand +Linked List +*/ + +/* + Thoughts: + Reverse the 2nd half of the list, save it to right node + Then compare left and right. + THey should be the same. + + How do we know it's odd or even number of nodes? + ( + At the end, if F.next == null, even. + If F == null, odd + Well, need a global bool isEven variable + - If odd: let = [0 , mid], right = [mid, end] + - If even: left = [0,mid], right = [mid +1, end] + ) + The odd/even actually does not matter: regardless which middle is returned, always reverse [mid.next, end] + - p1 has 1 extra element, p2 will loop to null first. That's okay. + - p1 and p2 size same, p2 will loop till end, null. + That is, if somewhere they break, and p2 is not loopped to null yet, that means, the palindrome fails + 1. find mid. + 2. reverse 2nd half of the list. + 3. while loop to compare the node if they are all equal. if not false. + + border case: if head == null, or head.next == null, just true. +*/ + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +public class Solution { + + public boolean isPalindrome(ListNode head) { + if (head == null || head.next == null) { + return true; + } + //Find middle + ListNode mid = findMiddle(head); + //Reverse and return right side + ListNode right = reverse(mid.next); + mid.next = null; + ListNode left = head; + while (left != null && right != null) { + if (left.val != right.val) { + return false; + } + left = left.next; + right = right.next; + } + //It's possible that left&&right both finishes; or just right finishes. Both cases are returnning true. + return right == null; + } + + public ListNode findMiddle(ListNode head) { + ListNode slow = head; + ListNode fast = head.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + public ListNode reverse(ListNode head) { + ListNode dummy = new ListNode(0); + ListNode reversedList = dummy; + while (head != null) { + //store head.next for iteration usage + ListNode temp = head.next; + //insert head into the reservedList, at starting spot. + head.next = reversedList.next; + reversedList.next = head; + //Move to next node + head = temp; + } + return dummy.next; + } +} + + + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Palindrome Partitioning II.java b/Others/old records/LintCode-Backup/Palindrome Partitioning II.java new file mode 100644 index 0000000..447313e --- /dev/null +++ b/Others/old records/LintCode-Backup/Palindrome Partitioning II.java @@ -0,0 +1,57 @@ +Double for loop 检查每种substring string (i~j). 若i,j相邻或者同点,那么肯定isPal;否则,i,j之间的(i+1, j-1)一定得isPal。 +看上去,在检查i,j的时候,中间按的(i+1, j-1)怎么可能先知道? 其实不然..在j慢慢长大的时候,所有的0~j的substring都检查过。所以isPal[i+1][j-1]一定是已经知道结果的。 + +okay.那么假如以上任意一种情况成立,也就是说isPal[i][j] == true。那就要判断,切到第一层循环参数j的末尾点时,有多少种切法? +想法很顺:我们naturally会想到,把i之前的cut加上i~j之间发生的不就好了。 +反正现在j不变,现在就看吧i定在哪里,cut[i - 1]是否更小/最小; 再在cut[i-1]基础上+1就完了。 + 当然,如果i==0, 而 i~j又是isPal,那没啥好谈的,不必切,0刀。 + +最终,刷到cut[s.length() - 1] 也就是最后一点。 return的理所应当。 +``` +/* +Given a string s, cut s into some substrings such that every substring is a palindrome. +Return the minimum cuts needed for a palindrome partitioning of s. +Example +For example, given s = "aab", +Return 1 since the palindrome partitioning ["aa","b"] could be produced using 1 cut. +Tags Expand +Dynamic Programming +Thinking process: +DP problem. +Use a isPal to record if any [i ~ j] is Palindrome, true/false + for any char s[i] and s[j], if s[i] == s[j], then need to check if [i + 1, j - 1] is Palindrome, which is just isPal[i + 1, j - 1]. +Use cut[j] to record the minimal cut from char index [0 ~ j] + by default, cut[j] = j because the worst condition is cut j times at each charactor: none 2+ character palindrome, and split into individual chars. + update cut[j] by comparing existing cut[j] and (cut[i - 1] + 1). +At the end, return cut[s.length() - 1]. +*/ + +public class Solution { + /** + * @param s a string + * @return an integer + */ + public int minCut(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int length = s.length(); + boolean[][] isPal = new boolean[length][length]; + int[] cut = new int[length]; + for (int j = 0; j < length; j++) { + cut[j] = j; + for (int i = 0; i <= j; i++) { + if (s.charAt(i) == s.charAt(j) && (j - i <= 1 || isPal[i + 1][j - 1])) { + isPal[i][j] = true; + if (i > 0) { + cut[j] = Math.min(cut[j], cut[i - 1] + 1); + } else { + cut[j] = 0; + } + } + }//end i_for + }//end for j_for + return cut[length - 1]; + } +}; +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Palindrome Partitioning.java b/Others/old records/LintCode-Backup/Palindrome Partitioning.java new file mode 100644 index 0000000..95dff80 --- /dev/null +++ b/Others/old records/LintCode-Backup/Palindrome Partitioning.java @@ -0,0 +1,73 @@ +很清楚的DFS Backtracking. +在遍历str的时候,考虑从每个curr spot 到 str 结尾,是能有多少种palindorme? 那就从curr spot当个字符开始算,开始back tracing. +如果所选不是palindrome, 那move on. +若所选的确是palindrome, 加到path里面,DFS去下个level,等遍历到了结尾,这就产生了一种分割成palindrome的串。 +每次DFS结尾,要把这一层加的所选palindrome删掉,backtracking嘛。 +``` +/* +Given a string s, partition s such that every substring of the partition is a palindrome. +Return all possible palindrome partitioning of s. +Example +given s = "aab", +Return + [ + ["aa","b"], + ["a","a","b"] + ] +Tags Expand +Backtracking +Thinking process: +1. Know how to write isPalindrome +2. Back tracking: + check partial string + add it + recursive call + remove it. +*/ + +public class Solution { + /** + * @param s: A string + * @return: A list of lists of string + */ + public List> partition(String s) { + List> rst = new ArrayList>(); + if (s == null || s.length() < 0) { + return rst; + } + ArrayList path = new ArrayList(); + helper(s, path, 0, rst); + return rst; + } + //Check Palindrome + public boolean isPalindrome(String s){ + int start = 0; + int end = s.length() - 1; + while (start < end) { + if (s.charAt(start) != s.charAt(end)) { + return false; + } + start++; + end--; + } + return true; + } + //helper: + public void helper(String s, ArrayList path, int pos, + List> rst) { + if (pos == s.length()) { + rst.add(new ArrayList(path)); + return; + } + for (int i = pos + 1; i <= s.length(); i++) {//i is used in s.sbustring(pos, i), which can equal to s.length() + String prefix = s.substring(pos, i); + if (!isPalindrome(prefix)) { + continue; + } + path.add(prefix); + helper(s, path, i, rst); + path.remove(path.size() - 1); + } + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Partition Array by Odd and Even.java b/Others/old records/LintCode-Backup/Partition Array by Odd and Even.java new file mode 100644 index 0000000..27c784f --- /dev/null +++ b/Others/old records/LintCode-Backup/Partition Array by Odd and Even.java @@ -0,0 +1,51 @@ +/* +Partition an integers array into odd number first and even number second. + +Example +Given [1, 2, 3, 4], return [1, 3, 2, 4] + +Challenge +Do it in-place. + +Tags Expand +Two Pointers Array + +Thougths: +Use two pointers: nextOddPt, firstEvenPt +1. Whenever nextOddPt > firstEvenPt, swapt them +2. Incrase nextOddPt in a for loop +Note: +After each swap, have to start checking again from beginning-switching point, which will be firstEvenPt. Need to set i = firstEvenPt. +However, since for loop will do i++, we need to set i = firstEvenPt - 1; +And firstEvenPt only needs to be update once so use -1 to check if it's set. +*/ + +public class Solution { + /** + * @param nums: an array of integers + * @return: nothing + */ + public void partitionArray(int[] nums) { + if (nums == null || nums.length == 0){ + return; + } + int nextOddPt = -1; + int firstEvenPt = -1; + for (int i = 0; i < nums.length; i++) { + if (nums[i] % 2 == 1) { + nextOddPt = i; + } else { + if (firstEvenPt == -1) { + firstEvenPt = i; + } + } + if (nextOddPt > firstEvenPt && firstEvenPt != -1) { + int temp = nums[nextOddPt]; + nums[nextOddPt] = nums[firstEvenPt]; + nums[firstEvenPt] = temp; + i = firstEvenPt - 1; + firstEvenPt = -1; + } + } + } +} diff --git a/Others/old records/LintCode-Backup/Partition Array.java b/Others/old records/LintCode-Backup/Partition Array.java new file mode 100644 index 0000000..5ded8b4 --- /dev/null +++ b/Others/old records/LintCode-Backup/Partition Array.java @@ -0,0 +1,74 @@ +Partition Array根据pivot把array分成两半。 +从array两边开始缩进。while loop到遍历完。非常直白的implement。 +注意low/high,或者叫start/end不要越边界 +O(n) + +Quick sort的基础。 +``` +/* +Given an array nums of integers and an int k, partition the array (i.e move the elements in "nums") such that: + +All elements < k are moved to the left +All elements >= k are moved to the right +Return the partitioning index, i.e the first index i nums[i] >= k. + +Example +If nums=[3,2,2,1] and k=2, a valid answer is 1. + +Note +You should do really partition in array nums instead of just counting the numbers of integers smaller than k. + +If all elements in nums are smaller than k, then return nums.length + +Challenge +Can you partition the array in-place and in O(n)? + +Tags Expand +Two Pointers Sort Array + +Thoughts: +Two pointer sort, still similar to quick sort. +Move small to left and large to right. +When the two pinter meets, that's crossing point about pivot k + +*/ +public class Solution { + /** + *@param nums: The integer array you should partition + *@param k: As description + *return: The index after partition + */ + public int partitionArray(int[] nums, int k) { + if (nums == null || nums.length == 0) { + return 0; + } + return helper(nums, 0, nums.length - 1, k); + } + + public void swap(int[] nums, int x, int y){ + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } + + public int helper(int[] nums, int start, int end, int pivot) { + int low = start; + int high = end; + while (low <= high) { + while(low <= high && nums[low] < pivot) { + low++; + } + while(low <= high && nums[high] >= pivot) { + high--; + } + if (low <= high) { + swap(nums, low, high); + low++; + high--; + } + } + return low; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Partition List.java b/Others/old records/LintCode-Backup/Partition List.java new file mode 100644 index 0000000..5e4a1e1 --- /dev/null +++ b/Others/old records/LintCode-Backup/Partition List.java @@ -0,0 +1,81 @@ +不能像partitioin array一样从两边遍历。 + +那就最普通的,建造两个list + +把满足条件(=x)的数字分别放到两个list里面 + +记得用dummyNode track head. +最终pre.next = post链接起来。 +``` +/* +33% Accepted +Given a linked list and a value x, +partition it such that all nodes less than x come before nodes greater than or equal to x. + +You should preserve the original relative order of the nodes in each of the two partitions. + +For example, +Given 1->4->3->2->5->2->null and x = 3, +return 1->2->2->4->3->5->null. + +Example +Tags Expand +Linked List Two Pointers +*/ + +/* +Thinking process: +0. dummyPre, dummyPost to store the head of the 2 list +1. Append node.val < x to listPre +2. Append node.val >= x to listPost +3. Link them togeter +4. return dummyPre.next +*/ + +/** + * Definition for ListNode. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int val) { + * this.val = val; + * this.next = null; + * } + * } + */ +public class Solution { + /** + * @param head: The first node of linked list. + * @param x: an integer + * @return: a ListNode + */ + public ListNode partition(ListNode head, int x) { + if (head == null) { + return head; + } + + ListNode dummyPre = new ListNode(0); + ListNode dummyPost = new ListNode(0); + ListNode pre = dummyPre; + ListNode post = dummyPost; + + while (head != null) { + if (head.val < x) { + pre.next = head; + pre = pre.next; + } else { + post.next = head; + post = post.next; + } + head = head.next; + } + + post.next = null; + pre.next = dummyPost.next; + + return dummyPre.next; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Permutation Index.java b/Others/old records/LintCode-Backup/Permutation Index.java new file mode 100644 index 0000000..0b67a44 --- /dev/null +++ b/Others/old records/LintCode-Backup/Permutation Index.java @@ -0,0 +1,89 @@ +这题目标为简单。但是做了很久。 +最后分析出来: +每个数位的数字,都是基于这个数字之前越过的战壕...好吧,意思是,跳过了多少种可能。 +对,就用4,2,1举例。网上大家都用这个例子。不行,我要换一个。 + +换【6,5,2】吧。我们找6,5,2是permudation里面的第几个。 +正常排序,也就是permutation的第一个,应该是【2,5,6】 +如果要从首位,2,变成6,要跨过多少条尸体呢? +很简单,高中就学过,重点来了:小于6的数字有多少个呢?(2,5).然后每个数字变成head,都有各自的变化可能,而每个数字打头,都有(n-1)!种可能。明显了吧,把每个(n-1)!加起来。 注意(n-1)意思就是出去开头的数字(2、5),后面有多少个,有序排列一下有多少情况,不就是(n-1)!嘛。 + 这一步,公式推出来就是很简单的 (有几个小于6的数字呀) ×(出去head剩下有多少个数字)! + +以上 种种,都是为了把6推上皇位,而牺牲的条数。 + +那么把6推上去以后,还有接下去的呢。 + +接下去要看5,2. +6确定,后面permudation可变的情况有可能是【6,5,2】,那还可能是【6,2,5】呢。 + +方法一样了。 +看given 数组的第二位5,算它接下去: +1. 有几个数字小于5呢? +2. 除去5,还有几个数字可以 factorial呢? +3. 一样的。第一步就结果乘以第二步。 + +接下去要看最后一个元素2了。 +一样的想法。 + + +6,5,2全看过了以后咋办。 +加起来呗。 +加起来,就是【6,5,2】上位,所踏过的所有小命啊! + +我这解释太生动了。因为耗费了好长时间思考... +``` +/* +Given a permutation which contains no repeated number, find its index in all the permutations of these numbers, which are ordered in lexicographical order. The index begins at 1. + +Example +Given [1,2,4], return 1. +*/ + +/* + Thoughts: + Given some thoughts online: + Take one example 4,2,1 (totally reversed, worse case) + Assume array length = n + 1. If sorted, it should be 1,2,4. In permutation, when does 4 appear? It appears after 1xx,2xx. That is, it appears after all of the smaller ones show up. + 2. For each number smaller than 4 in later indexes, each of them all have (n-1)! mutations. n -1 is actaully: current 0-based index of 4. + Therefore, for head number 4, there are: + #ofSmallerNumber * (curr 0-base index)! + 3. For loop on each num, calculate + SUM {#ofSmallerNumber * (index i)!} + 4. To store #ofSmallerNum, put hashmap. For example, for number 4, with index 2: There are 2 numbers smaller than 4, which are 1 and 2. + + O(n^2) +*/ + +public class Solution { + + public long permutationIndex(int[] A) { + if (A == null || A.length == 0) { + return 0; + } + HashMap map = new HashMap(); + int n = A.length; + long rst = 0; + //O(n^2) + //Count: in A, after A[i], how many smaller nums are left behind. + for (int i = 0; i < n; i++) { + int count = 0; + for (int j = i + 1; j < n; j++) { + if (A[j] < A[i]) { + count++; + } + } + map.put(A[i], count); + } + //O(n^2) + for (int i = 0; i < n; i++) { + long fact = 1; + for (int j = (n - i - 1); j >= 1; j--) { + fact *= j; + } + rst += map.get(A[i]) * fact; + } + return rst + 1; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Permutations II.java b/Others/old records/LintCode-Backup/Permutations II.java new file mode 100644 index 0000000..38a1438 --- /dev/null +++ b/Others/old records/LintCode-Backup/Permutations II.java @@ -0,0 +1,131 @@ +要unique,就是要确定visit过的那个index不被重新visit. +一个办法就是给一个visited queue。 和queue在所有的地方一同populate. 然后visited里面存得时visited indexes +``` +/* +Given a list of numbers with duplicate number in it. Find all unique permutations. + +Example +For numbers [1,2,2] the unique permutations are: + +[ + + [1,2,2], + + [2,1,2], + + [2,2,1] + +] + +Challenge +Do it without recursion. + +Tags Expand +LinkedIn Recursion + +*/ + + +/* + Thoughts: + Same as Permutation I, but check also check for duplicate, if duplicate, continue + Use a queue of marker to track if that index has been visited. +*/ +class Solution { + /** + * @param nums: A list of integers. + * @return: A list of unique permutations. + */ + public ArrayList> permuteUnique(ArrayList nums) { + ArrayList> rst = new ArrayList>(); + if (nums == null || nums.size() == 0) { + return rst; + } + Queue> queue = new LinkedList>(); + Queue> visited = new LinkedList>(); + ArrayList list; + ArrayList mark; + for (int i = 0; i < nums.size(); i++) { + list = new ArrayList(); + list.add(nums.get(i)); + mark = new ArrayList(); + mark.add(i); + queue.offer(new ArrayList(list)); + visited.offer(new ArrayList(mark)); + } + + while (!queue.isEmpty()) { + list = queue.poll(); + mark = visited.poll(); + if (list.size() == nums.size()) { + if (!rst.contains(list)) { + rst.add(new ArrayList(list)); + } + continue; + } + for (int i = 0; i < nums.size(); i++) { + if (!mark.contains(i)) { + list.add(nums.get(i)); + mark.add(i); + queue.offer(new ArrayList(list)); + visited.offer(new ArrayList(mark)); + list.remove(list.size() - 1); + mark.remove(mark.size() - 1); + } + } + } + + return rst; + } +} + + + + +/* + +Thougths: +Use regular recursion, use a mark[] to make sure the same charactor at same postion won't be reused +Do a backtrack on the dfs, to make sure a element has same chance of 'selectd' or 'non-solectd' +*/ +class Solution { + /** + * @param nums: A list of integers. + * @return: A list of unique permutations. + */ + public ArrayList> permuteUnique(ArrayList nums) { + ArrayList> rst = new ArrayList>(); + if (nums == null || nums.size() == 0) { + return rst; + } + Collections.sort(nums); + boolean[] mark = new boolean[nums.size()]; + dfs(nums, new ArrayList(), rst, mark); + return rst; + } + + public void dfs (ArrayList nums, ArrayList list, + ArrayList> rst, boolean[] mark) { + if (list.size() == nums.size()) { + rst.add(new ArrayList(list)); + return; + } + + for (int i = 0; i < nums.size(); i++) { + if (mark[i] || (i != 0 && mark[i - 1] && nums.get(i) == nums.get(i - 1))) { + continue; + } + list.add(nums.get(i)); + mark[i] = true; + dfs(nums, list, rst, mark); + list.remove(list.size() - 1); + mark[i] = false; + } + return; + } +} + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Permutations.java b/Others/old records/LintCode-Backup/Permutations.java new file mode 100644 index 0000000..89bfab2 --- /dev/null +++ b/Others/old records/LintCode-Backup/Permutations.java @@ -0,0 +1,130 @@ +还是递归: 取,或者不取。 +Iterative: 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍。 +``` +/* +Given a list of numbers, return all possible permutations. + +Example +For nums [1,2,3], the permutaions are: + +[ + + [1,2,3], + + [1,3,2], + + [2,1,3], + + [2,3,1], + + [3,1,2], + + [3,2,1] + +] + +Challenge +Do it without recursion + +Tags Expand +Recursion Search + +*/ + +/* + Thoughts: 12.07.2015 recap + recursive: + pass list, rst, nums. + when list.size() == nums.size(), add to rst and return. + Need to re-add all of those non-added spots. So do for loop everytime to try all possible ways. + note: check if !list.contains(candiate). +*/ + +/* + 12.07.2015 + Now, do a non-recursive. + Set up a queue, add all elements into it. + give a size/level variable to keep track of cycle. + +*/ +class Solution { + /** + * @param nums: A list of integers. + * @return: A list of permutations. + */ + public ArrayList> permute(ArrayList nums) { + ArrayList> rst = new ArrayList>(); + if (nums == null || nums.size() == 0) { + return rst; + } + Queue> queue = new LinkedList>(); + ArrayList list; + for (int num : nums) { + list = new ArrayList(); + list.add(num); + queue.offer(new ArrayList(list)); + } + + while (!queue.isEmpty()) { + list = queue.poll(); + if (list.size() == nums.size()) { + rst.add(new ArrayList(list)); + continue; + } + for (int i = 0; i < nums.size(); i++) { + if (!list.contains(nums.get(i))) { + list.add(nums.get(i)); + queue.offer(new ArrayList(list)); + list.remove(list.size() - 1); + } + } + } + + return rst; + } +} + + + +/* + +Thinking Process: +1. Very similar idea: choose or not choose (1 / 0) + A key point is: when jumpped into next level of recursion, the 'list' will surely be filled up until it reach the max length. + That is: when 'not choose', the empty seat will be filled eventually with points not existed in 'list'. +2. The recursion does not end before the list is filled. +3. A for loop is doiong the filling of blank. Any order/combination will occur. +*/ + +class Solution { + /** + * @param nums: A list of integers. + * @return: A list of permutations. + */ + public ArrayList> permute(ArrayList nums) { + ArrayList> rst = new ArrayList>(); + if (nums == null || nums.size() == 0) { + return rst; + } + ArrayList list = new ArrayList(); + helper(rst, list, nums); + return rst; + } + + public void helper(ArrayList> rst, ArrayList list, ArrayList nums) { + if (list.size() == nums.size()) { + rst.add(new ArrayList(list)); + return ; + } + for (int i = 0; i < nums.size(); i++) { + if (!list.contains(nums.get(i))) { + list.add(nums.get(i)); + helper(rst, list, nums); + list.remove(list.size() - 1); + } + } + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Plus One.java b/Others/old records/LintCode-Backup/Plus One.java new file mode 100644 index 0000000..4659f19 --- /dev/null +++ b/Others/old records/LintCode-Backup/Plus One.java @@ -0,0 +1,95 @@ +/* +Given a non-negative number represented as an array of digits, plus one to the number. + +The digits are stored such that the most significant digit is at the head of the list. + + +Example +Given [1,2,3] which represents 123, return [1,2,4]. + +Given [9,9,9] which represents 999, return [1,0,0,0]. + +Tags Expand +Array + +*/ + +public class Solution { + public int[] plusOne(int[] digits) { + if(digits.length==0) return digits; + + digits[digits.length-1] += 1; + //Check index digit.length-1 to 1 + for(int i = digits.length-1; i>0; i--){ + if(digits[i] == 10){ + digits[i]=0; + digits[i-1]+=1; + } + else return digits; + } + + //Check index 0. If ==0, set it to 0 and carry over 1 + if(digits[0]==10){ + int[] output = new int[digits.length+1]; + output[0] = 1; + output[1] = 0; + for(int i=2; i productExcludeItself(ArrayList A) { + if (A == null || A.size() == 0) { + return null; + } + ArrayList rst = new ArrayList(); + for (int i = 0; i < A.size(); i++) { + long num = 1; + for (int j = 0; j < A.size(); j++) { + if (j != i) { + num *= A.get(j); + } + } + rst.add(num); + } + return rst; + } +} + + diff --git a/Others/old records/LintCode-Backup/QuickSort.java b/Others/old records/LintCode-Backup/QuickSort.java new file mode 100644 index 0000000..9f1a879 --- /dev/null +++ b/Others/old records/LintCode-Backup/QuickSort.java @@ -0,0 +1,64 @@ +代码是不难的. + +首先partition. 返还一个partition的那个中间点的位置。 +然后劈开两半。 +前后各自 quick sort, recursively + +注意:在partition里面, 比较的时候nums[start] < pivot, nums[end]>pivot, 如果写成了 <= 会 stack overflow. + + +但是: 在partition array那个题目里面,第二个 nums[end] >= pivot, 是要去加上这个 ‘=’的 +``` +/* +Quick Sort a array. + +Self test. + +*/ + +class Solution{ + public void quickSort(int[] nums){ + if (nums == null || nums.length == 0) { + return; + } + sortHelper(nums, 0, nums.length - 1); + } + + public void sortHelper(int[] nums, int start, int end){ + if (start >= end) { + return; + } else { + + int partitionPoint = partition(nums, start, end); + sortHelper(nums, start, partitionPoint - 1); + sortHelper(nums, partitionPoint, end); + } + } + + public void swap(int[] nums, int x, int y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + } + + public int partition(int[] nums, int start, int end){ + int mid = start + (end - start)/2; + + int pivot = nums[mid]; + while (start <= end) { + while (start <= end && nums[start] < pivot) { + start++; + } + while (start <= end && nums[end] > pivot) { + end--; + } + if (start <= end) { + swap(nums, start, end); + start++; + end--; + } + } + return start; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Recover Rotated Sorted Array.java b/Others/old records/LintCode-Backup/Recover Rotated Sorted Array.java new file mode 100644 index 0000000..67030ac --- /dev/null +++ b/Others/old records/LintCode-Backup/Recover Rotated Sorted Array.java @@ -0,0 +1,73 @@ +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 +``` +/* +Given a rotated sorted array, recover it to sorted array in-place. + +Example +[4, 5, 1, 2, 3] -> [1, 2, 3, 4, 5] + +Challenge +In-place, O(1) extra space and O(n) time. + +Clarification +What is rotated array: + + - For example, the orginal array is [1,2,3,4], The rotated array of it can be [1,2,3,4], [2,3,4,1], [3,4,1,2], [4,1,2,3] + +Tags Expand +Array Sorted Array + + +*/ + +/* + Thougths: 12.08.2015. + Same idea as previous solution. Just re-write to practice + 1. reverse function. + 2. find break point. + 3. reverse! + - reverse 1st part + - reverse 2nd part + - reverse all +*/ +public class Solution { + public void recoverRotatedSortedArray(ArrayList nums) { + if (nums == null || nums.size() == 0) { + return; + } + for (int i = 0; i < nums.size() - 1; i++) { + if (nums.get(i) > nums.get(i + 1)) { + reverse(nums, 0, i); + reverse(nums, i + 1, nums.size() - 1); + reverse(nums, 0, nums.size() - 1); + return; + } + } + + } + //reverse certain range + public void reverse(ArrayList nums, int start, int end){ + for (int i = start, j = end; i < j; i++,j--) { + int temp = nums.get(i); + nums.set(i, nums.get(j)); + nums.set(j, temp); + } + } +} + +/* +Thinking process: + Create a reverse function + When there is a wrong position, start 3 steps: + 1. Reverse 1st part. + 2. Reverse 2nd part. + 3. Reverse them all. +*/ + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Rehashing.java b/Others/old records/LintCode-Backup/Rehashing.java new file mode 100644 index 0000000..37f69b4 --- /dev/null +++ b/Others/old records/LintCode-Backup/Rehashing.java @@ -0,0 +1,126 @@ +/* +The size of the hash table is not determinate at the very beginning. If the total size of keys is too large (e.g. size >= capacity / 10), we should double the size of the hash table and rehash every keys. Say you have a hash table looks like below: + +size=3, capacity=4 + +[null, 21, 14, null] + ↓ ↓ + 9 null + ↓ + null +The hash function is: + +int hashcode(int key, int capacity) { + return key % capacity; +} +here we have three numbers, 9, 14 and 21, where 21 and 9 share the same position as they all have the same hashcode 1 (21 % 4 = 9 % 4 = 1). We store them in the hash table by linked list. + +rehashing this hash table, double the capacity, you will get: + +size=3, capacity=8 + +index: 0 1 2 3 4 5 6 7 +hash : [null, 9, null, null, null, 21, 14, null] +Given the original hash table, return the new hash table after rehashing . + +Example +Given [null, 21->9->null, 14->null, null], + +return [null, 9->null, null, null, null, 21->null, 14->null, null] + +Note +For negative integer in hash table, the position can be calculated as follow: + +C++/Java: if you directly calculate -4 % 3 you will get -1. You can use function: a % b = (a % b + b) % b to make it is a non negative integer. +Python: you can directly use -1 % 3, you will get 2 automatically. +Tags Expand +LintCode Copyright Hash Table + +Thoughts: +1. Loop through the hashtable[] and find longest, calcualte new capacity +2. rehash + +*/ + +/** + * Definition for ListNode + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ + public class Solution { + /** + * @param hashTable: A list of The first node of linked list + * @return: A list of The first node of linked list which have twice size + */ + public ListNode[] rehashing(ListNode[] hashTable) { + if (hashTable == null || hashTable.length == 0) { + return hashTable; + } + //Find longest size + /* + int longest = 0; + for (int i = 0; i < hashTable.length; i++) { + ListNode node = hashTable[i]; + int count = 0; + while (node != null) { + count++; + node = node.next; + } + longest = Math.max(longest, count); + }*/ + //Calculate new capacity + //Just to clarify, this problem asks to double the hashtable size, rather than 'longest' times longer. + int capacity = hashTable.length * 2; + if (capacity == hashTable.length) { + return hashTable; + } + + ListNode[] rst = new ListNode[capacity]; + for (int i = 0; i < hashTable.length; i++) { + ListNode node = hashTable[i]; + while (node != null) { + ListNode newNode = new ListNode(node.val); + int hCode = hashcode(newNode.val, capacity); + if (rst[hCode] == null) { + rst[hCode] = newNode; + } else { + ListNode move = rst[hCode]; + while (move.next != null) { + move = move.next; + } + move.next = newNode; + } + node = node.next; + } + } + + return rst; + } + + public int hashcode(int key, int capacity) { + if (key < 0) { + return (key % capacity + capacity) % capacity; + } else { + return key % capacity; + } + } +}; + + + + + + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Remove Duplicates from Sorted Array.java b/Others/old records/LintCode-Backup/Remove Duplicates from Sorted Array.java new file mode 100644 index 0000000..48c960c --- /dev/null +++ b/Others/old records/LintCode-Backup/Remove Duplicates from Sorted Array.java @@ -0,0 +1,60 @@ +Remove Duplicate from Array 不同于remove from linked list. + +LinkedList里面我们是最好不要动node.val的,直接把node去掉。 +而array我们很难直接把node去掉,又不能用新array,那么就要: + +把不重复的element一个个放到最前面。 + + +这个思想跟merge two sorted array (其中一个后续非常长的array可以放下arr1,arr2) 类似。 +就是找个不会事后mess up,不会去动得index,把满足条件的element 填进去。这样保证了in place. + +* 有个反向思维:remove duplicate,实际上也是找unique elements, and insert into original array + +``` +/*31% Accepted +Given a sorted array, remove the duplicates in place such that each element appear only once and return the new length. + +Do not allocate extra space for another array, you must do this in place with constant memory. + +For example, +Given input array A = [1,1,2], + +Your function should return length = 2, and A is now [1,2]. + +Example +Tags Expand +Array Two Pointers + +Thinking Process: +Two pointers, i, j +i: the regular for loop +j - jumper: +If nums[i] == nums[j], do not update nums[j]. It stays the same. +after i++, compare nums[j] with the new nums[i]. If not the same, means the position after j can have a new number that’s not duplicate of nums[j]. In this case, we update nums[j] = nums[i]. +Do this until regular i runs out. +At the end, j is actually the last index of new Array. j + 1 is the size. +*/ + +public class Solution { + /** + * @param A: a array of integers + * @return : return an integer + */ + public int removeDuplicates(int[] nums) { + if (nums == null || nums.length == 0) { + return 0; + } + int i = 0; + int j = 0; + for (i = 0; i < nums.length; i++) { + if (nums[j] != nums[i]) { + nums[++j] = nums[i]; + } + } + return j + 1; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Remove Duplicates from Sorted List II.java b/Others/old records/LintCode-Backup/Remove Duplicates from Sorted List II.java new file mode 100644 index 0000000..5da207e --- /dev/null +++ b/Others/old records/LintCode-Backup/Remove Duplicates from Sorted List II.java @@ -0,0 +1,65 @@ +斩草除根。 +多个node,check node.next ?= node.next.next +``` +/* +26% Accepted +Given a sorted linked list, delete all nodes that have duplicate numbers, +leaving only distinct numbers from the original list. + +Example +Given 1->2->3->3->4->4->5, return 1->2->5. +Given 1->1->1->2->3, return 2->3. + +Tags Expand +Linked List + +Thinking process: +Create a dummyHead +User a pointer node to run through the list +Similar to Remove Duplicates from Sorted List I, but checking next and next.next +If there is a match on head.next && head.next.next, delete head.next node and link to next ones until a different node appear +return dummyHead.next +*/ + + +/** + * Definition for ListNode + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + /** + * @param ListNode head is the head of the linked list + * @return: ListNode head of the linked list + */ + public static ListNode deleteDuplicates(ListNode head) { + if (head == null) { + return head; + } + ListNode dummyHead = new ListNode(0); + dummyHead.next = head; + ListNode node = dummyHead; + + while (node.next != null && node.next.next != null) { + if (node.next.val == node.next.next.val) { + int duplicatedVal = node.next.val; + while (node.next != null && node.next.val == duplicatedVal) { + node.next = node.next.next; + } + } else { + node = node.next; + } + } + + return dummyHead.next; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Remove Duplicates from Sorted List.java b/Others/old records/LintCode-Backup/Remove Duplicates from Sorted List.java new file mode 100644 index 0000000..5815642 --- /dev/null +++ b/Others/old records/LintCode-Backup/Remove Duplicates from Sorted List.java @@ -0,0 +1,63 @@ +一旦node.next 和node是重复,跳 +``` +/* +40% 通过 +Given a sorted linked list, delete all duplicates such that each element appear only once. + +样例 +Given 1->1->2, return 1->2. +Given 1->1->2->3->3, return 1->2->3. + +标签 Expand +Linked List + +Thinking process: +check head null +Use dummy node to reserve head +while everything when head.next is not null +compare head.val == head.next.val? +If so, head.next = head.next.next +*/ + +/** + * Definition for ListNode + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + /** + * @param ListNode head is the head of the linked list + * @return: ListNode head of linked list + */ + public static ListNode deleteDuplicates(ListNode head) { + if (head == null) { + return head; + } + ListNode node = head; + while (node.next != null) { + if (node.val == node.next.val) { + node.next = node.next.next; + } else { + node = node.next; + } + } + return head; + } +} + + + + +/* +Use two pointers: +http://gongxuns.blogspot.com/2012/12/leetcode-remove-duplicates-from-sorted_11.html +*/ + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Remove Duplicates from Unsorted List.java b/Others/old records/LintCode-Backup/Remove Duplicates from Unsorted List.java new file mode 100644 index 0000000..1600519 --- /dev/null +++ b/Others/old records/LintCode-Backup/Remove Duplicates from Unsorted List.java @@ -0,0 +1,155 @@ +基本方法: O(n) sapce, time +遍历。 +遇到duplicate(可能多个), while直到node.next不是duplicate. +接下去,既然不是duplicate,那就add 进 set + + +如果不用extra memory, do it in place: +那就要sort linked list. 用merge sort. + +复习merge sort: +1. find middle. +2. recursively: right = sort(mid.next); left = sort(head). +3. within sort(), at the end call merge(left, right) +``` +/* +Remove Duplicates from Unsorted List + +Write code to remove duplicates from an unsorted linked list. + +Example +Given 1->3->2->1->4. + +Return 1->3->2->4 + +Challenge +(hard) How would you solve this problem if a temporary buffer is not allowed? In this case, you don't need to keep the order of nodes. +Tags Expand +Linked List +*/ + +/* + Thoughts: + Basic: use a hashSet to store node. Skip duplicate +*/ +/** + * Definition for ListNode + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ + +public class Solution { + /** + * @param head: The first node of linked list. + * @return: head node + */ + public ListNode removeDuplicates(ListNode head) { + if (head == null) { + return head; + } + HashSet set = new HashSet(); + ListNode node = head; + set.add(node.val); + while (node != null) { + while (node.next != null && set.contains(node.next.val)) { + node.next = node.next.next; + } + if (node.next != null) { + set.add(node.next.val); + } + node = node.next; + } + return head; + } +} + + +//No extra buffer: merge Sort the linked list, then loop it through + +public class Solution { + /** + * @param head: The first node of linked list. + * @return: head node + */ + public ListNode removeDuplicates(ListNode head) { + if (head == null) { + return head; + } + ListNode newHead = sort(head); + ListNode dummy = newHead; + while (newHead != null) { + if (newHead.next != null && newHead.val == newHead.next.val) { + newHead.next = newHead.next.next; + } + newHead = newHead.next; + } + return dummy; + } + //Merge Sort's code + + /* + Merge sort, using findMidle and merge. + */ + public ListNode sort(ListNode node) { + if (node == null || node.next == null) { + return node; + } + ListNode mid = findMiddle(node); + ListNode right = sort(mid.next); + mid.next = null; + ListNode left = sort(node); + return merge(left, right); + } + + + /* + Two pointer slow, fast + When fast pointer reaches end, return mid; + */ + public ListNode findMiddle(ListNode node){ + ListNode slow = node; + ListNode fast = node.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + /* + Merge the two list based on value + */ + public ListNode merge(ListNode n1, ListNode n2) { + ListNode node = new ListNode(0); + ListNode dummy = node; + while (n1 != null && n2 != null) { + if (n1.val < n2.val) { + node.next = n1; + n1 = n1.next; + } else { + node.next = n2; + n2 = n2.next; + } + node = node.next; + } + if (n1 != null) { + node.next = n1; + } + if (n2 != null) { + node.next = n2; + } + return dummy.next; + } + + +} + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Remove Linked List Elements.java b/Others/old records/LintCode-Backup/Remove Linked List Elements.java new file mode 100644 index 0000000..b5aaad8 --- /dev/null +++ b/Others/old records/LintCode-Backup/Remove Linked List Elements.java @@ -0,0 +1,54 @@ +如果match. parent.next = node.next. +如果不match, parent 和 node 一起移动 +``` +/* +Remove all elements from a linked list of integers that have value val. + +Have you met this question in a real interview? Yes +Example +Given 1->2->3->3->4->5->3, val = 3, you should return the list as 1->2->4->5 + +Tags Expand +Linked List +*/ + +/* +Thoughts: +While loop through. Maintain a parent, so it can be used to skip current node. +*/ + + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +public class Solution { + /** + * @param head a ListNode + * @param val an integer + * @return a ListNode + */ + public ListNode removeElements(ListNode head, int val) { + if (head == null) { + return head; + } + ListNode parent = new ListNode(0); + parent.next = head; + ListNode dummy = parent; + while (head != null) { + if (head.val == val) { + parent.next = head.next; + } else { + parent = parent.next; + } + head = head.next; + } + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Remove Node in Binary Search Tree.java b/Others/old records/LintCode-Backup/Remove Node in Binary Search Tree.java new file mode 100644 index 0000000..bffbc98 --- /dev/null +++ b/Others/old records/LintCode-Backup/Remove Node in Binary Search Tree.java @@ -0,0 +1,135 @@ +/* +Given a root of Binary Search Tree with unique value for each node. Remove the node with given value. If there is no such a node with given value in the binary search tree, do nothing. You should keep the tree still a binary search tree after removal. + +Example +Given binary search tree: + + 5 + + / \ + + 3 6 + + / \ + +2 4 + +Remove 3, you can either return: + + 5 + + / \ + + 2 6 + + \ + + 4 + +or : + + 5 + + / \ + + 4 6 + + / + +2 + +Tags Expand +LintCode Copyright Binary Search Tree + +Thoughts: +We can think of a couple different cases: where is that target node to remove? It can be root, a child (a couple more situations) +Note: before going futher, remember the technique to rip off parent node. In a binary tree, L > parent > R, so always find the L's right-most node and replace parent. +Cases1: +0. Root is target: find L's right-most node, replace root. +case1. A node with 2 null children: set target.parent.L/R = null +case2. A node with just just left node: set target.parent.L/R = target.right +case3. A node with left ndoe != null: find target.right-most node's left side X and replace target.parent.L/R.value = X.value. Remove set that X to null. + +Divide into 2 major task: +1. Find the target node, and it's parent. +2. Remove that node (most complex logic) +*/ +public class Solution { + public TreeNode removeNode(TreeNode root, int value) { + if (root == null || (root.left == null && root.right == null)) { + return null; + } + TreeNode dummy = new TreeNode(0);; + dummy.left = root; + //Find node + TreeNode parent = findTargetParent(dummy, root, value); + TreeNode child; + if (parent.left != null && parent.left.val == value) { + child = parent.left; + } else if (parent.right != null && parent.right.val == value) { + child = parent.right; + } else { + return dummy.left; + } + //Delete that node: + deleteTargetNode(parent, child); + return dummy.left; + } + + + //Find target node + public TreeNode findTargetParent(TreeNode parent, TreeNode node, int value){ + if (node == null || node.val == value) { + return parent; + } + + if (value < node.val) { + return findTargetParent(node, node.left, value); + } else { + return findTargetParent(node, node.right, value); + } + } + //Delete node + public void deleteTargetNode(TreeNode parent, TreeNode target) { + //Case1 + case2: (target.L == null && target.R == null) || (target.R == null && target.L != null) + if (target.right == null) { + if (parent.left == target) { + parent.left = target.left; + } else { + parent.right = target.left; + } + } else {//Case3: when target.right != null + TreeNode replaceNode = target.right; + TreeNode replaceParent = target; + while(replaceNode.left != null) { + replaceParent = replaceNode; + replaceNode = replaceNode.left; + } + //Remove replaceNode from replaceParent + if (replaceParent.left == replaceNode) {//Usually it'll be replaceParent.left + replaceParent.left = replaceNode.right; + } else {//Sometimes when target.left == null, than means replaceParent.right will be replaceNode (while loop didn't start at all) + replaceParent.right = replaceNode.right; + } + + //Remove target from parent: not sure it's left or right node of parent + if (parent.left == target) { + parent.left = replaceNode; + } else { + parent.right = replaceNode; + } + + replaceNode.left = target.left; + replaceNode.right = target.right; + } + } + +} + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Remove Nth Node From End of List.java b/Others/old records/LintCode-Backup/Remove Nth Node From End of List.java new file mode 100644 index 0000000..cc8a57a --- /dev/null +++ b/Others/old records/LintCode-Backup/Remove Nth Node From End of List.java @@ -0,0 +1,62 @@ +/* +Given a linked list, remove the nth node from the end of list and return its head. + +Note +The minimum number of nodes in list is n. + +Example +Given linked list: 1->2->3->4->5->null, and n = 2. +After removing the second node from the end, the linked list becomes 1->2->3->5->null. +Challenge +O(n) time + +Tags Expand +Two Pointers Linked List + +Thinking process: +Very similar to 'Nth to last node'. Except, have a pre pointer to keep track of the previous node of 'nth to last'. +Also have a dummy.next to store the beginning of the list; +*/ +/** + * Definition for ListNode. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int val) { + * this.val = val; + * this.next = null; + * } + * } + */ +public class Solution { + /** + * @param head: The first node of linked list. + * @param n: An integer. + * @return: The head of linked list. + */ + ListNode removeNthFromEnd(ListNode head, int n) { + if (head == null || n < 0) { + return null; + } + int count = 0; + ListNode dummy = new ListNode(0); + ListNode pre = new ListNode(0); + pre.next = head; + dummy = pre; + ListNode node = head; + while (node != null && count < n) { + node = node.next; + count++; + } + while (node != null) { + node = node.next; + head = head.next; + pre = pre.next; + } + pre.next = head.next; + return dummy.next; + } +} + + + diff --git a/Others/old records/LintCode-Backup/Reorder List.java b/Others/old records/LintCode-Backup/Reorder List.java new file mode 100644 index 0000000..ada5af2 --- /dev/null +++ b/Others/old records/LintCode-Backup/Reorder List.java @@ -0,0 +1,93 @@ +/* +24% 通过 +Given a singly linked list L: L0→L1→…→Ln-1→Ln, +reorder it to: L0→Ln→L1→Ln-1→L2→Ln-2→… + +You must do this in-place without altering the nodes' values. + + + +样例 +For example, +Given 1->2->3->4->null, reorder it to 1->4->2->3->null. + +标签 Expand +Linked List + +Thinking Process: +Similar to sort list: +find middle. +reverse last section +merge(head, mid) alternatively by using index % 2. +Append whatever left from the 2 lists +Note: re-order in place, does not necessarily mean you can create any variable. As long as the variable is O(1), it should be fine. +*/ + +/** + * Definition for ListNode. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int val) { + * this.val = val; + * this.next = null; + * } + * } + */ +public class Solution { + private ListNode reverse(ListNode head) { + ListNode reversedList = null; + while (head != null) { + ListNode temp = head.next; + head.next = reversedList; + reversedList = head; + head = temp; + } + return reversedList; + } + + private void merge(ListNode head1, ListNode head2) { + ListNode dummy = new ListNode(0); + int index = 0; + while (head1 != null && head2 != null) { + if (index % 2 == 0) { + dummy.next = head1; + head1 = head1.next; + } else { + dummy.next = head2; + head2 = head2.next; + } + dummy = dummy.next; + index += 1; + } + if (head1 != null) { + dummy.next = head1; + } else if (head2 != null) { + dummy.next = head2; + } + } + + private ListNode findMiddle(ListNode head) { + ListNode slow = head; + ListNode fast = head.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + + public void reorderList(ListNode head) { + if (head == null || head.next == null) { + return; + } + + ListNode mid = findMiddle(head); + ListNode tail = reverse(mid.next); + mid.next = null; + + merge(head, tail); + } +} + diff --git a/Others/old records/LintCode-Backup/Reverse Integer.java b/Others/old records/LintCode-Backup/Reverse Integer.java new file mode 100644 index 0000000..37c51bc --- /dev/null +++ b/Others/old records/LintCode-Backup/Reverse Integer.java @@ -0,0 +1,37 @@ +/* +Reverse digits of an integer. Returns 0 when the reversed integer overflows (signed 32-bit integer). + +Example +Given x = 123, return 321 + +Given x = -123, return -321 + +Tags Expand +Integer + +Thoughts: +1. Use long to capture the result. If > Integer.MAX_VALUE,return 0; +2. Use string to reverse, the conver to long +3. use string builder to reverse string + +*/ + + +public class Solution { + /** + * @param n the integer to be reversed + * @return the reversed integer + */ + public int reverseInteger(int n) { + long num = (long)n; + int sign = n > 0 ? 1 : -1; + String rst = new StringBuilder(Math.abs(num)+"").reverse().toString(); + num = Long.parseLong(rst) * sign; + + if (num > Integer.MAX_VALUE || num < Integer.MIN_VALUE) { + return 0; + } else { + return (int)num; + } + } +} diff --git a/Others/old records/LintCode-Backup/Reverse Linked List II .java b/Others/old records/LintCode-Backup/Reverse Linked List II .java new file mode 100644 index 0000000..6a75f48 --- /dev/null +++ b/Others/old records/LintCode-Backup/Reverse Linked List II .java @@ -0,0 +1,88 @@ +遍历到M前, +存一下那个点, +从M开始, for loop, reverse [m~n]。 然后把三段链接在一起。 + + +``` +/* +28% Accepted +Reverse a linked list from position m to n. + +Note +Given m, n satisfy the following condition: 1 = m = n = length of list. + +Example +Given 1->2->3->4->5->NULL, m = 2 and n = 4, return 1->4->3->2->5->NULL. + +Challenge +Reverse it in-place and in one-pass + +Tags Expand +Linked List + +Thinking process: +0. Use dummy node to store head +1. Find mNode, store the front nodes +2. Reverse from mNode to nNode +3. Link front, middle, end nodes together +Note, when doing reverse, always: + - reversedList = 1st item + - postNode = 2nd item + - store 3rd item in temp: temp = postNode.next +*/ + +/** + * Definition for ListNode + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + /** + * @param ListNode head is the head of the linked list + * @oaram m and n + * @return: The head of the reversed ListNode + */ + public ListNode reverseBetween(ListNode head, int m, int n) { + if (head == null || m >= n) { + return head; + } + + ListNode dummyNode = new ListNode(0); + dummyNode.next = head; + head = dummyNode; + ListNode nodeFront = null; + + + for (int countM = 1; countM < m; countM++) { + if (head == null) { + return head; + } + head = head.next; + } + nodeFront = head; + ListNode mNode = head.next; //Head is Mth node. Reserve it + ListNode reversedList = mNode; + ListNode postNode = mNode.next; + for (int countN = m; countN < n; countN++) { + ListNode temp = postNode.next; + postNode.next = reversedList; + reversedList = postNode; + postNode = temp; + } + //List front, middle and end section + nodeFront.next = reversedList; + mNode.next = postNode; + + return dummyNode.next; + } +} + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Reverse Linked List.java b/Others/old records/LintCode-Backup/Reverse Linked List.java new file mode 100644 index 0000000..174b727 --- /dev/null +++ b/Others/old records/LintCode-Backup/Reverse Linked List.java @@ -0,0 +1,38 @@ +E + +建立新list。每次把newList append 在current node的后面。 +用head来循环所有node。 + +``` +/* +Reverse a linked list. + +Have you met this question in a real interview? Yes +Example +For linked list 1->2->3, the reversed linked list is 3->2->1 + +Challenge +Reverse it in-place and in one-pass + +Tags Expand +Linked List Facebook Uber +*/ + +//Use empty node, add to tail, append empty node to next node. keep going like that +public class Solution { + public ListNode reverseList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode newList = null; + + while (head != null) { + ListNode temp = head.next; + head.next = newList; + newList = head; + head = temp; + } + return newList; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Reverse Words in a String.java b/Others/old records/LintCode-Backup/Reverse Words in a String.java new file mode 100644 index 0000000..934ef0b --- /dev/null +++ b/Others/old records/LintCode-Backup/Reverse Words in a String.java @@ -0,0 +1,145 @@ +几种不同的方法flip: +坑: 1. 结尾不能有空格。 2. 注意,如果Input是 ‘ ’的话,split以后就啥也没有了。check split以后 length == 0 +``` +/* +23% Accepted +Given an input string, reverse the string word by word. + +For example, +Given s = "the sky is blue", +return "blue is sky the". + +Example +Clarification +What constitutes a word? +A sequence of non-space characters constitutes a word. + +Could the input string contain leading or trailing spaces? +Yes. However, your reversed string should not contain leading or trailing spaces. +How about multiple spaces between two words? +Reduce them to a single space in the reversed string. +Tags Expand +String +*/ + + + + + +/* + Thoughts:12.08.2015 + Have multiple two other ways to do it: + 1. flip all,then flip each individual word; + 2. break into parts and append from end to beginning. + For simplicity of code, try the appending from behind. +*/ +public class Solution { + public String reverseWords(String s) { + if (s == null || s.length() == 0 || s.indexOf(" ") == -1) { + return s; + } + + String[] strs = s.split(" "); + if (strs.length == 0) { + return s; + } + StringBuffer sb = new StringBuffer(); + + for (int i = strs.length - 1; i >= 0; i--) { + sb.append(strs[i] + " "); + } + + return sb.substring(0, sb.length() - 1).toString(); + } +} + + + + +/* +Thinking Process: +1. Reverse it like reversing a int array +2. Use Split into arrays. +3. When reversing, make sure not empty string "" +*/ +public class Solution { + /** + * @param s : A string + * @return : A string + */ + public String reverseWords(String s) { + String[] strs = s.split(" "); + for (int i = 0, j = strs.length - 1; i < j; i++, j--) { + String temp = new String(strs[j]); + strs[j] = new String(strs[i]); + strs[i] = temp; + } + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < strs.length; i++){ + if (strs[i].length() > 0) { + sb.append(strs[i]); + if (i < strs.length - 1) { + sb.append(" "); + } + } + } + return sb.toString(); + } +} + + +/* + + Thoughts: 12.08.2015, + Try the reverse method on string + This is the leaset favor: because it creates too much trouble. + Simply reverse the words broken into String[] with split(" ") is much easier + + Only good practice here: the reverse with StringBuffer +*/ + // I LOVE YOU +public class Solution { + public String reverseWords(String s) { + //Reverse the contents of the string buffer + public void reverse(StringBuffer sb, int start, int end) { + for (int i = start, j = end; i < j; i++,j--) { + char temp = sb.charAt(i); + sb.setCharAt(i, sb.charAt(j)); + sb.setCharAt(j, temp); + } + } + + if (s == null || s.length() == 0 || s.indexOf(" ") == -1) { + return s; + } + s = s.trim();//no head && tail " " + if (s.length() == 0) { + return s; + } + + StringBuffer sb = new StringBuffer(); + for (String str : s.split(" ")) { + if (str.trim().length() != 0) { + sb.append(str + " "); + } + } + + reverse(sb, 0, sb.length() - 1); + sb.append(" "); + int start = 1; + int end = start + sb.substring(start).indexOf(" "); + //Process all words separate by space + while (end != -1 && start < end) { + reverse(sb, start, end - 1); + start = end + 1; + end = start + (sb.substring(start)).indexOf(" "); + } + + return sb.toString().trim(); + } + + + +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Rotate Image.java b/Others/old records/LintCode-Backup/Rotate Image.java new file mode 100644 index 0000000..c832148 --- /dev/null +++ b/Others/old records/LintCode-Backup/Rotate Image.java @@ -0,0 +1,51 @@ +M + +找到个转角度的规律公式。用一个temp。in place. + +``` +/* +You are given an n x n 2D matrix representing an image. +Rotate the image by 90 degrees (clockwise). + +Have you met this question in a real interview? Yes +Example +Given a matrix + +[ + [1,2], + [3,4] +] +rotate it by 90 degrees (clockwise), return + +[ + [3,1], + [4,2] +] +Challenge +Do it in-place. + +Tags Expand +Cracking The Coding Interview Matrix +*/ + + +//in matrix, to find next position: r = c; c = (w - r). Work on the equation => oldR = w - c, oldC = r +//In pace: do loop, change 4 at once. do a quater of the matrix +public class Solution { + public void rotate(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return; + } + int width = matrix.length; + for (int i = 0; i < width/2; i++) { + for (int j = 0; j < Math.ceil(width/2.0); j++) { + int temp = matrix[i][j]; + matrix[i][j] = matrix[width - 1 - j][i]; + matrix[width - 1 - j][i] = matrix[width - 1 - i][width - 1 - j]; + matrix[width - 1 - i][width - 1 - j] = matrix[j][width - 1 - i]; + matrix[j][width - 1 - i] = temp; + } + } + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Rotate List.java b/Others/old records/LintCode-Backup/Rotate List.java new file mode 100644 index 0000000..7c64d9f --- /dev/null +++ b/Others/old records/LintCode-Backup/Rotate List.java @@ -0,0 +1,64 @@ + +/* Given a list, rotate the list to the right by k places, where k is non-negative. + +Example +Given 1->2->3->4->5->null and k=2 +return 4->5->1->2->3->null +Tags Expand +Basic Implementation Linked List + +Thining process: +Two pointers. +First pointer move k steps. +Then 2 pointers start moving together. When 1st pointer reaches the end, then 2nd pointer should be in middle. +Let 2nd pointer be head, and move original head to tail of the list + +*/ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + /** + * @param head: the List + * @param k: rotate to the right k places + * @return: the list after rotation + */ + public ListNode rotateRight(ListNode head, int k) { + if (head == null || k == 0) { + return head; + } + //Check length + int length = 0; + ListNode dummy = head; + while(dummy != null) { + dummy = dummy.next; + length++; + } + k = k % length; + //Store dummy as 1 node before tail + dummy = new ListNode(0); + dummy.next = head; + head = dummy; + for (int i = 0; i < k; i++) { + head = head.next; + } + //Move 2 pointers. When head reaches end, tail.next will be at the newHead + ListNode tail = dummy; + while (head.next != null) { + head = head.next; + tail = tail.next; + } + head.next = dummy.next;//Link old Head to the end, form circle + dummy.next = tail.next;//Link tail.next as new head. tail should be end point. + tail.next = null;//add null to end point tail + return dummy.next; + } +} diff --git a/Others/old records/LintCode-Backup/Rotate String.java b/Others/old records/LintCode-Backup/Rotate String.java new file mode 100644 index 0000000..b1840f9 --- /dev/null +++ b/Others/old records/LintCode-Backup/Rotate String.java @@ -0,0 +1,54 @@ +还是三步rotate. +有个坑:offset可能很长,那么要%length,才能得到真正需要rotate的部分。 +Note: rotate 一个 full length之后,是string 不变 +``` +/* +17% Accepted +Given a string and an offset, rotate string by offset. (rotate from left to right) + +Example +Given "abcdefg" + +for offset=0, return "abcdefg" + +for offset=1, return "gabcdef" + +for offset=2, return "fgabcde" + +for offset=3, return "efgabcd" + +... + + +Tags Expand +String +*/ + +public class Solution { + /* + * param A: A string + * param offset: Rotate string with offset. + * return: Rotated string. + */ + public char[] rotateString(char[] A, int offset) { + if (A == null || A.length == 0) return A; + offset = offset % (A.length); + reverse(A, 0, A.length - offset - 1); + reverse(A, A.length - offset, A.length - 1); + reverse(A, 0, A.length - 1); + return A; + } + + + //Helper function: reverse certain range of array + public void reverse(char[] A, int start, int end) { + for (int i = start, j = end; i < j; i++, j--) { + char temp = A[j]; + A[j] = A[i]; + A[i] = temp; + } + } +}; + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Search Insert Position.java b/Others/old records/LintCode-Backup/Search Insert Position.java new file mode 100644 index 0000000..c259195 --- /dev/null +++ b/Others/old records/LintCode-Backup/Search Insert Position.java @@ -0,0 +1,104 @@ +一般的binary search. +在结尾判断该return 哪个position。 +``` +/* +28% Accepted +Given a sorted array and a target value, return the index if the target is found. +If not, return the index where it would be if it were inserted in order. + +You may assume no duplicates in the array. + +Example +[1,3,5,6], 5 → 2 +[1,3,5,6], 2 → 1 +[1,3,5,6], 7 → 4 +[1,3,5,6], 0 → 0 + +Tags Expand +Binary Search Array Sorted Array +*/ + +/* + Recap 12.08.2015 + Find the occurance of the target, return it. + If not found, at the point, start + 1 = end + return the insert position at start + 1 +*/ + +public class Solution { + + public int searchInsert(int[] A, int target) { + if (A == null || A.length == 0) {//Insert at 0 position + return 0; + } + int start = 0; + int end = A.length - 1; + int mid = start + (end - start)/2; + + while (start + 1 < end) { + mid = start + (end - start)/2; + if (A[mid] == target) { + return mid; + } else if (A[mid] > target) { + end = mid; + } else { + start = mid; + } + } + + if (A[start] >= target) { + return start; + } else if (A[start] < target && target <= A[end]) { + return end; + } else { + return end + 1; + } + } +} + + + + +//older version +public class Solution { + /** + * param A : an integer sorted array + * param target : an integer to be inserted + * return : an integer + */ + public int searchInsert(ArrayList A, int target) { + // write your code here + int start = 0; + int end = A.size() - 1; + int mid; + if (A == null || A.size() == 0 || target <= A.get(0)) { + return 0; + } + //find the last number less than target + while (start + 1 < end) { + mid = start + (end - start) / 2; + if (A.get(mid) == target) {//Since no duplicates, return when found + return mid; + } else if (A.get(mid) < target) { + start = mid; + } else { + end = mid; + } + } + //Always 2 elements left to check, first:start, second: end + if (A.get(end) == target) { + return end; + } + if (A.get(end) < target) { + return end + 1; + } + if (A.get(start) == target) { + return start; + } + return start + 1; + + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Search Range in Binary Search Tree .java b/Others/old records/LintCode-Backup/Search Range in Binary Search Tree .java new file mode 100644 index 0000000..b7a9423 --- /dev/null +++ b/Others/old records/LintCode-Backup/Search Range in Binary Search Tree .java @@ -0,0 +1,60 @@ +/* +37% Accepted +Given two values k1 and k2 (where k1 < k2) and a root pointer to a Binary Search Tree. Find all the keys of tree in range k1 to k2. i.e. print all x such that k1<=x<=k2 and x is a key of given BST. Return all the keys in ascending order. + +Example +For example, if k1 = 10 and k2 = 22, then your function should print 12, 20 and 22. + + 20 + + / \ + + 8 22 + + / \ + +4 12 + +Tags Expand +Binary Tree Binary Search Tree +Recursive + +Thinking Process: +Find lowest and turn around. + If don’t hit the ground, keep digging: + if (root.val > k1) dig into root.left +Check current +Find maximum and turn around. + If don’t hit the ceiling, keep climbing: + if (root.val < k2) climb to root.right + +*/ + +public class Solution { + /** + * @param root: The root of the binary search tree. + * @param k1 and k2: range k1 to k2. + * @return: Return all keys that k1<=key<=k2 in ascending order. + */ + public ArrayList searchRange(TreeNode root, int k1, int k2) { + ArrayList result = new ArrayList(); + helper(result, root, k1, k2); + return result; + } + + public void helper(ArrayList result, TreeNode root, int k1, int k2) { + if (root == null) { + return; + } + if (root.val > k1) { + helper(result, root.left, k1, k2); + } + if (root.val >= k1 && root.val <= k2) { + result.add(root.val); + } + if (root.val < k2) { + helper(result, root.right, k1, k2); + } + } +} + diff --git a/Java/Search Rotated in Sorted Array II.java b/Others/old records/LintCode-Backup/Search Rotated in Sorted Array II.java similarity index 100% rename from Java/Search Rotated in Sorted Array II.java rename to Others/old records/LintCode-Backup/Search Rotated in Sorted Array II.java diff --git a/Java/Search Rotated in Sorted Array.java b/Others/old records/LintCode-Backup/Search Rotated in Sorted Array.java similarity index 100% rename from Java/Search Rotated in Sorted Array.java rename to Others/old records/LintCode-Backup/Search Rotated in Sorted Array.java diff --git a/Others/old records/LintCode-Backup/Search a 2D Matrix II.java b/Others/old records/LintCode-Backup/Search a 2D Matrix II.java new file mode 100644 index 0000000..c9c8710 --- /dev/null +++ b/Others/old records/LintCode-Backup/Search a 2D Matrix II.java @@ -0,0 +1,80 @@ +根据给定的性质,找个点(left-bottom):每次有任何情况,只能往一个方向运行。 +每次删掉一行,或者一列 +``` +/* +Write an efficient algorithm that searches for a value in an m x n matrix, return the occurrence of it. + +This matrix has the following properties: + + * Integers in each row are sorted from left to right. + + * Integers in each column are sorted from up to bottom. + + * No duplicate integers in each row or column. + +Example +Consider the following matrix: + +[ + + [1, 3, 5, 7], + + [2, 4, 7, 8], + + [3, 5, 9, 10] + +] + +Given target = 3, return 2. + +Challenge +O(m+n) time and O(1) extra space + +*/ + +/* + Thoughts: 11.29.2015. LintCode updated. Practice again. + Understand that: + 1. Each row has exactly 1 instance of that integer, no duplicates. + 2. Each row begins with smallest number. So, if matrix[x][y] < target, first thing to do is x++. + 3. Each col ends with largest number. So if matrix[x][y] > target, + (no need to care x++ since it's alreay too large for this row), then simply just y--. + 4. If match, next step will be x--,y++. + x-- because it has to change a row; + y++ because [x-1, y] can't be the target since no duplicate in column + Beneftis of going from bottown-left: No matter which condition, always have 1 possible way to move. +*/ + +public class Solution { + /** + * @param matrix: A list of lists of integers + * @param: A number you want to search in the matrix + * @return: An integer indicate the occurrence of target in the given matrix + */ + public int searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return 0; + } + int row = matrix.length; + int col = matrix[0].length; + int x = row - 1; + int y = 0; + int count = 0; + while (x >= 0 && y < col) { + if (matrix[x][y] < target) { + y++; + } else if (matrix[x][y] > target) { + x--; + } else {//matrix[x][y] == target + count++; + x--; + y++; + } + } + return count; + } +} + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Search a 2D Matrix.java b/Others/old records/LintCode-Backup/Search a 2D Matrix.java new file mode 100644 index 0000000..23759e1 --- /dev/null +++ b/Others/old records/LintCode-Backup/Search a 2D Matrix.java @@ -0,0 +1,135 @@ +一行一行是从小到大,连续的: +2D转1D。 +Binary Search +``` +/* +Write an efficient algorithm that searches for a value in an m x n matrix. + +This matrix has the following properties: + + * Integers in each row are sorted from left to right. + + * The first integer of each row is greater than the last integer of the previous row. + +Example +Consider the following matrix: + +[ + + [1, 3, 5, 7], + + [10, 11, 16, 20], + + [23, 30, 34, 50] + +] + +Given target = 3, return true. + +Challenge +O(log(n) + log(m)) time + +Tags Expand +Binary Search Matrix + + +*/ + +/* + Thoughts: 11.29.2015 + The problem is updated on LintCode. Practice again. + Convert into 1D array. Binary Search +*/ +public class Solution { + /** + * @param matrix, a list of lists of integers + * @param target, an integer + * @return a boolean, indicate whether matrix contains target + */ + public boolean searchMatrix(int[][] matrix, int target) { + if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { + return false; + } + int row = matrix.length; + int col = matrix[0].length; + int start = 0; + int end = row * col - 1; + int mid; + + while (start + 1 < end) { + mid = start + (end - start)/2; + int num = matrix[mid/col][mid%col]; + if (target == num) { + return true; + } else if (num < target) { + start = mid; + } else { + end = mid; + } + } + + return (matrix[start/col][start%col] == target || matrix[end/col][end%col] == target); + } +} + + + + + + + + +/* +Thinking process: +0. The elements are unique, no duplicates +Treat it as a 1-D array +Do binary search + 1. check head element + 2. find the element until only start & end element left + 3. Deal with start and end +*/ + +public class Solution { + /** + * @param matrix, a list of lists of integers + * @param target, an integer + * @return a boolean, indicate whether matrix contains target + */ + //Turn 2D array into 1D array and perform regular binary search + public boolean searchMatrix(ArrayList> matrix, int target) { + if(matrix.size() == 0) { + return false; + } + // write your code + int rows = matrix.size(); + int cols = matrix.get(0).size(); + int start = 0; + int end = rows * cols - 1; + int mid; + + while (start + 1 < end) {//This will leave exact 1 start and 1 end element for next code section + mid = start + (end - start) / 2; + int midVal = matrix.get(mid / cols).get(mid % cols); //trick to get element + + if (midVal == target) { + return true; + } else if (midVal < target) { + start = mid; + } else { + end = mid; + } + }//end while + + //Deal with the 1 start and 1 end element + int startVal = matrix.get(start / cols).get(start % cols); + int endVal = matrix.get(end / cols).get(end % cols); + if (startVal == target || endVal == target) { + return true; + } else { + return false; + } + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Search for a Range.java b/Others/old records/LintCode-Backup/Search for a Range.java new file mode 100644 index 0000000..e498cb5 --- /dev/null +++ b/Others/old records/LintCode-Backup/Search for a Range.java @@ -0,0 +1,174 @@ +前后跑2个while loop。 找first/last occurance +``` +/* +Given a sorted array of integers, find the starting and ending position of a given target value. + +Your algorithm's runtime complexity must be in the order of O(log n). + +If the target is not found in the array, return [-1, -1]. + +Example +Given [5, 7, 7, 8, 8, 10] and target value 8, +return [3, 4]. + +Tags Expand +Binary Search Array Sorted Array +*/ + +/* + Recap: 12.08.2015 + input sorted + 2 binary search while loop. + First one, keep looking for 1st occurnace. + Second one, keep looking for alst occurance. + + check border case: + A = [] + + +*/ +public class Solution { + public int[] searchRange(int[] A, int target) { + int[] rst = new int[]{-1, -1}; + if (A == null || A.length == 0) { + return rst; + } + int start = 0; + int end = A.length - 1; + int mid = start + (end - start)/2; + + //1st occurance. + int first = 0; + while (start + 1 < end) { + mid = start + (end - start)/2; + if (A[mid] == target) { + if (mid - 1 >= 0 && A[mid - 1] == target) { + end = mid; + continue; + } + break; + } else if (A[mid] < target) { + start = mid; + } else { + end = mid; + } + } + if (A[start] == target) { + first = start; + } else if (A[mid] == target) { + first = mid; + } else if (A[end] == target) { + first = end; + } else { + return rst; + } + rst[0] = first; + + //check last occurance + int last = 0; + start = first; + end = A.length - 1; + while (start + 1 < end) { + mid = start + (end - start)/2; + if (A[mid] == target) { + if (mid + 1 < A.length && A[mid + 1] == target) { + start = mid; + continue; + } + break; + } else if (A[mid] < target) { + start = mid; + } else { + end = mid; + } + } + if (A[end] == target) { + last = end; + } else if (A[mid] == target) { + last = mid; + } else if (A[start] == target) { + last = start; + } else { + last = first; + } + rst[1] = last; + + return rst; + } +} + + + + + + + + + + +//Older solution: +public class Solution { + /** + *@param A : an integer sorted array + *@param target : an integer to be inserted + *return : a list of length 2, [index1, index2] + */ + public ArrayList searchRange(ArrayList A, int target) { + // write your code here + int start = 0; + int end = A.size() - 1; + int mid; + ArrayList bound = new ArrayList(); + bound.add(-1); + bound.add(-1); + + if ( A.size() == 0) { + return bound; + } + + //Left: + while (start + 1 < end) { + mid = start + (end - start) / 2; + if (A.get(mid) < target) { + start = mid; + } else if (A.get(mid) > target) { + end = mid; + } else { + end = mid;//Run to left + } + } + if (A.get(start) == target) {//Run to left + bound.set(0, start); + } else if (A.get(end) == target) { + bound.set(0, end); + } else { + return bound; + } + + //Right: + start = 0; + end = A.size() - 1; + while (start + 1 < end) { + mid = start + (end - start) / 2; + if (A.get(mid) < target) { + start = mid; + } else if (A.get(mid) > target) { + end = mid; + } else { + start = mid;//Run to right + } + } + if (A.get(end) == target) {//Run to right + bound.set(1, end); + } else if (A.get(start) == target) { + bound.set(1, start); + } else { + return bound; + } + + return bound; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Segment Tree Build II.java b/Others/old records/LintCode-Backup/Segment Tree Build II.java new file mode 100644 index 0000000..4c8b25f --- /dev/null +++ b/Others/old records/LintCode-Backup/Segment Tree Build II.java @@ -0,0 +1,87 @@ +给的是Array。注意找区间内的max, assign给区间。 +想:区间break到底,像segment tree build I 里面一样最终也就是 start==end。也就是max=A[start] 或者A[end] +往上一层,其实max就是比较左右孩子。然后一次递推。每次找max其实都是在两个sub-tree里面比较sub-tree的max。 +这就好做了: +先分,找到left/right,比较max,在create current node,再append到当前node上面。 + +实际上是depth-first, 自底向上建立起的。 +``` +/* +The structure of Segment Tree is a binary tree which each node has two attributes start and end +denote an segment / interval. +start and end are both integers, they should be assigned in following rules: +The root's start and end is given by build method. +The left child of node A has start=A.left, end=(A.left + A.right) / 2. +The right child of node A has start=(A.left + A.right) / 2 + 1, end=A.right. +if start equals to end, there will be no children for this node. +Implement a build method with a given array, so that we can create a corresponding segment tree +with every node value represent the corresponding interval max value in the array, +return the root of this segment tree. + + +Example +Given [3,2,1,4]. The segment tree will be: + [0, 3] (max = 4) + / \ + [0, 1] (max = 3) [2, 3] (max = 4) + / \ / \ +[0, 0](max = 3) [1, 1](max = 2)[2, 2](max = 1) [3, 3] (max = 4) +Clarification +Segment Tree (a.k.a Interval Tree) is an advanced data structure which can support queries like: +which of these intervals contain a given point +which of these points are in a given interval +See wiki: Segment Tree Interval Tree +Tags Expand +Segment Tree +*/ + +/* +Thoughts: +Similar to Segment Tree, where the start and end are just : 0 and arrary.length -1. +For max value: How about a backgracking? SO: split and recursive build tree first; +each recursion, compare the returnning left/right child's max value, and then put into curr node's max. +Note: each leaf node will have start==end, so their max is really easy to figure out. +*/ + +/** + * Definition of SegmentTreeNode: + * public class SegmentTreeNode { + * public int start, end, max; + * public SegmentTreeNode left, right; + * public SegmentTreeNode(int start, int end, int max) { + * this.start = start; + * this.end = end; + * this.max = max + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + *@param A: a list of integer + *@return: The root of Segment Tree + */ + public SegmentTreeNode build(int[] A) { + if (A == null || A.length == 0) { + return null; + } + return buildHelper(0, A.length - 1, A); + } + + public SegmentTreeNode buildHelper(int start, int end, int[] A) { + if (start > end) { + return null; + } else if (start == end) { + return new SegmentTreeNode(start, end, A[end]); + } + + SegmentTreeNode leftChild = buildHelper(start, (start + end)/2, A); + SegmentTreeNode rightChild = buildHelper((start + end)/2 + 1, end, A); + + SegmentTreeNode node = new SegmentTreeNode(start, end, Math.max(leftChild.max, rightChild.max)); + node.left = leftChild; + node.right = rightChild; + return node; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Segment Tree Build.java b/Others/old records/LintCode-Backup/Segment Tree Build.java new file mode 100644 index 0000000..32660c8 --- /dev/null +++ b/Others/old records/LintCode-Backup/Segment Tree Build.java @@ -0,0 +1,65 @@ +按定义: +左孩子:(A.left, (A.left+A.rigth)/2) +右孩子:((A.left+A.rigth)/2+1, A.right) +``` +/* +The structure of Segment Tree is a binary tree which each node has two attributes start and end denote an segment / interval. + +start and end are both integers, they should be assigned in following rules: + +The root's start and end is given by build method. +The left child of node A has start=A.left, end=(A.left + A.right) / 2. +The right child of node A has start=(A.left + A.right) / 2 + 1, end=A.right. +if start equals to end, there will be no children for this node. +Implement a build method with two parameters start and end, so that we can create a corresponding segment tree with every node has the correct start and end value, return the root of this segment tree. + +Example +Given start=0, end=3. The segment tree will be: + + [0, 3] + / \ + [0, 1] [2, 3] + / \ / \ + [0, 0] [1, 1] [2, 2] [3, 3] + +Given start=1, end=6. The segment tree will be: + + [1, 6] + / \ + [1, 3] [4, 6] + / \ / \ + [1, 2] [3,3] [4, 5] [6,6] + / \ / \ +[1,1] [2,2] [4,4] [5,5] + +Clarification +Segment Tree (a.k.a Interval Tree) is an advanced data structure which can support queries like: + +which of these intervals contain a given point +which of these points are in a given interval +See wiki: +Segment Tree +Interval Tree + +Tags Expand +LintCode Copyright Binary Tree Segment Tree +*/ + +public class Solution { + /** + *@param start, end: Denote an segment / interval + *@return: The root of Segment Tree + */ + public SegmentTreeNode build(int start, int end) { + if (start > end) { + return null; + } else if (start == end) { + return new SegmentTreeNode(start, end); + } + SegmentTreeNode node = new SegmentTreeNode(start, end); + node.left = build(start, (start + end)/2); + node.right = build((start + end)/2 + 1, end); + return node; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Segment Tree Modify.java b/Others/old records/LintCode-Backup/Segment Tree Modify.java new file mode 100644 index 0000000..f506afe --- /dev/null +++ b/Others/old records/LintCode-Backup/Segment Tree Modify.java @@ -0,0 +1,88 @@ +在segment tree里面找index. +找到就update it with value. +每次下一层以后,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下。 +最后轮回到头顶,头顶一下包括头顶,就全部都是max了。 +``` +/* +For a Maximum Segment Tree, which each node has an extra value max to store the maximum value in this node's interval. + +Implement a modify function with three parameter root, index and value to change the node's value with [start, end] = [index, index] to the new given value. Make sure after this change, every node in segment tree still has the max attribute with the correct value. + +Example +For segment tree: + + [1, 4, max=3] + / \ + [1, 2, max=2] [3, 4, max=3] + / \ / \ +[1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=3] + +if call modify(root, 2, 4), we can get: + + [1, 4, max=4] + / \ + [1, 2, max=4] [3, 4, max=3] + / \ / \ +[1, 1, max=2], [2, 2, max=4], [3, 3, max=0], [4, 4, max=3] + +or call modify(root, 4, 0), we can get: + + [1, 4, max=2] + / \ + [1, 2, max=2] [3, 4, max=0] + / \ / \ +[1, 1, max=2], [2, 2, max=1], [3, 3, max=0], [4, 4, max=0] +Note +We suggest you finish problem Segment Tree Build and Segment Tree Query first. + +Challenge +Do it in O(h) time, h is the height of the segment tree. + +Tags Expand +LintCode Copyright Binary Tree Segment Tree +*/ + +/* + Thought: + Renew index x with new value, and update the max value alone the way. + 1. Use segmenttree property to find that leaf, which is node.start == node.end == index. + 2. Along the way, whenever going to one segment/interval, compare left.max and right.max again, and update max. +*/ + +/** + * Definition of SegmentTreeNode: + * public class SegmentTreeNode { + * public int start, end, max; + * public SegmentTreeNode left, right; + * public SegmentTreeNode(int start, int end, int max) { + * this.start = start; + * this.end = end; + * this.max = max + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + *@param root, index, value: The root of segment tree and + *@ change the node's value with [index, index] to the new given value + *@return: void + */ + public void modify(SegmentTreeNode root, int index, int value) { + if (root.start == root.end && root.start == index) { + root.max = value; + return; + } + + //Divide and seawrch + int mid = (root.start + root.end)/2; + if (index <= mid) { + modify(root.left, index, value); + } else { + modify(root.right, index, value); + } + root.max = Math.max(root.left.max, root.right.max); + } +} + +``` diff --git a/Others/old records/LintCode-Backup/Segment Tree Query II.java b/Others/old records/LintCode-Backup/Segment Tree Query II.java new file mode 100644 index 0000000..30b6ea6 --- /dev/null +++ b/Others/old records/LintCode-Backup/Segment Tree Query II.java @@ -0,0 +1,88 @@ +和 Segment Tree Query I 以及其他Segment Tree问题没啥区别。这个就是return个count。 +这个题目考的是:validate input source... +搞不清楚LintCode出这个题目干啥。 +``` +/* +For an array, we can build a SegmentTree for it, each node stores an extra attribute count to denote the number of elements in the the array which value is between interval start and end. (The array may not fully filled by elements) + +Design a query method with three parameters root, start and end, find the number of elements in the in array's interval [start, end] by the given root of value SegmentTree. + +Example +For array [0, 2, 3], the corresponding value Segment Tree is: + + [0, 3, count=3] + / \ + [0,1,count=1] [2,3,count=2] + / \ / \ + [0,0,count=1] [1,1,count=0] [2,2,count=1], [3,3,count=1] + +query(1, 1), return 0 + +query(1, 2), return 1 + +query(2, 3), return 2 + +query(0, 2), return 2 + +Note +It is much easier to understand this problem if you finished Segment Tree Buildand Segment Tree Query first. + +Tags Expand +LintCode Copyright Binary Tree Segment Tree +*/ + +/* + Thoughts: + Since SegmentTree is already constructed, just use it to calculate count. + If all on left/right, easy, just return that portion. + If mid is between start,end, deal with it carefully. +*/ + +/** + * Definition of SegmentTreeNode: + * public class SegmentTreeNode { + * public int start, end, count; + * public SegmentTreeNode left, right; + * public SegmentTreeNode(int start, int end, int count) { + * this.start = start; + * this.end = end; + * this.count = count; + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + *@param root, start, end: The root of segment tree and + * an segment / interval + *@return: The count number in the interval [start, end] + */ + public int query(SegmentTreeNode root, int start, int end) { + if (root == null || start > end) { + return 0; + } + if (root.start == start && root.end == end) { + return root.count; + } + //Check if over range + if ((start < root.start && end > root.end) || + (start < root.start && end < root.start) || + (start > root.end && end > root.end)) { + return 0; + } else if (start < root.start && end <= root.end) { + start = root.start; + } else if (start >= root.start && end > root.end) { + end = root.end; + } + int mid = (root.start + root.end)/2; + if (end <= mid) { + return query(root.left, start, end); + } + if (start > mid) { + return query(root.right, start, end); + } + return query(root.left, start, root.left.end) + query(root.right, root.right.start, end); + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Segment Tree Query.java b/Others/old records/LintCode-Backup/Segment Tree Query.java new file mode 100644 index 0000000..05f9749 --- /dev/null +++ b/Others/old records/LintCode-Backup/Segment Tree Query.java @@ -0,0 +1,83 @@ +[start,end]跟mid相比,可能: +全在mid左 +全在mid右 +包含了mid: 这里要特别break into 2 query method + +按定义: +mid = (root.start + root.end)/2 +``` +/* +For an integer array (index from 0 to n-1, where n is the size of this array), in the corresponding SegmentTree, each node stores an extra attribute max to denote the maximum number in the interval of the array (index from start to end). + +Design a query method with three parameters root, start and end, find the maximum number in the interval [start, end] by the given root of segment tree. + +Example +For array [1, 4, 2, 3], the corresponding Segment Tree is: + + [0, 3, max=4] + / \ + [0,1,max=4] [2,3,max=3] + / \ / \ + [0,0,max=1] [1,1,max=4] [2,2,max=2], [3,3,max=3] +query(root, 1, 1), return 4 + +query(root, 1, 2), return 4 + +query(root, 2, 3), return 3 + +query(root, 0, 2), return 4 + +Note +It is much easier to understand this problem if you finished Segment Tree Build first. + +Tags Expand +LintCode Copyright Binary Tree Segment Tree +*/ + +/* + Thoughts: + Search the segment tree, and find the node that matches the interval (start, end) + if (start == root.start && right == root.end) return max; + if end <= (root.left + root.right) / 2 : go left; + if start> (root.left + root.right): go right + However if start <= mid < end, break it into 2 segments and meger afterwards. +*/ +/** + * Definition of SegmentTreeNode: + * public class SegmentTreeNode { + * public int start, end, max; + * public SegmentTreeNode left, right; + * public SegmentTreeNode(int start, int end, int max) { + * this.start = start; + * this.end = end; + * this.max = max + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + *@param root, start, end: The root of segment tree and + * an segment / interval + *@return: The maximum number in the interval [start, end] + */ + public int query(SegmentTreeNode root, int start, int end) { + if (start == root.start && end == root.end) { + return root.max; + } + int mid = (root.start + root.end)/2; + if (end <= mid) { + return query(root.left, start, end); + } + if (start > mid) { + return query(root.right, start, end); + } + //start <= mid && end > mid + int maxLeft = query(root.left, start, root.left.end); + int maxRight = query(root.right, root.right.start, end); + + return Math.max(maxLeft, maxRight); + } +} + +``` \ No newline at end of file diff --git a/Java/Serilization and Deserialization Of Binary Tree.java b/Others/old records/LintCode-Backup/Serilization and Deserialization Of Binary Tree.java similarity index 100% rename from Java/Serilization and Deserialization Of Binary Tree.java rename to Others/old records/LintCode-Backup/Serilization and Deserialization Of Binary Tree.java diff --git a/Others/old records/LintCode-Backup/Single Number II.java b/Others/old records/LintCode-Backup/Single Number II.java new file mode 100644 index 0000000..695efc8 --- /dev/null +++ b/Others/old records/LintCode-Backup/Single Number II.java @@ -0,0 +1,37 @@ +/* +Given 3*n + 1 numbers, every numbers occurs triple times except one, find it. +Example +Given [1,1,2,3,3,3,2,2,4,1] return 4 + +Challenge +One-pass, constant extra space +Thinking process: +Still using bit manipulation. We need to erase all of the 3-appearance number and leave the single number out. A few steps: +Store the final result by continuously bit OR with the result variable. +Want to XOR the 3 numbers, but can’t erase them as if only 2 duplicate numbers:Consider the number as 3-based number, so XOR can be understand this way + when add 3 numbers together, add each individual bit. If the sum is 3, then set it as 0. If not 3, leave as is. +3. Store the bits in a integer array, which simulates a binary version of the integer +4. When each bit’s XOR process finishes, bit OR it with result +*/ + +public class Solution { + public int singleNumberII(int[] A) { + if (A == null || A.length == 0) { + return -1; + } + //present the XOR results in binary format + int[] bits = new int[32]; + int rst = 0; + for (int i = 0; i < 32; i++) { + for (int j = 0; j < A.length; j++){ + //XOR the numbers in a 3-base fashion. Whenever bit[i] has a number 3, set it back to 0. + bits[i] += A[j] >> i & 1; + bits[i] %= 3; + } + //OR it to the result. However, each time only the i - spot is updated with the bits[i]. + rst |= bits[i] << i; + } + return rst; + } +} + diff --git a/Others/old records/LintCode-Backup/Single Number III.java b/Others/old records/LintCode-Backup/Single Number III.java new file mode 100644 index 0000000..556f15b --- /dev/null +++ b/Others/old records/LintCode-Backup/Single Number III.java @@ -0,0 +1,51 @@ +/* +Given 2*n + 2 numbers, every numbers occurs twice except two, find them. + +Example +Given [1,2,2,3,4,4,5,3] return 1 and 5 + +Challenge +O(n) time, O(1) extra space. + +Thinking Process: +The 2 exception must have this feature: a ^ b != 0, since they are different +Still want to do 2n + 1 problem as in Single Number I, then we need to split a and b into 2 groups and deal with two 2n+1 problems +Assume c = a^b, there mush be a bit where a and b has the difference, so that bit in c is 1. +Find this bit position and use it to split the group: shift number in the array by ‘bit-position’ indexes. If the shifted number has 1 at the ‘bit-position’, set it to one group; otherwise to another group. +*/ + +public class Solution { + /** + * @param A : An integer array + * @return : Two integers + */ + public List singleNumberIII(int[] A) { + if (A == null || A.length == 0) { + return null; + } + List rst = new ArrayList(); + int xor = 0; + for (int i = 0; i < A.length; i++) { + xor ^= A[i]; + } + int bitOnePos = 0; + for (int i = 0; i < 32; i++) { + if ((xor >> i & 1) == 1) { + bitOnePos = i; + } + } + int rstA = 0; + int rstB = 0; + for (int i = 0; i < A.length; i++) { + if ((A[i] >> bitOnePos & 1) == 1) { + rstA ^= A[i]; + } else { + rstB ^= A[i]; + } + } + rst.add(rstA); + rst.add(rstB); + return rst; + } +} + diff --git a/Others/old records/LintCode-Backup/Single Number.java b/Others/old records/LintCode-Backup/Single Number.java new file mode 100644 index 0000000..f67f2e2 --- /dev/null +++ b/Others/old records/LintCode-Backup/Single Number.java @@ -0,0 +1,40 @@ +/* +62% Accepted +Given 2*n + 1 numbers, every numbers occurs twice except one, find it. + +Example +Given [1,2,2,1,3,4,3], return 4 + +Challenge +One-pass, constant extra space + +Tags Expand +Greedy + +Manipulate bits: +Thinking process: +One-pass and constant extra space. +since all numbers appears twice, consider them as in bits format. Two identical number XOR will be zero. If we XOR everything double-numbers together, it will be zero. At the end, we use o XOR our target number, the result is actually the target number. +Very smart trick to use bits. +In order to compare from index 0 to the end, we need to extract index 0 first as result before for loop. And start for loop at i = 1. +*/ + +public class Solution { + /** + *@param A : an integer array + *return : a integer + */ + public int singleNumber(int[] A) { + if (A == null || A.length == 0) { + return 0; + } + int rst = A[0]; + for (int i = 1; i < A.length; i++) { + rst = rst ^ A[i]; + } + return rst; + } +} + + + diff --git a/Others/old records/LintCode-Backup/Singleton.java b/Others/old records/LintCode-Backup/Singleton.java new file mode 100644 index 0000000..7a5b4d2 --- /dev/null +++ b/Others/old records/LintCode-Backup/Singleton.java @@ -0,0 +1,45 @@ +/* +Singleton is a most widely used design pattern. If a class has and only has one instance at every moment, we call this design as singleton. For example, for class Mouse (not a animal mouse), we should design it in singleton. + +You job is to implement a getInstance method for given class, return the same instance of this class every time you call this method. + + +Example +In Java: + +A a = A.getInstance(); +A b = A.getInstance(); +a should equal to b. + +Challenge +If we call getInstance concurrently, can you make sure your code could run correctly? + +Tags Expand +LintCode Copyright OO Design + +Thoughts: +... +Was not clear to me. Need to loop up more on synchronized/volatile +Good reference: +http://www.cnblogs.com/EdwardLiu/p/4443230.html + +*/ + + +class Solution { + public static volatile Solution solution = null; + /** + * @return: The same instance of this class every time + */ + public static Solution getInstance() { + if (solution == null) { + synchronized (Solution.class) { + // Double check + if (solution == null) { + solution = new Solution(); + } + } + } + return solution; + } +}; diff --git a/Others/old records/LintCode-Backup/Sliding Window Maximum.java b/Others/old records/LintCode-Backup/Sliding Window Maximum.java new file mode 100644 index 0000000..bb9bde0 --- /dev/null +++ b/Others/old records/LintCode-Backup/Sliding Window Maximum.java @@ -0,0 +1,75 @@ +妙:用deque数据结构(实际上采用LinkedList的形式)来做一个递减的queue. +每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +为啥可以不管不无地剔除? +因为我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +``` +/* +Given an array of n integer with duplicate number, and a moving window(size k), move the window at each iteration from the start of the array, find the maximum number inside the window at each moving. + +Example +For array [1, 2, 7, 7, 8], moving window size k = 3. return [7, 7, 8] + +At first the window is at the start of the array like this + +[|1, 2, 7| ,7, 8] , return the maximum 7; + +then the window move one step forward. + +[1, |2, 7 ,7|, 8], return the maximum 7; + +then the window move one step forward again. + +[1, 2, |7, 7, 8|], return the maximum 8; + +Challenge +o(n) time and O(k) memory + +Tags Expand +LintCode Copyright Deque +*/ + +/* + Thoughts: + Create deque: ArrayDeque, or LinkedList + Maintain a decreasing deque that stores indexes: so peekFirst() is always the maxium value's index + To do that, we need: whenever adding a new value, remove all values in the deque that are smaller than current node. + Loop k elements first, then process the rest + O(n) time, O(k) space +*/ +public class Solution { + /** + * @param nums: A list of integers. + * @return: The maximum number inside the window at each moving. + */ + public ArrayList maxSlidingWindow(int[] nums, int k) { + ArrayList rst = new ArrayList(); + if (nums == null || nums.length == 0 || k < 0) { + return rst; + } + Deque deque = new LinkedList(); + for (int i = 0; i < k; i++) { + while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]) { + deque.pollLast(); + } + deque.offerLast(i); + } + + for (int i = k; i < nums.length; i++) { + rst.add(nums[deque.peekFirst()]); + if (!deque.isEmpty() && deque.peekFirst() <= i - k) { + deque.pollFirst(); + } + while (!deque.isEmpty() && nums[deque.peekLast()] <= nums[i]) { + deque.pollLast(); + } + deque.offerLast(i); + } + + //Last move's result needs to be recorded: + rst.add(nums[deque.peekFirst()]); + return rst; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Sliding Window Median.java b/Others/old records/LintCode-Backup/Sliding Window Median.java new file mode 100644 index 0000000..dd954a3 --- /dev/null +++ b/Others/old records/LintCode-Backup/Sliding Window Median.java @@ -0,0 +1,110 @@ +Median还是用min-heap 和 max-heap。 +移动窗口2step: +1. 加一个数。 +2. 减一个数。 +加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +抽完balance一下。 + +记得: +左边的maxHeap总有 x+1或者x个数字。 +后边minHeap应该一直有x个数字。 + +``` +/* +Given an array of n integer, and a moving window(size k), move the window at each iteration from the start of the array, find the median of the element inside the window at each moving. (If there are even numbers in the array, return the N/2-th number after sorting the element in the window. ) + +Example +For array [1,2,7,8,5], moving window size k = 3. return [2,7,7] + +At first the window is at the start of the array like this + +[ | 1,2,7 | ,8,5] , return the median 2; + +then the window move one step forward. + +[1, | 2,7,8 | ,5], return the median 7; + +then the window move one step forward again. + +[1,2, | 7,8,5 | ], return the median 7; + +Challenge +O(nlog(n)) time + +Tags Expand +LintCode Copyright Heap +*/ + +//NOT DONE +/* +Thoughts: +1. from 0 ~k, populate the maxHeap and minHeap +2. i = k ~ nums.length, add one value and minus one value, calculate median +*/ + +public class Solution { + PriorityQueue minHeap; + PriorityQueue maxHeap; + + /** + * @param nums: A list of integers. + * @return: The median of the element inside the window at each moving. + */ + public ArrayList medianSlidingWindow(int[] nums, int k) { + ArrayList rst = new ArrayList(); + if (nums == null || nums.length == 0 || k <= 0) { + return rst; + } + minHeap = new PriorityQueue(); + maxHeap = new PriorityQueue(10, new Comparator(){ + public int compare(Integer x, Integer y){ + return y - x; + } + }); + maxHeap.offer(nums[0]); + + for (int i = 1; i < k; i++) { + add(nums[i]); + } + + rst.add(maxHeap.peek()); + for (int i = k; i < nums.length; i++) { + add(nums[i]); + remove(nums[i - k]); + rst.add(maxHeap.peek()); + } + return rst; + } + + public void add(int val) { + int preMedian = maxHeap.peek(); + if (val > preMedian) { + minHeap.offer(val); + } else { + maxHeap.offer(val); + } + balance(); + } + + public void remove(int val) { + int preMedian = maxHeap.peek(); + if (val > preMedian) { + minHeap.remove(val); + } else { + maxHeap.remove(val); + } + balance(); + } + + public void balance() { + if (maxHeap.size() > minHeap.size() + 1) { + minHeap.offer(maxHeap.poll()); + } else if (maxHeap.size() < minHeap.size()) { + maxHeap.offer(minHeap.poll()); + } + } + +} + + +``` \ No newline at end of file diff --git a/Java/Sort Color.java b/Others/old records/LintCode-Backup/Sort Color.java similarity index 100% rename from Java/Sort Color.java rename to Others/old records/LintCode-Backup/Sort Color.java diff --git a/Others/old records/LintCode-Backup/Sort Colors II.java b/Others/old records/LintCode-Backup/Sort Colors II.java new file mode 100644 index 0000000..a272ffe --- /dev/null +++ b/Others/old records/LintCode-Backup/Sort Colors II.java @@ -0,0 +1,84 @@ +/* +Given an array of n objects with k different colors (numbered from 1 to k), sort them so that objects of the same color are adjacent, with the colors in the order 1, 2, ... k. + +Example +GIven colors=[3, 2, 2, 1, 4], k=4, your code should sort colors in-place to [1, 2, 2, 3, 4]. + +Note +You are not suppose to use the library's sort function for this problem. + +Challenge +A rather straight forward solution is a two-pass algorithm using counting sort. That will cost O(k) extra memory. + +Can you do it without using extra memory? + +Tags Expand +Two Pointers Sort + +Thoughts (Need to revist and think about this, very interesting) +Doing quick sort partition for K -1 times. +1. Use K - 1 value as pivot +2. Starting from 0, whenever low0, and greater than pivot, high-- +4. Result: only swap when low and high have disagreement on the pivot value. + +*/ + +class Solution { + /** + * @param colors: A list of integer + * @param k: An integer + * @return: nothing + */ + public void sortColors2(int[] colors, int k) { + if (colors == null || colors.length == 0 || k <= 0) { + return; + } + int end = colors.length - 1; + for (int i = 0; i < k - 1; i++) { + end = helper(colors, 0, end, k - i - 1); + } + } + + public void swap(int[] colors, int x, int y){ + int temp = colors[x]; + colors[x] = colors[y]; + colors[y] = temp; + } + + public int helper(int[] colors, int start, int end, int pivot) { + int low = start; + int high = end; + while (low <= high) { + while(low < high && colors[low] <= pivot) { + low++; + } + while(high > 0 && colors[high] > pivot) { + high--; + } + if (low <= high) { + swap(colors, low, high); + low++; + high--; + } + } + return low - 1; + } +} + + + + + + + + + + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Sort Letters by Case.java b/Others/old records/LintCode-Backup/Sort Letters by Case.java new file mode 100644 index 0000000..c136c97 --- /dev/null +++ b/Others/old records/LintCode-Backup/Sort Letters by Case.java @@ -0,0 +1,64 @@ +/* +Given a string which contains only letters. Sort it by lower case first and upper case second. + +Example +For "abAcD", a reasonable answer is "acbAD" + +Note +It's not necessary to keep the original order of lower-case letters and upper case letters. + +Challenge +Do it in one-pass and in-place. + +Tags Expand +String Two Pointers LintCode Copyright Sort + +Thoughts: +Another two pointer sorting. +Difference: use a ASCII code 'a' as the pivot. all the letters that from a ~ z +have bigger integer values, and A~Z have small integer values. +This problem requires lowcase+upperCase, so we'd sort the list from high to low. +NOTE: in the 2 while loop, the it's always having ">=' + +*/ + + +public class Solution { + /** + *@param chars: The letter array you should sort by Case + *@return: void + */ + public void sortLetters(char[] chars) { + if (chars == null || chars.length == 0) { + return; + } + char pivot = 'a'; + int start = 0; int end = chars.length - 1; + while (start <= end) { + while (start <= end && chars[start] >= pivot) { + start++; + } + while (start <= end && chars[end] < pivot) { + end--; + } + if (start <= end) { + char temp = chars[end]; + chars[end] = chars[start]; + chars[start] = temp; + start++; + end--; + } + } + } +} + + + + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Sort List.java b/Others/old records/LintCode-Backup/Sort List.java new file mode 100644 index 0000000..78c4b97 --- /dev/null +++ b/Others/old records/LintCode-Backup/Sort List.java @@ -0,0 +1,135 @@ +Merge sort: + 1. find middle. 快慢指针 + 2. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 + 3. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段。 + 然后mege. + 要recursively call itself. + +Quick sort: +想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ + +但是quick sort不建议用在list上面。 + +排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + +``` +/* +28% Accepted +Sort a linked list in O(n log n) time using constant space complexity. + +Example +Given 1-3->2->null, sort it to 1->2->3->null. + +Tags Expand +Linked List + + +*/ + +/** + * Definition for ListNode. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int val) { + * this.val = val; + * this.next = null; + * } + * } + */ + + + + +/* + Recap:12.09.2015. practice merge sort +Thinking process: +1.Divide and conquer +2. can use merge sort or quick sort. Used merge sort here. +3. Find middle +4. Sort each side recursively. +5. merge function. +Note: when checking null, node != null should be in front of node.next != null. Because if node is alreay null, node.next gives exception. + +*/ + + + +public class Solution { + public ListNode findMiddle(ListNode head) { + ListNode slow = head; + ListNode fast = head.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + public ListNode merge(ListNode left, ListNode right) { + ListNode dummy = new ListNode(0); + ListNode head = dummy; + while (left != null && right != null) { + if (left.val < right.val) { + head.next = left; + left = left.next; + } else { + head.next = right; + right = right.next; + } + head = head.next; + } + if (left != null) { + head.next = left; + } else if (right != null){ + head.next = right; + } + return dummy.next; + } + + /* + * @param head: The head of linked list. + * @return: You should return the head of the sorted linked list, + using constant space complexity. + */ + public ListNode sortList(ListNode head) { + if (head == null || head.next == null) { + return head; + } + ListNode mid = findMiddle(head); + ListNode left = sortList(mid.next); + mid.next = null; + ListNode right = sortList(head); + return merge(left, right); + } + + +} + + +/* + 12.09.2015: http://www.jiuzhang.com/solutions/sort-list/ + Quick sort. + + Didn't do this myself yet. This is because Quick sort in general is not so good for list. + Quick sort requires a lot random access to elements, which turns into worst case O(n) time to access a element + in list. So it can be worse to o(n^2). + So here Merge sort is prefered. + + Quick sort is prefered for array sort, partition. + + Merge sort is prefered for list sort +*/ + +public class Solution { + /* + * @param head: The head of linked list. + * @return: You should return the head of the sorted linked list, + using constant space complexity. + */ + public ListNode sortList(ListNode head) { + // write your code here + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Space Replacement.java b/Others/old records/LintCode-Backup/Space Replacement.java new file mode 100644 index 0000000..cd8ca45 --- /dev/null +++ b/Others/old records/LintCode-Backup/Space Replacement.java @@ -0,0 +1,55 @@ +/* +Write a method to replace all spaces in a string with %20. The string is given in a characters array, you can assume it has enough space for replacement and you are given the true length of the string. + +Example +Given "Mr John Smith", length = 13. + +The string after replacement should be "Mr%20John%20Smith". + +Note +If you are using Java or Python,please use characters array instead of string. + +Challenge +Do it in-place. + +Tags Expand +String Cracking The Coding Interview + +Thoughts: +Overriding the array from the back to front. +This is because as we re-writing the string from the back, stuff at head of the string does not change yet. +This is wonderful:) + +*/ + + +public class Solution { + /** + * @param string: An array of Char + * @param length: The true length of the string + * @return: The true length of new string + */ + public int replaceBlank(char[] string, int length) { + if (string == null || string.length == 0) { + return 0; + } + int count = 0; + for (char c : string) { + if (c == ' ') { + count += 2; + } + } + int lastIndex = length + count - 1; + //from back to front: + for (int i = length - 1; i >= 0; i--) { + if (string[i] == ' ') { + string[lastIndex--] = '0'; + string[lastIndex--] = '2'; + string[lastIndex--] = '%'; + } else { + string[lastIndex--] = string[i]; + } + } + return length + count; + } +} diff --git a/Others/old records/LintCode-Backup/Sqrt(x).java b/Others/old records/LintCode-Backup/Sqrt(x).java new file mode 100644 index 0000000..7a50108 --- /dev/null +++ b/Others/old records/LintCode-Backup/Sqrt(x).java @@ -0,0 +1,45 @@ +/* +Implement int sqrt(int x). + +Compute and return the square root of x. + +Example +sqrt(3) = 1 + +sqrt(4) = 2 + +sqrt(5) = 2 + +sqrt(10) = 3 +Challenge +O(log(x)) + +Tags Expand +Binary Search + +Thinking process: +Binary search. While loop until the head and tail meets. +*/ + +class Solution { + /** + * @param x: An integer + * @return: The sqrt of x + */ + public int sqrt(int x) { + long start = 0; + long end = x; + while (end >= start) { + long mid = start + (end - start) / 2; + if (mid * mid > x) { + end = mid - 1; + } else if (mid * mid < x) { + start = mid + 1; + } else { + return (int)mid; + } + } + //When start > end, while loop ends. That means, end must be the largest possible integer that end^2 is closest to x. + return (int)end; + } +} diff --git a/Others/old records/LintCode-Backup/Stone Game.java b/Others/old records/LintCode-Backup/Stone Game.java new file mode 100644 index 0000000..7ebbf4d --- /dev/null +++ b/Others/old records/LintCode-Backup/Stone Game.java @@ -0,0 +1,83 @@ +这个DP有点诡异. 需要斟酌。 +NOT DONE YET +``` +/* +There is a stone game.At the beginning of the game the player picks n piles of stones in a line. + +The goal is to merge the stones in one pile observing the following rules: + +At each step of the game,the player can merge two adjacent piles to a new pile. + +The score is the number of stones in the new pile. + +You are to determine the minimum of the total score. + +Example +[3, 4, 3] return 17 + +[1, 1, 1, 1] return 8 + +[4, 4, 5, 9] return 43 + +Tags Expand +Dynamic Programming +*/ + +/* +Thoughts: +Based on given outline. +sum[i][j] = stone sum between i and j +f[i][j]: min cost/score if we merge ith pile into jth pile. It can be f[0][1], f[0][2].. or our final result f[0][n-1] +break it by k and check both side f[start][k] and f[start][k+1]; +Tricky: add sum[start][end] at the end. +*/ + +public class Solution { + /** + * @param A an integer array + * @return an integer + */ + public int stoneGame(int[] A) { + // Algorithm: Dynamic Programming + // state: f[start][end] denote the minimum score that we can get if we merge stones from start-th pile to end-th pile into one pile. + // function: f[start][end] = min{f[start][k] + f[k + 1][end] + sum[start][end]} + + if (A == null || A.length == 0) { + return 0; + } + + int n = A.length; + + // initialize f[i][i] + int[][] f = new int[n][n]; + for (int i = 0; i < n; i++) { + f[i][i] = 0; + } + + // preparation for sum[i][j] + int[][] sum = new int[n][n]; + sum[0][0] = A[0]; + for (int i = 0; i < n; i++) { + sum[i][i] = A[i]; + for (int j = i + 1; j < n; j++) { + sum[i][j] = sum[i][j - 1] + A[j]; + } + } + + // dp + // delta is the distance between the start and end + for (int delta = 1; delta < n; delta++) { + for (int start = 0; start < n - delta; start++) { + int end = start + delta; + //initialize f[start][end] + f[start][end] = Integer.MAX_VALUE; + for (int k = start; k < end; k++) { + f[start][end] = Math.min(f[start][end], f[start][k] + f[k + 1][end] + sum[start][end]); + } + } + } + + return f[0][n - 1]; + } +} +``` \ No newline at end of file diff --git a/Java/StrStr.java b/Others/old records/LintCode-Backup/StrStr.java similarity index 100% rename from Java/StrStr.java rename to Others/old records/LintCode-Backup/StrStr.java diff --git a/Others/old records/LintCode-Backup/String to Integer(atoi).java b/Others/old records/LintCode-Backup/String to Integer(atoi).java new file mode 100644 index 0000000..9c7f895 --- /dev/null +++ b/Others/old records/LintCode-Backup/String to Integer(atoi).java @@ -0,0 +1,122 @@ +E + +方法1: 问清情况,一点一点把case都涉及到。 + +方法2: 用regular expression。if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + +``` +/* +Implement function atoi to convert a string to an integer. + +If no valid conversion could be performed, a zero value is returned. + +If the correct value is out of the range of representable values, INT_MAX (2147483647) or INT_MIN (-2147483648) is returned. + +Example +"10" => 10 + +"-1" => -1 + +"123123123123123" => 2147483647 + +"1.0" => 1 + +Tags Expand +Basic Implementation String + +*/ + + +/* +02.02.2016 from leetcode +Requirements for atoi: +The function first discards as many whitespace characters as necessary until the first non-whitespace character is found. Then, starting from this character, takes an optional initial plus or minus sign followed by as many numerical digits as possible, and interprets them as a numerical value. + +The string can contain additional characters after those that form the integral number, which are ignored and have no effect on the behavior of this function. + +If the first sequence of non-whitespace characters in str is not a valid integral number, or if no such sequence exists because either str is empty or it contains only whitespace characters, no conversion is performed. + +If no valid conversion could be performed, a zero value is returned. If the correct value is out of the range of representable values, INT_MAX (2147483647) or INT_MIN (-2147483648) is returned. +*/ +public class Solution { + public int myAtoi(String str) { + + if (str == null) { + return 0; + } + str = str.trim(); + if (str.length() == 0) { + return 0; + } + + char[] arr = str.toCharArray(); + String rst = ""; + for (int i = 0; i < arr.length; i++) { + char c = arr[i]; + if (i == 0 && (c == '+' || c == '-')) { + rst += c; + } else if (c < '0' || c > '9') { + break; + } else { + rst += c; + } + } + if (rst.length() == 0 || rst.equals("+") || rst.equals("-")) { + return 0; + } + if (rst.length() > 11) { + return rst.charAt(0) == '-' ? Integer.MIN_VALUE : Integer.MAX_VALUE; + } + long largeNum = Long.parseLong(rst); + if (largeNum > Integer.MAX_VALUE || largeNum < Integer.MIN_VALUE) { + return largeNum > 0 ? Integer.MAX_VALUE : Integer.MIN_VALUE; + } + return Integer.parseInt(rst); + } +} + + + +/* + +Thoughts: +First idea: why not using Integer.parseInt(str)? Maybe that's too costly, and maybe it does not over all possible integers? +Can we just use a Long.parseLong(str) ? + +Issues to check: +Long is not enough, because we might be getting decimal point. So we can use Double here. +String might have space: remove all " " +String might have char of other kind: check each individual char if isNaN() + +It looks like somewhere we'd need to use regular exp to search for pattern, and remove space. + +Note: need to ask if things like '21lintcode' can be considered as partial-integer and return 21. This is a more complex case, even after reg exp. + +*/ + +public class Solution { + /** + * @param str: A string + * @return An integer + */ + public int atoi(String str) { + if (str == null || str.length() == 0) { + return 0; + } + str = str.replaceAll("\\s",""); + if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")) { + return 0; + } + double rst = Double.parseDouble(str); + if (rst > Integer.MAX_VALUE) { + return Integer.MAX_VALUE; + } else if (rst < Integer.MIN_VALUE) { + return Integer.MIN_VALUE; + } else { + return (int)rst; + } + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Subarray Sum Closest.java b/Others/old records/LintCode-Backup/Subarray Sum Closest.java new file mode 100644 index 0000000..8422953 --- /dev/null +++ b/Others/old records/LintCode-Backup/Subarray Sum Closest.java @@ -0,0 +1,147 @@ +/* +Given an integer array, find a subarray with sum closest to zero. Return the indexes of the first number and last number. + +Example +Given [-3, 1, 1, -3, 5], return [0, 2], [1, 3], [1, 1], [2, 2] or [0, 4] + +Challenge +O(nlogn) time + +Tags Expand +Subarray Sort + +Thoughts: +Took a me a while to think through how to find the closest sum to 0. +Credits should be given to: http://rafal.io/posts/subsequence-closest-to-t.html +*/ + + +class CustomComparator implements Comparator { + public int compare(int[] a, int[] b) { + return Integer.compare(a[0], b[0]); + } +} + +public class Solution { + + /** + * @param nums: A list of integers + * @return: A list of integers includes the index of the first number + * and the index of the last number + */ + public ArrayList subarraySumClosest(int[] nums) { + ArrayList rst = new ArrayList(); + if(nums == null || nums.length == 0) { + return rst; + } + if (nums.length == 1) { + rst.add(0); rst.add(0); + return rst; + } + int[][] culmulate = new int[nums.length][2]; + culmulate[0][0] = nums[0]; + culmulate[0][1] = 0; + for (int i = 1; i < nums.length; i++) { + culmulate[i][0] = culmulate[i - 1][0] + nums[i]; + culmulate[i][1] = i; + } + + Arrays.sort(culmulate, new CustomComparator()); + int min = Integer.MAX_VALUE; + int start = 0; + int end = 0; + for (int i = 0; i < nums.length - 1; i++) { + int temp = culmulate[i + 1][0] - culmulate[i][0]; + if (temp <= min) { + min = temp; + start = culmulate[i][1]; + end = culmulate[i + 1][1]; + } + } + if (start < end) { + rst.add(start + 1); + rst.add(end); + } else { + rst.add(end + 1); + rst.add(start); + } + return rst; + } +} + + + + + +//I also had to run a little java program locally to test/debug: +/* + +import java.lang.*; +import java.util.*; + +class CustomComparator implements Comparator { + public int compare(int[] a, int[] b) { + return Integer.compare(a[0], b[0]); + } +} + +public class test { + public ArrayList subarraySumClosest(int[] nums) { + ArrayList rst = new ArrayList(); + if(nums == null || nums.length == 0) { + return rst; + } + int[][] culmulate = new int[nums.length][2]; + culmulate[0][0] = nums[0]; + culmulate[0][1] = 0; + for (int i = 1; i < nums.length; i++) { + culmulate[i][0] = culmulate[i - 1][0] + nums[i]; + culmulate[i][1] = i; + } + //TEST: + for(int i =0 ; i < nums.length; i++) { + System.out.println("test:" + culmulate[i][0] + " " + culmulate[i][1]); + } + Arrays.sort(culmulate, new CustomComparator()); + for(int i =0 ; i < nums.length; i++) { + System.out.println("sorted:" + culmulate[i][0] + " " + culmulate[i][1]); + } + + int min = Integer.MAX_VALUE; + int start = 0; + int end = 0; + for (int i = 0; i < nums.length - 1; i++) { + int temp = culmulate[i + 1][0] - culmulate[i][0]; + System.out.println(culmulate[i + 1][0] + " minus " + culmulate[i][0] + " = " + temp); + if (temp <= min) { + min = temp; + start = culmulate[i][1]; + end = culmulate[i + 1][1]; + System.out.println("record:" + start + " " + end ); + } + } + System.out.println("min:" + min); + if (start < end) { + rst.add(start + 1); + rst.add(end); + } else { + rst.add(end + 1); + rst.add(start); + } + return rst; + } + + public static void main(String[] args){ + + int[] nums = {6,-4,-8,3,1,7};//{5,10,5,3,2,1,1,-2,-4,3}; + test t = new test(); + ArrayList rst = t.subarraySumClosest(nums); + System.out.println(rst.get(0) + " " + rst.get(1)); + } +} + +*/ + + + + diff --git a/Others/old records/LintCode-Backup/Subarray Sum.java b/Others/old records/LintCode-Backup/Subarray Sum.java new file mode 100644 index 0000000..ab42c31 --- /dev/null +++ b/Others/old records/LintCode-Backup/Subarray Sum.java @@ -0,0 +1,47 @@ +/* +Given an integer array, find a subarray where the sum of numbers is zero. Your code should return the index of the first number and the index of the last number. + +Example +Given [-3, 1, 2, -3, 4], return [0, 2] or [1, 3]. + +Note +There is at least one subarray that it's sum equals to zero. + +Tags Expand +Subarray Hash Table + +Thougths: +Record the sum from (0 ~ a). +Check sum on each index i, when found an existing sum in the hashMap, we are done. +Reason: +If adding all the numbers together, for example if sum[0 ~ a] = -3, ... sum[0 - b] = -3 again, a subarraySum(int[] nums) { + ArrayList rst = new ArrayList(); + if (nums == null || nums.length == 0) { + return rst; + } + int sum = 0; + HashMap map = new HashMap(); + map.put(0, -1); + //we know that sub-array (a,b) has zero sum if SUM(0 ... a-1) = SUM(0 ... b) + for (int i = 0; i < nums.length; i++) { + sum += nums[i]; + if (map.containsKey(sum)) { + rst.add(map.get(sum) + 1); + rst.add(i); + return rst; + } + map.put(sum, i); + }//for + return rst; + } +} diff --git a/Java/Subset.java b/Others/old records/LintCode-Backup/Subset.java similarity index 100% rename from Java/Subset.java rename to Others/old records/LintCode-Backup/Subset.java diff --git a/Others/old records/LintCode-Backup/Subsets II.java b/Others/old records/LintCode-Backup/Subsets II.java new file mode 100644 index 0000000..b73756c --- /dev/null +++ b/Others/old records/LintCode-Backup/Subsets II.java @@ -0,0 +1,161 @@ +递归:找准需要pass along的几个数据结构。 + +Iterative: 写一写,用个Queue. +``` +/* +Given a list of numbers that may has duplicate numbers, return all possible subsets + +Have you met this question in a real interview? Yes +Example +If S = [1,2,2], a solution is: + +[ + [2], + [1], + [1,2,2], + [2,2], + [1,2], + [] +] +Note +Each element in a subset must be in non-descending order. +The ordering between two subsets is free. +The solution set must not contain duplicate subsets. +Challenge +Can you do it in both recursively and iteratively? + +Tags Expand +Recursion +*/ + +/* + Thoughts: 12.07.2015 + try to do non-recursive - iterative + + create a queue, initi with []. put [] into rst as well. + Each time pick/not pick curr element: 2 branch: add back into queue, and try to add to rst(if non-exist) + For loop through all elements in S + + Use Queue +*/ + +class Solution { + /** + * @param S: A set of numbers. + * @return: A list of lists. All valid subsets. + */ + public ArrayList> subsetsWithDup(ArrayList S) { + ArrayList> rst = new ArrayList>(); + if (S == null || S.size() == 0) { + return rst; + } + Collections.sort(S); + Queue> queue = new LinkedList>(); + ArrayList list = new ArrayList(); + queue.offer(new ArrayList(list)); + rst.add(new ArrayList(list)); + + for (int i = 0; i < S.size(); i++) { + int num = S.get(i); + int size = queue.size(); + while(size > 0) { + list = queue.poll(); + //Pick + list.add(num); + if (!rst.contains(list)) { + rst.add(new ArrayList(list)); + } + queue.offer(new ArrayList(list)); + list.remove(list.size() - 1); + //Not pick + queue.offer(new ArrayList(list)); + size--; + } + } + return rst; + } +} + + + + +/* + Thoughts: 12.07.2015. + Do regular subset recursion: pick curr or not pick curr, (rst, list, level, S) + Use a HashMap to mark if the cmobination exists already + Recursive. +*/ +class Solution { + /** + * @param S: A set of numbers. + * @return: A list of lists. All valid subsets. + */ + public ArrayList> subsetsWithDup(ArrayList S) { + ArrayList> rst = new ArrayList>(); + if (S == null || S.size() == 0) { + return rst; + } + Collections.sort(S); + ArrayList list = new ArrayList(); + helper(rst, list, S, 0); + + return rst; + } + + public void helper(ArrayList> rst, ArrayList list, + ArrayList S, int level) { + if (!rst.contains(list)) { + rst.add(new ArrayList(list)); + } + if (level == S.size()) { + return; + } + + //pick curr + list.add(S.get(level)); + helper(rst, list, S, level + 1); + list.remove(list.size() - 1); + + //no pick curr + helper(rst, list, S, level + 1); + } +} + + + + + +//Older version, with for loop: + + +class Solution { + /** + * @param S: A set of numbers. + * @return: A list of lists. All valid subsets. + */ + public ArrayList> subsetsWithDup(ArrayList source) { + // write your code here + ArrayList> output = new ArrayList>(); + ArrayList newList = new ArrayList(); + Collections.sort(source); + subsetHelper(0, source, newList, output); + return output; + } + + + public void subsetHelper(int pos, + ArrayList source, ArrayList newList, + ArrayList> output){ + if (!output.contains(newList)){ + output.add(new ArrayList(newList)); + } + + for (int i = pos; i < source.size(); i++){ + newList.add(source.get(i)); + subsetHelper(i + 1, source, newList, output); + newList.remove(newList.size() - 1); + } + + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Subtree.java b/Others/old records/LintCode-Backup/Subtree.java new file mode 100644 index 0000000..f049e3b --- /dev/null +++ b/Others/old records/LintCode-Backup/Subtree.java @@ -0,0 +1,71 @@ +/* +You have two every large binary trees: T1, with millions of nodes, and T2, with hundreds of nodes. Create an algorithm to decide if T2 is a subtree of T1. + +Example +T2 is a subtree of T1 in the following case: + + 1 3 + / \ / +T1 = 2 3 T2 = 4 + / + 4 +T2 isn't a subtree of T1 in the following case: + + 1 3 + / \ \ +T1 = 2 3 T2 = 4 + / + 4 +Note +A tree T2 is a subtree of T1 if there exists a node n in T1 such that the subtree of n is identical to T2. That is, if you cut off the tree at node n, the two trees would be identical. + +Tags Expand +Recursion Binary Tree + +Thoughts: +When T2 == null, reardless of T1 == null or NO, it can always return true; +WHen T2 != null, T1==null returns false; +1. recursively compare the two nodes: if both null, okay; if everything goes well, get deeper into the child nodes. +2. resursively check subtree: check root.left or root.right comparing with T2. + +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + * @param T1, T2: The roots of binary tree. + * @return: True if T2 is a subtree of T1, or false. + */ + public boolean isSubtree(TreeNode T1, TreeNode T2) { + if (T2 == null) { + return true; + } else if (T1 == null) { + return false; + } else { + return compare(T1, T2) || isSubtree(T1.left, T2) || isSubtree(T1.right, T2); + } + } + //Recursive compare + public boolean compare(TreeNode node1, TreeNode node2) { + if (node1 == null && node2 == null) { + return true; + } + if (node1 == null || node2 == null){ + return false; + } + if (node1.val != node2.val) { + return false; + } + return compare(node1.left, node2.left) && compare(node1.right, node2.right); + } +} diff --git a/Others/old records/LintCode-Backup/Swap Nodes in Pairs.java b/Others/old records/LintCode-Backup/Swap Nodes in Pairs.java new file mode 100644 index 0000000..2c3116b --- /dev/null +++ b/Others/old records/LintCode-Backup/Swap Nodes in Pairs.java @@ -0,0 +1,61 @@ +swap总是confuse. +画三个block, 1,2,3. 连线。 +``` +/* +Swap Nodes in Pairs + +Given a linked list, swap every two adjacent nodes and return its head. + +Example +Given 1->2->3->4, you should return the list as 2->1->4->3. + +Challenge +Your algorithm should use only constant space. You may not modify the values in the list, only nodes itself can be changed. + +Tags Expand +Linked List +*/ + +/* + Thoughts: + 1. swap + 2. move 2 steps, then swap again. + 3. becareful node.next == null, that's the end of list. no swapping. +*/ + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ + +public class Solution { + /** + * @param head a ListNode + * @return a ListNode + */ + public ListNode swapPairs(ListNode head) { + if (head == null) { + return head; + } + ListNode dummy = new ListNode(0); + dummy.next = head; + head = dummy; + while (head.next != null && head.next.next != null) { + ListNode n1 = head.next; + ListNode n2 = head.next.next; + + n1.next = n2.next; + n2.next = n1; + n1 = n2; + + head = head.next.next; + } + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Java/Symmetric Binary Tree.java b/Others/old records/LintCode-Backup/Symmetric Binary Tree.java similarity index 100% rename from Java/Symmetric Binary Tree.java rename to Others/old records/LintCode-Backup/Symmetric Binary Tree.java diff --git a/Others/old records/LintCode-Backup/The Smallest Difference.java b/Others/old records/LintCode-Backup/The Smallest Difference.java new file mode 100644 index 0000000..fd9c4e9 --- /dev/null +++ b/Others/old records/LintCode-Backup/The Smallest Difference.java @@ -0,0 +1,84 @@ +/* +Given two array of integers(the first array is array A, the second array is array B), now we are going to find a element in array A which is A[i], and another element in array B which is B[j], so that the difference between A[i] and B[j] (|A[i] - B[j]|) is as small as possible, return their smallest difference. + + +Example +For example, given array A = [3,6,7,4], B = [2,8,9,3], return 0 + +Challenge +O(n log n) time + +Tags Expand +Two Pointers LintCode Copyright Sort Array +*/ + +/* + Thoughts: + Sort A, B. O(nLogn) + Use smaller array to binarh in longer array. n * logn + +*/ + + +public class Solution { + /** + * @param A, B: Two integer arrays. + * @return: Their smallest difference. + */ + public int smallestDifference(int[] A, int[] B) { + if (A == null || A.length == 0 || B == null || B.length == 0) { + return 0; + } + if (A.length > B.length) { + int[] temp = A; + A = B; + B = temp; + } + Arrays.sort(A); + Arrays.sort(B); + int diff = Integer.MAX_VALUE; + + for (int i = 0; i < A.length; i++) {//10 + int start = 0; + int end = B.length - 1; + int mid; + //Small enhancement + if (B[start] >= A[A.length - 1]) { + return B[start] - A[A.length - 1]; + } + if (A[start] >= B[B.length - 1]) { + return A[start] - B[B.length - 1]; + } + while (start + 1 < end) { + mid = start + (end - start)/2; + if (B[mid] == A[i]) { + return 0; + } else if (mid - 1 >= 0 && B[mid - 1] < A[i] && A[i] < B[mid]) { + diff = Math.min(diff, Math.min(A[i] - B[mid - 1], B[mid] - A[i])); + break; + } else if (mid + 1 < B.length - 1 && B[mid] < A[i] && A[i] < B[mid + 1]) { + diff = Math.min(diff, Math.min(A[i] - B[mid], B[mid + 1] - A[i])); + break; + } else if (B[mid] > A[i]) { + end = mid; + } else { + start = mid; + } + + }//end while + if (start + 1 >= end) { + int min = Math.min(Math.abs(A[i] - B[start]), Math.abs(A[i] - B[end])); + diff = Math.min(diff, min); + } + }//end for + return diff; + } +} + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Top K Frequent Words.java b/Others/old records/LintCode-Backup/Top K Frequent Words.java new file mode 100755 index 0000000..f2f031f --- /dev/null +++ b/Others/old records/LintCode-Backup/Top K Frequent Words.java @@ -0,0 +1,91 @@ +M + +方法1:Brutle force用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 + 注意排序时Collection.sort()的cost是O(nLogk) + + +方法2: Trie && MinHeap屌炸天 + http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +``` +/* +Given a list of words and an integer k, return the top k frequent words in the list. + +Example +Given + +[ + "yes", "lint", "code", + "yes", "code", "baby", + "you", "baby", "chrome", + "safari", "lint", "code", + "body", "lint", "code" +] +for k = 3, return ["code", "lint", "baby"]. + +for k = 4, return ["code", "lint", "baby", "yes"], + +Note +You should order the words by the frequency of them in the return list, the most frequent one comes first. If two words has the same frequency, the one with lower alphabetical order come first. + +Challenge +Do it in O(nlogk) time and O(n) extra space. + +Extra points if you can do it in O(n) time with O(k) extra space. + +Tags Expand +Hash Table Heap Priority Queue +*/ + +/* + Attempt1, Thoughts: + Brutle force + HashMap, size n. O(n) + ArrayList> lists: each entry has a list of strings with index as frequency. O(n) + Adding to result String[], do a Collections.sort(), which cause O(nlogk) with a O(n) for loop on top; It becomes O(nLogk)on average. + Return lists' top k. +*/ +public class Solution { + public String[] topKFrequentWords(String[] words, int k) { + String[] rst = new String[k]; + if (words == null || words.length == 0 || k <= 0) { + return rst; + } + + HashMap map = new HashMap(); + ArrayList> lists = new ArrayList>(); + for (int i = 0; i <= words.length; i++) { + lists.add(new ArrayList()); + } + //Fill map + for (int i = 0; i < words.length; i++) { + if (!map.containsKey(words[i])) { + map.put(words[i], 0); + } + map.put(words[i], map.get(words[i]) + 1); + } + //Fill ArrayList + for (Map.Entry entry : map.entrySet()) { + int freqIndex = entry.getValue(); + lists.get(freqIndex).add(entry.getKey()); + } + + int count = 0; + for (int i = lists.size() - 1; i >= 0; i--) { + ArrayList list = lists.get(i); + Collections.sort(list); + for (int j = 0; j < list.size(); j++) { + if (count < k) { + rst[count] = list.get(j); + count++; + } + if (count >= k) { + return rst; + } + } + } + + return rst; + } +} +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Topological Sorting.java b/Others/old records/LintCode-Backup/Topological Sorting.java new file mode 100644 index 0000000..3b4aa87 --- /dev/null +++ b/Others/old records/LintCode-Backup/Topological Sorting.java @@ -0,0 +1,90 @@ +/* +Given an directed graph, a topological order of the graph nodes is defined as follow: + +For each directed edge A -> B in graph, A must before B in the order list. +The first node in the order can be any node in the graph with no nodes direct to it. +Find any topological order for the given graph. + +Example +For graph as follow: + +picture + +The topological order can be: + +[0, 1, 2, 3, 4, 5] +[0, 2, 3, 1, 5, 4] +... +Note +You can assume that there is at least one topological order in the graph. + +Challenge +Can you do it in both BFS and DFS? + +Tags Expand +LintCode Copyright Geeks for Geeks Depth First Search Breadth First Search + +Thoughts: +First idea is Breadth First Search. +1. Find the node which has no parent node: this will be the beginning node. Use a HashMap to map all nodes with children, and whatever not in that map, is a root option. +2. Starting from this node, put all nodes in the queue (breadth-first) +3. process each node in the queue: add to array list + + +Note: All all possible root node (whatever not added into the map) because there could be multiple heads : (. Really need to ask about this if not sure. + +*/ + +/** + * Definition for Directed graph. + * class DirectedGraphNode { + * int label; + * ArrayList neighbors; + * DirectedGraphNode(int x) { label = x; neighbors = new ArrayList(); } + * }; + */ +public class Solution { + /** + * @param graph: A list of Directed graph node + * @return: Any topological order for the given graph. + */ + public ArrayList topSort(ArrayList graph) { + ArrayList rst = new ArrayList(); + if (graph == null || graph.size() == 0) { + return graph; + } + //Keep track of all neighbors in HashMap + HashMap map = new HashMap(); + for (DirectedGraphNode node : graph) { + for (DirectedGraphNode neighbor : node.neighbors) { + int keyN = neighbor.label; + if (map.containsKey(keyN)) { + map.put(keyN, map.get(keyN) + 1); + } else { + map.put(keyN, 1); + } + } + } + //BFS: Add root node. Note: + Queue queue = new LinkedList(); + for (DirectedGraphNode node : graph) { + if (!map.containsKey(node.label)) { + queue.offer(node); + rst.add(node); + } + } + //BFS: go through all children + while (!queue.isEmpty()) { + DirectedGraphNode node = queue.poll(); + for (DirectedGraphNode n : node.neighbors) { + int label = n.label; + map.put(label, map.get(label) - 1); + if (map.get(label) == 0) { + rst.add(n); + queue.offer(n); + } + } + } + return rst; + } +} diff --git a/Others/old records/LintCode-Backup/Total Occurrence of Target.java b/Others/old records/LintCode-Backup/Total Occurrence of Target.java new file mode 100644 index 0000000..c48c726 --- /dev/null +++ b/Others/old records/LintCode-Backup/Total Occurrence of Target.java @@ -0,0 +1,101 @@ +想法很简单。写起来有点长。 +找total number of occurance. 首先找first occurance, 再找last occurance. +``` +/* +Total Occurrence of Target + +Given a target number and an integer array sorted in ascending order. Find the total number of occurrences of target in the array. + +Example +Given [1, 3, 3, 4, 5] and target = 3, return 2. + +Given [2, 2, 3, 4, 6] and target = 4, return 1. + +Given [1, 2, 3, 4, 5] and target = 6, return 0. + +Challenge +Time complexity in O(logn) + +Tags Expand +Binary Search +*/ + +/* + Thought: + Similar to find last occurance of the target. Now: find the occurance, jump out. Find front, end occurance index. +*/ +public class Solution { + /** + * @param A an integer array sorted in ascending order + * @param target an integer + * @return an integer + */ + public int totalOccurrence(int[] A, int target) { + if (A == null || A.length == 0) { + return 0; + } + int start = 0; + int end = A.length - 1; + int mid = start + (end - start)/2; + //Find first occurance + int first = 0; + int last = 0; + while (start + 1 < end){ + mid = start + (end - start)/2; + if (A[mid] == target) { + if (mid - 1 >= 0 && A[mid - 1] == target) { + end = mid; + } else { + break; + } + } else if (A[mid] < target) { + start = mid; + } else { + end = mid; + } + } + if (A[start] == target) { + first = start; + } else if (A[mid] == target){ + first = mid; + } else if (A[end] == target){ + first = end; + } else { + return 0; + } + //If no 2nd occurance, just return + if (mid + 1 < A.length && A[mid + 1] != target) { + return 1; + } + + //Find last occurance + start = first; + last = start + 1; + end = A.length - 1; + while (start + 1 < end){ + mid = start + (end - start)/2; + if (A[mid] == target) { + if (mid + 1 < A.length && A[mid + 1] == target) { + start = mid; + } else { + break; + } + } else if (A[mid] < target) { + start = mid; + } else { + end = mid; + } + } + + if (A[end] == target) { + last = end; + } else if (A[mid] == target){ + last = mid; + } else if (A[start] == target) { + last = start; + } + return last - first + 1; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Trailing Zeros.java b/Others/old records/LintCode-Backup/Trailing Zeros.java new file mode 100644 index 0000000..c28f157 --- /dev/null +++ b/Others/old records/LintCode-Backup/Trailing Zeros.java @@ -0,0 +1,94 @@ +/* +Write an algorithm which computes the number of trailing zeros in n factorial. + +Example +11! = 39916800, so the out should be 2 + +Challenge +O(log N) time + +Tags Expand +Mathematics + +Thoughts: +Attempt1: +Can this problem be converted to : how many 10's to I have? +Loop through n, and check how many 2s, 5s do we have. +For each i, do while loop and count the number of 2s, and 5s in that particular i. + +Note: +5 and 2 makes 10. So don't worry about 10. +Some values will be checked redundantly, so record the ones checked, return the hash value directly. + +Attempt2: +Don't even need to worry about 2's because 2 is definitely more than 5's. Only need to care about 5's. + +How many 5's? n/5. loop (1 ~ n) +However, some number within (1 ~ n) may give more 5's, which for example is: 25 = 5 * 5, double 5's. And 125 = triple 5's. + +In fact count = n / 5 + n / 25 + n / 125 + .... +*/ +class Solution { + /* + * param n: As desciption + * return: An integer, denote the number of trailing zeros in n! + */ + public long trailingZeros(long n) { + if ( n < 5) { + return 0; + } + long count = 0; + for (long i = 5; n / i != 0; i *= 5) { + count += n / i; + } + return count; + } + +} + + + +/* +//Attempt 1: +//This solution exceed time limit, and it's over-complex. 滚粗。 +class Solution { + + private HashMap mapTwo = new HashMap(); + private HashMap mapFive = new HashMap(); + public long trailingZeros(long n) { + if (n < 5) { + return 0; + } + long countFive = 0; + long countTwo = 0; + for (int i = 1; i <= n; i++) { + if (i % 2 == 0) { + countTwo += countExistance(i, 2, mapTwo); + } + if (i % 5 == 0) { + countFive += countExistance(i, 5,mapFive); + } + } + return (countFive < countTwo) ? countFive : countTwo; + } + public long countExistance(long n, long m, HashMap map) { + long temp = n; + long count = 0; + double num = (double)n; + while (num / m == n / m) { + count++; + n = n / m; + num = (double)n; + if (map.containsKey(n)) { + count += map.get(n); + break; + } + } + if (!map.containsKey(temp)) { + map.put(temp, count); + } + return count; + } +}; + +*/ \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Trapping Rain Water II.java b/Others/old records/LintCode-Backup/Trapping Rain Water II.java new file mode 100644 index 0000000..bf459ec --- /dev/null +++ b/Others/old records/LintCode-Backup/Trapping Rain Water II.java @@ -0,0 +1,119 @@ +/* +Trapping Rain Water II +Given n x m non-negative integers representing an elevation map 2d where the area of each cell is 1 x 1, compute how much water it is able to trap after raining. + + +Example +Given 5*4 matrix + +[12,13,0,12] +[13,4,13,12] +[13,8,10,12] +[12,13,12,12] +[13,13,13,13] +return 14. + +Tags Expand + +LintCode Copyright Heap Matrix + +*/ + +/* +Thoughts: same idea as the trap Rain Water I. +Since this is not 1-way run through a 1D array (2D array can go 4 directions...), need to mark visted spot. + +Use PriorityQueue, sort lowest on top, because the lowest surroundings determines the best we can get. + +Bukkit theory: the lowest bar determines the height of the bukkit water. So, we always process the lowest first. +Therefore, we use a min-heap, a natural order priorityqueue based on height. + +Note: when adding a new block into the queue, comparing with the checked origin, we still want to add the higher height into queue. +(The high bar will always exist and hold the bukkit.) + +Step: +1. Create Cell (x,y,h) +2. Priorityqueue on Cell of all 4 borders +3. Process each element in queue, and add surrounding blocks into queue. +4. Mark checked block + + +*/ + +public class Solution { + class Cell { + int x; + int y; + int h; + public Cell(int x, int y, int height) { + this.x = x; + this.y = y; + this.h = height; + } + } + /** + * @param heights: a matrix of integers + * @return: an integer + */ + public int trapRainWater(int[][] heights) { + if (heights == null || heights.length == 0 || heights[0].length == 0) { + return 0; + } + + PriorityQueue queue = new PriorityQueue(1, new Comparator(){ + public int compare(Cell A, Cell B) { + return A.h - B.h; + } + }); + int n = heights.length; + int m = heights[0].length; + boolean[][] visited = new boolean[n][m]; + + //LEFT-RIGHT + for (int i = 0; i < n; i++) { + visited[i][0] = true; + visited[i][m - 1] = true; + queue.offer(new Cell(i, 0, heights[i][0])); + queue.offer(new Cell(i, m - 1, heights[i][m - 1])); + } + //TOP-BOTTOM + for (int i = 0; i < m; i++) { + visited[0][i] = true; + visited[n - 1][i] = true; + queue.offer(new Cell(0, i, heights[0][i])); + queue.offer(new Cell(n - 1, i, heights[n - 1][i])); + } + + int[] xs = {0, 0, 1, -1}; + int[] ys = {1, -1, 0, 0}; + int sum = 0; + while (!queue.isEmpty()) { + Cell cell = queue.poll(); + for (int i = 0; i < 4; i++) { + int nx = cell.x + xs[i]; + int ny = cell.y + ys[i]; + if (nx >= 0 && nx < n && ny >= 0 && ny < m && !visited[nx][ny]) { + visited[nx][ny] = true; + sum += Math.max(0, cell.h - heights[nx][ny]); + queue.offer(new Cell(nx, ny, Math.max(heights[nx][ny], cell.h))); + } + } + }//end while + return sum; + } +}; + + + + + + + + + + + + + + + diff --git a/Others/old records/LintCode-Backup/Trapping Rain Water.java b/Others/old records/LintCode-Backup/Trapping Rain Water.java new file mode 100644 index 0000000..cf83d97 --- /dev/null +++ b/Others/old records/LintCode-Backup/Trapping Rain Water.java @@ -0,0 +1,104 @@ +2 Pointers, +双面夹击:找中间最大bar,两面往中心扫 + +``` +/* +Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining. + +Example +Given [0,1,0,2,1,0,1,3,2,1,2,1], return 6. + +Challenge +O(n) time and O(1) memory + +O(n) time and O(n) memory is also acceptable. + +Tags Expand +Two Pointers Forward-Backward Traversal Array + +Related Problems Expand +Medium Container With Most Water + +LeetCode: +Tags: Array, Stack, Two Pointers +Similar Problems: (M) Container With Most Water, (M) Product of Array Except Self + + +*/ + +/* +Thoughts: +1. Find max's index, and use this index as the central pivot. (WHY? because highest bar can hold any volumn of water) +2. Process left and right separately. + a. assume each height jump fills that increased height of volumn. that is, if increased by r rows, add r rows of water into sum + b. all the black blocks (stones) should be minus from the sum on each index. +O(n) on finding the max. +O(n/2) for both left and right. +Total 2*O(n) = O(n) + +*/ + +//O(n) + +public class Solution { + /** + * @param heights: an array of integers + * @return: a integer + */ + public int trapRainWater(int[] heights) { + if (heights == null || heights.length == 0) { + return 0; + } + + int max = 0; + int maxIndex = 0; + int sum = 0; + int prev = 0; + for (int i = 0; i < heights.length; i++) { + if (heights[i] > max) { + max = heights[i]; + maxIndex = i; + } + } + + //Process left + for (int i = 0; i < maxIndex; i++) { + if (heights[i] > prev) { + sum += (maxIndex - i) * (heights[i] - prev); + prev = heights[i]; + } + sum -= heights[i]; + } + + //Process right: + prev = 0; + for (int i = heights.length - 1; i > maxIndex; i--) { + if (heights[i] > prev) { + sum += (i - maxIndex) * (heights[i] - prev); + prev = heights[i]; + } + sum -= heights[i]; + } + + return sum; + } +} + + + + + + + + + + + + + + + + + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Triangle Count.java b/Others/old records/LintCode-Backup/Triangle Count.java new file mode 100644 index 0000000..4a9f57a --- /dev/null +++ b/Others/old records/LintCode-Backup/Triangle Count.java @@ -0,0 +1,62 @@ +/* +Given an array of integers, how many three numbers can be found in the array, so that we can build an triangle whose three edges length is the three numbers that we find? + +Example +Given array S = [3,4,6,7], return 3. They are: + +[3,4,6] +[3,6,7] +[4,6,7] +Given array S = [4,4,4,4], return 4. They are: + +[4(1),4(2),4(3)] +[4(1),4(2),4(4)] +[4(1),4(3),4(4)] +[4(2),4(3),4(4)] +Tags Expand +Two Pointers LintCode Copyright +*/ + +/* +Thoughts: +Pick 3 integers that fits the condition: +A + B > C +B + C > A +A + C > B +If we sort the input, then we know A <= B <= C, so we can remove 2 conditoins above and only have: +A + B > C +That is, Pick one C, and pick two integers A,B in front. Similar to TWO SUM II. +Have a fixed C as target, and find A + B > target in the remaining array on left of C. +How about just use 2 pointers left, right, and compare with a C (s[i] in for loop) +Time: O(n^2) + +Note: don't forget to sort +*/ + +public class Solution { + /** + * @param S: A list of integers + * @return: An integer + */ + public int triangleCount(int S[]) { + if (S == null || S.length == 0) { + return 0; + } + Arrays.sort(S); + int count = 0; + for (int i = 0; i < S.length; i++) { + int left = 0; + int right = i - 1; //at least 1 step left from C + while (left < right){ + if (S[left] + S[right] > S[i]) { + count += (right - left); + right--; + } else {//(S[left] + S[right] <= S[i]) + left++; + } + } + } + return count; + } +} + diff --git a/Others/old records/LintCode-Backup/Tweaked Identical Binary Tree.java b/Others/old records/LintCode-Backup/Tweaked Identical Binary Tree.java new file mode 100644 index 0000000..cf00367 --- /dev/null +++ b/Others/old records/LintCode-Backup/Tweaked Identical Binary Tree.java @@ -0,0 +1,68 @@ +/* +Check two given binary trees are identical or not. Assuming any number of tweaks are allowed. A tweak is defined as a swap of the children of one node in the tree. + +Have you met this question in a real interview? Yes +Example + 1 1 + / \ / \ + 2 3 and 3 2 + / \ +4 4 +are identical. + + 1 1 + / \ / \ + 2 3 and 3 4 + / \ +4 2 +are not identical. + +Note +There is no two nodes with the same value in the tree. + +Challenge +O(n) time + +Tags Expand +Binary Tree +*/ + +/* + check isTweakedIdentical(a.left, b.right); + + corner case: if both null, true; + if one null, false +*/ +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + * @param a, b, the root of binary trees. + * @return true if they are tweaked identical, or false. + */ + public boolean isTweakedIdentical(TreeNode a, TreeNode b) { + if (a == null || b == null) { + return a == null && b == null; + } + if (a.val != b.val) { + return false; + } + return (isTweakedIdentical(a.left, b.left) && isTweakedIdentical(a.right, b.right)) + || (isTweakedIdentical(a.left, b.right) && isTweakedIdentical(a.right, b.left)); + } +} + + + + + + diff --git a/Others/old records/LintCode-Backup/Two Lists Sum.java b/Others/old records/LintCode-Backup/Two Lists Sum.java new file mode 100644 index 0000000..62f071a --- /dev/null +++ b/Others/old records/LintCode-Backup/Two Lists Sum.java @@ -0,0 +1,58 @@ +/* +You have two numbers represented by a linked list, where each node contains a single digit.The digits are stored in reverse order, such that the 1’s digit is at the head of the list.Write a function that adds the two numbers and returns the sum as a linked list. + +Example +Given two lists, 3->1->5->null and 5->9->2->null, return 8->0->8->null + +Tags Expand +Linked List Backtracking +//TODO: check 9chapter solution +Thinking process: +Simply add 2 lists’ values together. +Handle the carrier +Use dummy node at beginning. +*/ + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +public class Solution { + /** + * @param l1: the first list + * @param l2: the second list + * @return: the sum list of l1 and l2 + */ + public ListNode addLists(ListNode l1, ListNode l2) { + ListNode rst = new ListNode(0); + ListNode dummy = rst; + int carrier = 0; + //while + while (l1 != null || l2 != null) { + if (l1 != null) { + carrier += l1.val; + l1 = l1.next; + } + if (l2 != null) { + carrier += l2.val; + l2 = l2.next; + } + rst.next = new ListNode(carrier % 10); + carrier = carrier / 10; + rst = rst.next; + } + //check the carrier + if (carrier == 1) { + rst.next = new ListNode(1); + } + return dummy.next; + } +} + diff --git a/Others/old records/LintCode-Backup/Two Strings Are Anagrams.java b/Others/old records/LintCode-Backup/Two Strings Are Anagrams.java new file mode 100644 index 0000000..88b09b4 --- /dev/null +++ b/Others/old records/LintCode-Backup/Two Strings Are Anagrams.java @@ -0,0 +1,103 @@ +E + +方法1:char ascii 用count[256] +坑:不要想象这个是个26letter lowercase. may not be true. + +方法2: 若是其他字符encoding, 而不只是utf16-encoding (java char)? +那么就继续用string去做 + +``` +/* +Write a method anagram(s,t) to decide if two strings are anagrams or not. + +Example +Given s="abcd", t="dcab", return true. + +Challenge +O(n) time, O(1) extra space + +Tags Expand +String Cracking The Coding Interview + +*/ +/* + Recap 12.09.2015 + O(n) time: cannot Arrays.sort() + O(1) extra space: cannot convert to char array. But we can use a count[256] to count the occuranceo of letters. + + do 1 for loop: + + occurance index + - occurance index + + border cases: + null: false. + both length() == 0, true + length() not equal: false + + don't assume all lower case. + there are ' ' space, so it can't be just 26 letters. make it 256 +*/ +public class Solution { + public boolean anagram(String s, String t) { + if (s == null || t == null) { + return false; + } else if (s.length() != t.length()) { + return false; + } else if (s.length() == 0 && t.length() == 0) { + return true; + } + int[] count = new int[256]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i)]++; + count[t.charAt(i)]--; + } + for (int num : count) { + if (num != 0) { + return false; + } + } + return true; + } +}; + + +/* +What if it's not just ascii code, maybe uni-code? +Then the character (utf16-encoding) may not be enough. So we use String here. +*/ + +//check length. compare +public class Solution { + public boolean isAnagram(String s, String t) { + if (s == null || t == null || s.length() != t.length()) { + return false; + } + if (s.equals(t)) { + return true; + } + HashMap map = new HashMap(); + for (int i = 0; i < s.length(); i++) { + String ss = s.substring(i, i + 1); + String tt = t.substring(i, i + 1); + if (!map.containsKey(ss)) { + map.put(ss, 0); + } + map.put(ss, map.get(ss) + 1); + if (!map.containsKey(tt)) { + map.put(tt, 0); + } + map.put(tt, map.get(tt) - 1); + } + + for (Map.Entry entry : map.entrySet()) { + if (entry.getValue() != 0) { + return false; + } + } + + return true; + } +} + + +``` \ No newline at end of file diff --git a/Java/Two Sum II.java b/Others/old records/LintCode-Backup/Two Sum II.java similarity index 100% rename from Java/Two Sum II.java rename to Others/old records/LintCode-Backup/Two Sum II.java diff --git a/Others/old records/LintCode-Backup/Ugly Number.java b/Others/old records/LintCode-Backup/Ugly Number.java new file mode 100644 index 0000000..026c1d7 --- /dev/null +++ b/Others/old records/LintCode-Backup/Ugly Number.java @@ -0,0 +1,63 @@ +/* +Ugly number is a number that only have factors 3, 5 and 7. + +Design an algorithm to find the Kth ugly number. The first 5 ugly numbers are 3, 5, 7, 9, 15 ... + +Example +If K=4, return 9. + +Challenge +O(K log K) or O(K) time. + +Tags Expand +LintCode Copyright Priority Queue + +Thoughts: +Every level it's like: +3 5 7 +3 3,5 3,5,7 + +Use a priority queue to keep track. +Use a for loop to keep calculating the target number, and return it at the end + +Note: +Why not offer 3,5, 7 in first if statement? (Which is my original thought). Maybe, we want to limit the number of offers in 3's case, in case some 3's cases becomes bigger than 5's case. That, will accidentally prevent the program to check on 5's. +Therefore, leave 3,5,7 cases till 7's . + +*/ + +class Solution { + /** + * @param k: The number k. + * @return: The kth prime number as description. + */ + public long kthPrimeNumber(int k) { + if (k == 0) { + return 0; + } + PriorityQueue queue = new PriorityQueue(); + queue.offer((long)3); + queue.offer((long)5); + queue.offer((long)7); + long num = 0; + for (int i = 0; i < k; i++) { + num = queue.poll(); + if (num % 3 == 0) { + queue.offer(num * 3); + } else if (num % 5 == 0) { + queue.offer(num * 3); + queue.offer(num * 5); + } else if (num % 7 == 0) { + queue.offer(num * 3); + queue.offer(num * 5); + queue.offer(num * 7); + } + } + return num; + } +}; + + +/* +Can use DP as well:http://blog.welkinlan.com/2015/07/28/ugly-number-lintcode-java/ +*/ \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Unique Binary Search Tree II.java b/Others/old records/LintCode-Backup/Unique Binary Search Tree II.java new file mode 100644 index 0000000..b7ec9a6 --- /dev/null +++ b/Others/old records/LintCode-Backup/Unique Binary Search Tree II.java @@ -0,0 +1,61 @@ +/* +Given n, generate all structurally unique BST's (binary search trees) that store values 1...n. + +Example +Given n = 3, your program should return all 5 unique BST's shown below. + + 1 3 3 2 1 + \ / / / \ \ + 3 2 1 1 3 2 + / / \ \ + 2 1 2 3 +Tags Expand +Dynamic Programming Depth First Search + +Thinking process: +- For a BST, root can be any node from node(1) to node(n). +- For each root, left nodes has mutiple forms of BST, and right node has mutiple forms of BST. +- For each root node, divide and conquer left / right +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ +public class Solution { + /** + * @paramn n: An integer + * @return: A list of root + */ + public List generateTrees(int n) { + return generate(1, n); + } + public ArrayList generate(int start, int end) { + ArrayList rst = new ArrayList(); + if (start > end) { + rst.add(null); + return rst; + } + for (int i = start; i <= end; i++){ + ArrayList left = generate(start, i - 1); + ArrayList right = generate(i + 1, end); + for (TreeNode l : left) { + for (TreeNode r : right) { + TreeNode root = new TreeNode(i); + root.left = l; + root.right = r; + rst.add(root); + } + } + } + return rst; + } +} + diff --git a/Others/old records/LintCode-Backup/Unique Binary Search Tree.java b/Others/old records/LintCode-Backup/Unique Binary Search Tree.java new file mode 100644 index 0000000..5d373b4 --- /dev/null +++ b/Others/old records/LintCode-Backup/Unique Binary Search Tree.java @@ -0,0 +1,42 @@ +/* +Given n, how many structurally unique BST's (binary search trees) that store values 1...n? + + + +Example +Given n = 3, there are a total of 5 unique BST's. + + 1 3 3 2 1 + \ / / / \ \ + 3 2 1 1 3 2 + / / \ \ + 2 1 2 3 +Tags Expand +Catalan Number Dynamic Programming + +Thinking proces: +Knowing what is Catalan number. +C(n+1) = SUM(C(i)*C(n-i)) +OR: C(n) = SUM(C(i)*C(n-i-1)). +*/ + +public class Solution { + /** + * @paramn n: An integer + * @return: An integer + */ + public int numTrees(int n) { + if (n <= 1) { + return 1; + } + int[] count = new int[n + 1]; + count[0] = 1; + count[1] = 1; + for (int i = 2; i < n + 1; i++) { + for (int j = 0; j < i; j++) { + count[i] += count[j] * count[i - j - 1]; + } + } + return count[n]; + } +} diff --git a/Others/old records/LintCode-Backup/Unique Characters.java b/Others/old records/LintCode-Backup/Unique Characters.java new file mode 100644 index 0000000..6e4c03c --- /dev/null +++ b/Others/old records/LintCode-Backup/Unique Characters.java @@ -0,0 +1,74 @@ +不用额外data structure, O(n^2), double for loop. +用hashSet, space O(n), time O(n) +``` +/* +Implement an algorithm to determine if a string has all unique characters. + +Example +Given "abc", return true. + +Given "aab", return false. + +Challenge +What if you can not use additional data structures? + +Tags Expand +String Cracking The Coding Interview Array +*/ + +/* + Thought: + do it without hash set. + Can do a double-for loop, check from i~j, if str[i] exist later in the string. + O(n^2) +*/ + +public class Solution { + /** + * @param str: a string + * @return: a boolean + */ + public boolean isUnique(String str) { + if (str == null || str.length() == 0) { + return true; + } + for (int i = 0; i < str.length(); i++) { + for (int j = i + 1; j < str.length(); j++) { + if (str.charAt(i) == str.charAt(j)) { + return false; + } + } + } + return true; + } +} + + + +/* + Thought: + 1st, write hasset, there you go. +*/ +public class Solution { + /** + * @param str: a string + * @return: a boolean + */ + public boolean isUnique(String str) { + if (str == null || str.length() == 0) { + return true; + } + HashSet set = new HashSet(); + for (int i = 0; i < str.length(); i++) { + if (!set.contains(str.charAt(i))) { + set.add(str.charAt(i)); + } else { + return false; + } + }//end for + + return true; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Unique Path.java b/Others/old records/LintCode-Backup/Unique Path.java new file mode 100644 index 0000000..90e5be2 --- /dev/null +++ b/Others/old records/LintCode-Backup/Unique Path.java @@ -0,0 +1,106 @@ +/* +A robot is located at the top-left corner of a m x n grid (marked 'Start' in the diagram below). + +The robot can only move either down or right at any point in time. The robot is trying to reach the bottom-right corner of the grid (marked 'Finish' in the diagram below). + +How many possible unique paths are there? + +Note +m and n will be at most 100. + +Example +1,1 1,2 1,3 1,4 1,5 1,6 1,7 +2,1 +3,1 3,7 + +Above is a 3 x 7 grid. How many possible unique paths are there? + +Tags Expand +Array Dynamic Programming + +Thinking process: +f[x][y]: want to find out all possible path +To get to f[m][m] from f[m-1][n-1] has 2 way: f[m-1][n] or f[m][n-1]. +After found 'f[m-1][n-1]', store it to a Hashmap with the #path. +Every node pair (x,y) should have 1 solution. +Recursively add up to (0,0), will find out the total path. + +1. Own solution: user HashMap to memorize +*/ + +public class Solution { + /** + * @param n, m: positive integer (1 <= n ,m <= 100) + * @return an integer + */ + private int m,n; + public int uniquePaths(int m, int n) { + if (m <= 1 || n <= 1) { + return 1; + } + this.m = m; + this.n = n; + HashMap, Integer> his = + new HashMap, Integer>(); + int right = helper(0, 1, his); + int down = helper(1, 0, his); + return right + down; + } + + public int helper(int x, int y, HashMap, Integer> his) { + ArrayList pair = new ArrayList(); + pair.add(x); + pair.add(y); + if (his.containsKey(pair)) { + return his.get(pair); + } + if (x >= this.m -1 || y >= this.n - 1) { + his.put(pair, 1); + return his.get(pair); + } + int right = helper(x, y + 1, his); + int down = helper(x + 1, y, his); + his.put(pair, right + down); + return his.get(pair); + } +} + +/* + +2. 9Chapter solution +Thinking process: +1. Assume (r,c) where r>=1, c>=1. Any node (r,c) has 2 ways to get to: (r-1, c) from top, or (r,c-1) from left-side. +2. (r-1, c) and (r,c-1) stores the possible paths to get to them +3. (r,c) = (r-1, c) + (r,c-1) +4. Initialize the top-row and left-column to be 1: Assuming landing on any initial node has path # of 1. +5. From top-bottom traverse +*/ + +public class Solution { + /** + * @param n, m: positive integer (1 <= n ,m <= 100) + * @return an integer + */ + //Traverse + public int uniquePaths(int m, int n) { + if (m <= 1 || n <= 1) { + return 1; + } + int[][] matrix = new int[m][n]; + //Initialize + for (int i = 0; i < m; i++) { + matrix[i][0] = 1; + } + for (int i = 0; i < n; i++) { + matrix[0][i] = 1; + } + //Traverse + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1]; + } + } + return matrix[m - 1][n - 1]; + } +} + diff --git a/Others/old records/LintCode-Backup/Unique Paths II.java b/Others/old records/LintCode-Backup/Unique Paths II.java new file mode 100644 index 0000000..51d7ef3 --- /dev/null +++ b/Others/old records/LintCode-Backup/Unique Paths II.java @@ -0,0 +1,70 @@ +/* +Follow up for "Unique Paths": + +Now consider if some obstacles are added to the grids. How many unique paths would there be? + +An obstacle and empty space is marked as 1 and 0 respectively in the grid. + +Note +m and n will be at most 100. + +Example +For example, +There is one obstacle in the middle of a 3x3 grid as illustrated below. + +[ + [0,0,0], + [0,1,0], + [0,0,0] +] +The total number of unique paths is 2. + +Tags Expand +Array Dynamic Programming + +Thinking process: +1. Still use an extra matrix to count possible paths. +2. When initializing, skip block if it's obstacle (break the for loop, basically skip this row/col) +3. When evaluating paths, skip block if it's obstacle (save current spot's path as 0, means no path through this point). +4. Note: At evaluating double-for loop, we cannot use break, because we still need to evaluate using upper/left block. Hence we set the obstacle = 0. +*/ + +public class Solution { + /** + * @param obstacleGrid: A list of lists of integers + * @return: An integer + */ + public int uniquePathsWithObstacles(int[][] obstacleGrid) { + if (obstacleGrid == null || obstacleGrid.length == 0 || obstacleGrid[0].length == 0) { + return 0; + } + int row = obstacleGrid.length; + int col = obstacleGrid[0].length; + int[][] matrix = new int[row][col]; + for (int i = 0; i < row; i++) { + if (obstacleGrid[i][0] == 1) { + break; + } else { + matrix[i][0] = 1; + } + } + for (int j = 0; j < col; j++) { + if (obstacleGrid[0][j] == 1) { + break; + } else { + matrix[0][j] = 1; + } + } + for (int i = 1; i < row; i++) { + for (int j = 1; j < col; j++) { + if (obstacleGrid[i][j] == 1) { + matrix[i][j] = 0; + } else { + matrix[i][j] = matrix[i - 1][j] + matrix[i][j - 1]; + } + } + } + return matrix[row - 1][col - 1]; + } +} + diff --git a/Others/old records/LintCode-Backup/Update Bits.java b/Others/old records/LintCode-Backup/Update Bits.java new file mode 100644 index 0000000..cdd2b5b --- /dev/null +++ b/Others/old records/LintCode-Backup/Update Bits.java @@ -0,0 +1,41 @@ +/* +Given two 32-bit numbers, N and M, and two bit positions, i and j. Write a method to set all bits between i and j in N equal to M (e g , M becomes a substring of N located at i and starting at j) + + + +Example +Given N = (10000000000)2, M = (10101)2, i = 2, j = 6 + +return N = (10001010100)2 + +Challenge +Minimum number of operations? + +Tags Expand +Cracking The Coding Interview Bit Manipulation Binary Representation + +Thinking process: +Create a mask: xxxx000000xxxx. +Trick part: when it encounters negative number or dealing with index at edge index = 31, it starts having issue. Interesting fix: use long for masks. +*/ + +class Solution { + /** + *@param n, m: Two integer + *@param i, j: Two bit positions + *return: An integer + */ + public int updateBits(int n, int m, int i, int j) { + //Create mask: xxx00000xxx + long rightMask = ~0 >> i; + rightMask = ~(rightMask << i);// 00000xxx + long leftMask = ~0 >> (j + 1); + leftMask = leftMask << (j + 1);//xxxxx00000000 + long mask = leftMask | rightMask;//xxx00000xxx + n = (int) (n & mask); + n = (int) (n | (m << i)); + return n; + } +} + + diff --git a/Others/old records/LintCode-Backup/Valid Palindrome.java b/Others/old records/LintCode-Backup/Valid Palindrome.java new file mode 100644 index 0000000..88a81a9 --- /dev/null +++ b/Others/old records/LintCode-Backup/Valid Palindrome.java @@ -0,0 +1,61 @@ +注意如何滤过: alphanumeric +``` +/* +Given a string, determine if it is a palindrome, considering only alphanumeric characters and ignoring cases. + +Example +"A man, a plan, a canal: Panama" is a palindrome. + +"race a car" is not a palindrome. + +Note +Have you consider that the string might be empty? This is a good question to ask during an interview. + +For the purpose of this problem, we define empty string as valid palindrome. + +Challenge +O(n) time without extra memory. + +Tags Expand +String Two Pointers +*/ +/* +Thoughts: +Pointer from front to end. Front char has to equal end char. +For loop check till s.length()/2. +If even, for example, s.length() == 4, i will be [0 ~ 1]. +If odd, for example, s.length() == 5, i will be [0 ~ 1]. And inddex 2 itself stands out, and won't hurt palindrome. +*/ + +public class Solution { + /** + * @param s A string + * @return Whether the string is a valid palindrome + */ + public boolean isPalindrome(String s) { + if (s == null || s.length() == 0) { + return true; + } + int start = 0; + int end = s.length() - 1; + s = s.toLowerCase(); + while (start < end) { + while (start < s.length() && + (s.charAt(start) < '0' || (s.charAt(start) > '9' && s.charAt(start) < 'a') || s.charAt(start) > 'z') ) { + start++; + } + while (end >= 0 && + (s.charAt(end) < '0' || (s.charAt(end) > '9' && s.charAt(end) < 'a') || s.charAt(end) > 'z')) { + end--; + } + if (start < end && s.charAt(start) != s.charAt(end)) { + return false; + } + start++; + end--; + } + return true; + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Valid Parentheses.java b/Others/old records/LintCode-Backup/Valid Parentheses.java new file mode 100644 index 0000000..8dd1ded --- /dev/null +++ b/Others/old records/LintCode-Backup/Valid Parentheses.java @@ -0,0 +1,104 @@ +E + +剥皮过程。解铃还须系铃人 +左边的外皮'{['在stack底部 +右边的外皮应该和stack顶上的左外皮一一对应 + + + +``` +/* +Given a string containing just the characters '(', ')', '{', '}', '[' and ']', determine if the input string is valid. + +Example +The brackets must close in the correct order, "()" and "()[]{}" are all valid but "(]" and "([)]" are not. + +Tags Expand +Stack +*/ + +//02.04.2015 Recap +//lock will be unlocked by the same key +//put in stack. when '),],}' appears, check stack.top() to make sure they are good match +public class Solution { + public boolean isValid(String s) { + if (s == null || s.length() == 0) { + return true; + } + if (s.length() % 2 != 0) { + return false; + } + Stack stack = new Stack(); + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '(' || c == '[' || c == '{') { + stack.push(c); + } else if (c ==')' || c == ']' || c == '}') { + if (stack.isEmpty()) { + return false; + } + char front = stack.pop(); + if (c == ')' && front != '(') { + return false; + } + if (c == ']' && front != '[') { + return false; + } + if (c == '}' && front != '{') { + return false; + } + } else { + return false; + } + } + + return stack.isEmpty(); + } +} + + +/* + Thoughts: + Did this on Leetcode. Think about how do we naturally check it? + Use stack to hold '({[', and check against peek() every time on next element. + Check it nagativly. If all pass, return true; + + Note: + If stack is not cleanup, then it's false. +*/ +public class Solution { + /** + * @param s A string + * @return whether the string is a valid parentheses + */ + public boolean isValidParentheses(String s) { + if (s == null || s.length() < 2) { + return false; + } + Stack stack = new Stack(); + char[] arr = s.toCharArray(); + for (int i = 0; i < arr.length; i++) { + if (arr[i] == '(' || arr[i] == '[' || arr[i] == '{') { + stack.push(arr[i]); + } else if (arr[i] == ')' || arr[i] == ']' || arr[i] == '}') { + if (stack.isEmpty()) { + return false; + } else { + char c = stack.pop(); + if ((c == '(' && arr[i] == ')') || + (c == '[' && arr[i] == ']') || + (c == '{' && arr[i] == '}')) { + continue; + } + return false; + } + } else { + return false; + } + } + return stack.isEmpty(); + } +} + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Valid Sudoku.java b/Others/old records/LintCode-Backup/Valid Sudoku.java new file mode 100644 index 0000000..4988597 --- /dev/null +++ b/Others/old records/LintCode-Backup/Valid Sudoku.java @@ -0,0 +1,90 @@ +好像没啥技术含量. 做就行了。 +validate block的时候虽然看到了4层for.其实也就是n^2. +``` +/* +Determine whether a Sudoku is valid. + +The Sudoku board could be partially filled, where empty cells are filled with the character .. + + +Example +The following partially filed sudoku is valid. + +Valid Sudoku (Image can display here: http://www.lintcode.com/en/problem/valid-sudoku/#) + +Note +A valid Sudoku board (partially filled) is not necessarily solvable. Only the filled cells need to be validated. + +Clarification +What is Sudoku? + +http://sudoku.com.au/TheRules.aspx +https://zh.wikipedia.org/wiki/%E6%95%B8%E7%8D%A8 +https://en.wikipedia.org/wiki/Sudoku +http://baike.baidu.com/subview/961/10842669.htm +Tags Expand +Matrix +*/ + +/* + Thoughts: + Each row/col/block can only 1 ~ 9, no duplicates + Use HashSet, reinitiate 3 times. + traverse row, col, block + O(n^2) +*/ + +class Solution { + /** + * @param board: the board + @return: wether the Sudoku is valid + */ + public boolean isValidSudoku(char[][] board) { + if (board == null || board.length == 0 || board[0].length == 0 || board.length != board[0].length) { + return false; + } + HashSet row,col,block; + int n = board.length; + + for (int i = 0; i < n; i++) { + row = new HashSet(); + col = new HashSet(); + for (int j = 0; j < n; j++) { + //Check row + if (!row.contains(board[i][j])) { + row.add(board[i][j]); + } else if (board[i][j] != '.'){ + return false; + } + //Check col + if (!col.contains(board[j][i])) { + col.add(board[j][i]); + } else if (board[j][i] != '.'){ + return false; + } + } + } + + for (int i = 0; i < n; i+=3) { + for (int j = 0; j < n; j+=3) { + block = new HashSet(); + //Check block + for (int k = 0; k < 3; k++) { + for (int h = 0; h < 3; h++) { + if (!block.contains(board[i+k][j+h])) { + block.add(board[i+k][j+h]); + } else if (board[i+k][j+h] != '.'){ + return false; + } + } + } + } + + } + + + return true; + } +}; + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Validate Binary Search Tree.java b/Others/old records/LintCode-Backup/Validate Binary Search Tree.java new file mode 100644 index 0000000..1942660 --- /dev/null +++ b/Others/old records/LintCode-Backup/Validate Binary Search Tree.java @@ -0,0 +1,74 @@ +M + +查看每个parent-child关系。同时把root level上面传下来max,min界限定住。 + +``` +/* +29% Accepted +Given a binary tree, determine if it is a valid binary search tree (BST). + +Assume a BST is defined as follows: + +The left subtree of a node contains only nodes with keys less than the node's key. +The right subtree of a node contains only nodes with keys greater than the node's key. +Both the left and right subtrees must also be binary search trees. +Example +An example: + + 1 + / \ + 2 3 + / + 4 + \ + 5 +The above binary tree is serialized as "{1,2,3,#,#,4,#,#,5}". + +Tags Expand +Tree Binary Tree Binary Search Tree + +*/ + +/** + * Definition of TreeNode: + * public class TreeNode { + * public int val; + * public TreeNode left, right; + * public TreeNode(int val) { + * this.val = val; + * this.left = this.right = null; + * } + * } + */ + +//recursively check if tree are BST, && them all + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public boolean isValidBST(TreeNode root) { + return helper(root, Long.MIN_VALUE, Long.MAX_VALUE); + } + + public boolean helper(TreeNode root, long min, long max) { + if (root == null) { + return true; + } + if (root.val < max && root.val > min && + helper(root.left, min, root.val) && + helper(root.right, root.val, max)) { + return true; + } + return false; + } +} + + +``` \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Wood Cut.java b/Others/old records/LintCode-Backup/Wood Cut.java new file mode 100644 index 0000000..0d862d3 --- /dev/null +++ b/Others/old records/LintCode-Backup/Wood Cut.java @@ -0,0 +1,71 @@ +/* +Given n pieces of wood with length L[i] (integer array). Cut them into small pieces to guarantee you could have equal or more than k pieces with the same length. What is the longest length you can get from the n pieces of wood? Given L & k, return the maximum length of the small pieces. + +Note +You couldn't cut wood into float length. + +Example +For L=[232, 124, 456], k=7, return 114. + +Challenge +O(n log Len), where Len is the longest length of the wood. + +Tags Expand +Binary Search + +Thinking process: +Take the largest item. +Priorities: +1. Have to get calculatedK >= givenK +2. Meanwhile, want to maximize the smal piece. + +One thing not clear: do we have to use the given small piece? If we have to, we need to concern about the shortest wood piece. See commentted-out part +In this problem, however, we can abandon the small pieces, as long as the max_small_pieces can allow calculatedK >= givenK. + +Use binary search on the largest item: +1. if calculatedK < givenK: end = mid; +2. If calculated >= givenK, move start = mid as much as possible, which gives maximized small piece. + +*/ + + +public class Solution { + /** + *@param L: Given n pieces of wood with length L[i] + *@param k: An integer + *return: The maximum length of the small pieces. + */ + public int woodCut(int[] L, int k) { + if (L == null || L.length == 0 || k < 0) { + return 0; + } + if (L.length == 1) { + return L[0] / (L[0] / k); + } + Arrays.sort(L); + int start = 0; + int end = L[L.length - 1]; + int mid = 0; + int max = 0; + // int min = L[0]; + while (start + 1 < end) { + mid = start + (end - start) / 2; + //if (mid > min) { + // end = mid; + // } else { + int count = 0; + for (int i : L) { + count += i / mid; + } + if (count < k) { + end = mid; + } else { + start = mid; + max = mid; + } + //} + }//end while + return max; + } +} + diff --git a/Others/old records/LintCode-Backup/Word Break.java b/Others/old records/LintCode-Backup/Word Break.java new file mode 100644 index 0000000..1ff9ff8 --- /dev/null +++ b/Others/old records/LintCode-Backup/Word Break.java @@ -0,0 +1,94 @@ +/* +Given a string s and a dictionary of words dict, determine if s can be break into a space-separated sequence of one or more dictionary words. + +Example +Given s = "lintcode", dict = ["lint", "code"]. + +Return true because "lintcode" can be break as "lint code". + +Tags Expand +String Dynamic Programming + +*/ +/* +Attemp3: +Optimize attempt2: If the input s is super long, but Dict does not have that long string, then we should avoid that case, so to save time. That is, check dict's strings' max length, and incldue that in 2nd-level for loop + +j: last word's length, range from 0 to i. +[i - j]: the first index of current word +rst[i - j]: if s[i ~ j] returns true +s.substring(i - j, i): if s[i-j position to i position] is in dict + +Note: use maxLength to optimize the solution. + +*/ + +public class Solution { + public boolean wordBreak(String s, Set dict) { + if (s == null || dict.contains(s)) { + return true; + } + boolean[] rst = new boolean[s.length() + 1]; + rst[0] = true; + int maxLength = calMaxLength(dict); + for (int i = 0; i <= s.length(); i++) { + for (int j = 0; j <= i && j <= maxLength; j++) { + if (rst[i - j] && dict.contains(s.substring(i - j, i))) { + rst[i] = true; + break; + } + } + } + return rst[s.length()]; + } + + public int calMaxLength(Set dict) { + int length = 0; + for (String word : dict) { + length = Math.max(length, word.length()); + } + return length; + } +} + + + +/* +Attemp2, Thought: +Use boolena to denote rst[i]: s[0,i-1] can be break to match dict. For the ease to explain, let's consider rst[i+1] with actually string s[0,i]; +How to calculate rst[i+1]? + As long as there is at least 1 way to break s[0, i], that would work. so do a for loop to check on string s[0, i]: + For each i, use another index j, j = 0 ~ i. If rst[j] works and s[j,i+1] is in dict, that makes rst[i+1] = true. + +Correct: however excceeds time limit at 97% correct +*/ + +public class Solution { + public boolean wordBreak(String s, Set dict) { + if (s == null || dict.contains(s)) { + return true; + } + boolean[] rst = new boolean[s.length() + 1]; + rst[0] = true; + for (int i = 0; i < s.length(); i++) { + for (int j = 0; j <= i; j++) { + if (rst[j] && dict.contains(s.substring(j, i + 1))) { + rst[i + 1] = true; + break; + } + } + } + return rst[s.length()]; + } +} + + + + +/* +Thoughts1: +Is this: select one or more words from dict, to construct the given string. +Create DP[i][j] based on dict that says: combine i number of dict strings and j number of dict strings, do we have a combined string that contains the target? + +However, this seems confusing and over-complex. We only have 1 set of variables: dict, so maybe it's now wise to create 2D DP[][]. +*/ \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Word Ladder II.java b/Others/old records/LintCode-Backup/Word Ladder II.java new file mode 100644 index 0000000..8a38c03 --- /dev/null +++ b/Others/old records/LintCode-Backup/Word Ladder II.java @@ -0,0 +1,280 @@ +/* +Given two words (start and end), and a dictionary, find all shortest transformation sequence(s) from start to end, such that: + +Only one letter can be changed at a time +Each intermediate word must exist in the dictionary +Have you met this question in a real interview? Yes +Example +Given: +start = "hit" +end = "cog" +dict = ["hot","dot","dog","lot","log"] +Return + [ + ["hit","hot","dot","dog","cog"], + ["hit","hot","lot","log","cog"] + ] +Note +All words have the same length. +All words contain only lowercase alphabetic characters. +Tags Expand +Backtracking Depth First Search Breadth First Search + +Attempt1 is by me: however it exceeds time/memory limit. +Some other good sources can be found online: +//http://www.jiuzhang.com/solutions/word-ladder-ii/ +//http://www.cnblogs.com/shawnhue/archive/2013/06/05/leetcode_126.html +Adjacency List, Prefix ... etc. Let's look at them one after another. First get it through with a NineChapter solution +*/ + +//Attempt2: Use Nine Chapter solution, BFS + DFS. It works, very nicely, using backtracking. +/* +BFS: +1. For all mutations in dict, create pastMap: all possible mutations that can turn into each particular str in dict. +2. For all mutations in dict, create distance: distance to start point. +DFS: +3. Find minimum path by checking distance different of just 1. Use a List to do DFS + +Note: +Map uses containsKey. Set uses contains +In DFS, add new copy: new ArrayList(path) +BFS: queue, while loop +DFS: recursion, with a structure to go deeper, remember to add/remove element when passing alone +*/ +public class Solution { + + public List> findLadders(String start, String end, Set dict) { + List> rst = new ArrayList>(); + Map> pastMap = new HashMap>(); + Map distance = new HashMap(); + Queue queue = new LinkedList(); + + //Initiate the variables + dict.add(start); + dict.add(end); + queue.offer(start); + distance.put(start, 0); + for (String s : dict) { + pastMap.put(s, new ArrayList()); + } + + //BFS + BFS(start, end, distance, pastMap, dict, queue); + + //DFS + ArrayList path = new ArrayList(); + DFS(start, end, distance, pastMap, path, rst); + + return rst; + } + //BFS to populate map and distance: + //Distance: distance from each str in dict, to the starting point. + //Map: all possible ways to mutate into each str in dict. + public void BFS(String start, String end, Map distance, Map> pastMap, Set dict, Queue queue) { + while(!queue.isEmpty()) { + String str = queue.poll(); + List list = expand(str, dict); + + for (String s : list) { + pastMap.get(s).add(str); + if (!distance.containsKey(s)) { + distance.put(s, distance.get(str) + 1); + queue.offer(s); + } + } + } + } + //DFS on the map, where map is the all possible ways to mutate into a particular str. Backtracking from end to start + public void DFS(String start, String str, Map distance, Map> pastMap, ArrayList path, List> rst) { + path.add(str); + if (str.equals(start)) { + Collections.reverse(path); + rst.add(new ArrayList(path)); + Collections.reverse(path); + } else {//next step, trace 1 step towards start + for (String s : pastMap.get(str)) {//All previous-mutation options that we have with str: + if (distance.containsKey(s) && distance.get(str) == distance.get(s) + 1) {//Only pick those that's 1 step away: keep minimum steps for optimal solution + DFS(start, s, distance, pastMap, path, rst); + } + } + } + path.remove(path.size() - 1); + } + //Populate all possible mutations for particular str, skipping the case that mutates back to itself. + public ArrayList expand(String str, Set dict) { + ArrayList list = new ArrayList(); + for (int i = 0; i < str.length(); i++) {//Alternate each letter position + for (int j = 0; j < 26; j++) {//Alter 26 letters + if (str.charAt(i) != (char)('a' + j)) { + String newStr = str.substring(0, i) + (char)('a' + j) + str.substring(i + 1); + if (dict.contains(newStr)) { + list.add(newStr); + } + } + } + } + return list; + } +} + + + +//Attempt1: probably works, however: +//Testing against input: "qa", "sq", ["si","go","se","cm","so","ph","mt","db","mb","sb","kr","ln","tm","le","av","sm","ar","ci","ca","br","ti","ba","to","ra","fa","yo","ow","sn","ya","cr","po","fe","ho","ma","re","or","rn","au","ur","rh","sr","tc","lt","lo","as","fr","nb","yb","if","pb","ge","th","pm","rb","sh","co","ga","li","ha","hz","no","bi","di","hi","qa","pi","os","uh","wm","an","me","mo","na","la","st","er","sc","ne","mn","mi","am","ex","pt","io","be","fm","ta","tb","ni","mr","pa","he","lr","sq","ye"] +//0. Could be backtrackList exceed memory limit. +//1. If use HashSet set to check if particular sequence exist, then exceed memory +//2. If use StringBuffer strCheck to check if particular sequence exist, then exceed time limit. +//It looks like we'd use DFS for final results. +public class Solution { + private Queue q = new LinkedList(); + private Queue> backtrackList = new LinkedList>(); + private Set dict; + private String end; + private int level = 1; + private int len = Integer.MAX_VALUE; + private List> rst = new ArrayList>(); + + public List> findLadders(String start, String end, Set dict) { + if (start == null || end == null || dict == null || start.length() != end.length()) { + return rst; + } + this.dict = dict; + this.end = end; + ArrayList head = new ArrayList(); + head.add(start); + q.offer(start); + backtrackList.offer(head); + while(!q.isEmpty()) {//BFS + int size = q.size();//Fix size + level++; + for (int k = 0; k < size; k++) {//LOOP through existing queue: for this specific level + String str = q.poll(); + ArrayList list = backtrackList.poll(); + validateMutations(str, list); + }//END FOR K + }//END WHILE + + List> minRst = new ArrayList>(); + for (int i = 0; i < rst.size(); i++) { + if (rst.get(i).size() == len) { + minRst.add(rst.get(i)); + } + } + return minRst; + } + + + public void validateMutations(String str, ArrayList list) { + if (list.size() > len) {//No need to digger further if list is already greater than min length + return; + } + for (int i = 0; i < str.length(); i++) {//Alternate each letter position + for (int j = 0; j < 26; j++) {//Alter 26 letters + if (str.charAt(i) == (char)('a' + j)) { + continue; + } + String newStr = str.substring(0, i) + (char)('a' + j) + str.substring(i + 1); + + ArrayList temp = (ArrayList)list.clone(); + temp.add(newStr); + if (dict.contains(newStr)) { + if (newStr.equals(end)) {//Found end + len = Math.min(len, level); + rst.add(temp); + } else { + q.offer(newStr); + backtrackList.offer(temp); + } + } + }//END FOR J + }//END FOR I + } +} + + + +//Solution from NineChapter, commented: + +/* +public class Solution { + public List> findLadders(String start, String end,Set dict) { + List> ladders = new ArrayList>(); + Map> map = new HashMap>(); + Map distance = new HashMap(); + + dict.add(start); + dict.add(end); + + bfs(map, distance, start, end, dict); + //Now at this step, we have: + //a distance map of all mutated string from start, + //a map of mutation and its list of 'pre-mutation' string + //dict: includes start and end + List path = new ArrayList(); + + dfs(ladders, path, end, start, distance, map); + + return ladders; + } + //crt: is not necessarily the 'end', since this is a recursive method + //crt at first is the 'end' string, then it's switching to other strings inorder to finally matches 'start' + void dfs(List> ladders, List path, String crt, + String start, Map distance, + Map> map) { + path.add(crt); + if (crt.equals(start)) {//Now, finally if the crt makes it to start and equals to start, we found a match. + Collections.reverse(path);//We had a reversed path + ladders.add(new ArrayList(path));//add + Collections.reverse(path);//need to reverse it back, becase we need 'path' for more recursive calls. + } else { + for (String next : map.get(crt)) {//Find all possible tranformations/mutations that can turn itself into crt: we have a ArrayList of candidates (pre-mutated strings) + if (distance.containsKey(next) && distance.get(crt) == distance.get(next) + 1) { //if that mutation is just 1 step different, that's good, which means these mutation takes minimum of 1 step to happen. Note: we are comparing the distance to start point. + dfs(ladders, path, next, start, distance, map);//If that's the case, pass varibles to next level: use new path (with crt added), and use the 'next' string (which is 1 step closer to start) for next level of searching. + } + } + } + path.remove(path.size() - 1);//remove that ending crt, since 'path' is shared in recursive methods, need to keep it cleaned. + } +//map: each string in the dict (including start, end) represents a key, and the value is a ArrayList of string. + void bfs(Map> map, Map distance, String start, String end, Set dict) { + Queue q = new LinkedList(); + q.offer(start); + distance.put(start, 0);//Distance: key: str, value: distance value from start. + for (String s : dict) { + map.put(s, new ArrayList()); + } + + while (!q.isEmpty()) { + String crt = q.poll();//Get head of queue, the item currently we are looking at. Called X. + + List nextList = expand(crt, dict);//generate all possible mutations (must exist in dict) + for (String next : nextList) {//For all mutations + map.get(next).add(crt);//append X to end of all of the mutated string (this will become a reverse order). This creates a path of mutation + if (!distance.containsKey(next)) {//If that mutated string never occured: + distance.put(next, distance.get(crt) + 1);//add distance to this mutation. This is fixed and will never change, btw. This becomes a list of all mutations and distance from start. + q.offer(next);//Add this mutation to queue. + } + } + } + } +//all possible mutations based on 1 str polled from the queue. + List expand(String crt, Set dict) { + List expansion = new ArrayList(); + + for (int i = 0; i < crt.length(); i++) { + for (char ch = 'a'; ch <= 'z'; ch++) { + if (ch != crt.charAt(i)) { + String expanded = crt.substring(0, i) + ch + + crt.substring(i + 1); + if (dict.contains(expanded)) { + expansion.add(expanded); + } + } + } + } + return expansion; + } +} + + +*/ \ No newline at end of file diff --git a/Others/old records/LintCode-Backup/Word Ladder.java b/Others/old records/LintCode-Backup/Word Ladder.java new file mode 100644 index 0000000..d15adbf --- /dev/null +++ b/Others/old records/LintCode-Backup/Word Ladder.java @@ -0,0 +1,126 @@ +/* +Given two words (start and end), and a dictionary, find the length of shortest transformation sequence from start to end, such that: + +Only one letter can be changed at a time +Each intermediate word must exist in the dictionary + +Example +Given: +start = "hit" +end = "cog" +dict = ["hot","dot","dog","lot","log"] +As one shortest transformation is "hit" -> "hot" -> "dot" -> "dog" -> "cog", +return its length 5. + +Note +Return 0 if there is no such transformation sequence. +All words have the same length. +All words contain only lowercase alphabetic characters. +Tags Expand +Breadth First Search + +Thoughts: +Use the dict (check if mutation exist in dict) as base to create a directed graph, use BFS to find shortest path. + +Note: +Be careful with queue size when trying to do for loop on it. Use a pre-fixed size = q.size(), in case queue's size changes during for loop. +*/ + +//Solution1: nested for loop +public class Solution { + public int ladderLength(String start, String end, Set dict) { + if (start == null || end == null || dict == null || start.length() != end.length()) { + return 0; + } + Queue q = new LinkedList(); + HashSet set = new HashSet(); + int level = 1; + int len = Integer.MAX_VALUE; + q.offer(start); + set.add(start); + while(!q.isEmpty()) {//BFS + int size = q.size();//Fix size + level++; + for (int k = 0; k < size; k++) {//LOOP through existing queue: for this specific level + String str = q.poll(); + for (int i = 0; i < str.length(); i++) {//Alternate each letter position + for (int j = 0; j < 26; j++) {//Alter 26 letters + String newStr; + if (i == 0 && str.length() == 1) { + newStr = "" + (char)('a' + j); + } + else if (i == str.length() - 1) { + newStr = str.substring(0, i) + (char)('a' + j); + } else { + newStr = str.substring(0, i) + (char)('a' + j) + str.substring(i + 1); + } + if (!set.contains(newStr) && dict.contains(newStr)) { + if (newStr.equals(end)) {//Found end + len = Math.min(len, level); + } else { + set.add(newStr); + q.offer(newStr); + } + } + }//END FOR J + }//END FOR I + }//END FOR K + }//END WHILE + return len; + } +} + + + +//Solution2: separate methods, and hope to make Word Ladder II problem easier +public class Solution { + private Queue q = new LinkedList(); + private HashSet set = new HashSet(); + private Set dict; + private String end; + private int level = 1; + private int len = Integer.MAX_VALUE; + + public int ladderLength(String start, String end, Set dict) { + if (start == null || end == null || dict == null || start.length() != end.length()) { + return 0; + } + this.dict = dict; + this.end = end; + q.offer(start); + set.add(start); + while(!q.isEmpty()) {//BFS + int size = q.size();//Fix size + level++; + for (int k = 0; k < size; k++) {//LOOP through existing queue: for this specific level + String str = q.poll(); + validateMutations(str); + }//END FOR K + }//END WHILE + return len; + } + + public void validateMutations(String str) { + for (int i = 0; i < str.length(); i++) {//Alternate each letter position + for (int j = 0; j < 26; j++) {//Alter 26 letters + String newStr; + if (i == 0 && str.length() == 1) { + newStr = "" + (char)('a' + j); + } + else if (i == str.length() - 1) { + newStr = str.substring(0, i) + (char)('a' + j); + } else { + newStr = str.substring(0, i) + (char)('a' + j) + str.substring(i + 1); + } + if (!set.contains(newStr) && dict.contains(newStr)) { + if (newStr.equals(end)) {//Found end + len = Math.min(len, level); + } else { + set.add(newStr); + q.offer(newStr); + } + } + }//END FOR J + }//END FOR I + } +} diff --git a/Others/old records/LintCode-Backup/Word Search II.java b/Others/old records/LintCode-Backup/Word Search II.java new file mode 100644 index 0000000..30f77a3 --- /dev/null +++ b/Others/old records/LintCode-Backup/Word Search II.java @@ -0,0 +1,210 @@ +/* +Given a matrix of lower alphabets and a dictionary. Find all words in the dictionary that can be found in the matrix. A word can start from any position in the matrix and go left/right/up/down to the adjacent position. + +Example +Given matrix: +doaf +agai +dcan +and dictionary: +{"dog", "dad", "dgdg", "can", "again"} + +return {"dog", "dad", "can", "again"} + + +dog: +doaf +agai +dcan + +dad: +doaf +agai +dcan + +can: +doaf +agai +dcan + +again: +doaf +agai +dcan + +Challenge +Using trie to implement your algorithm. + +Tags Expand +LintCode Copyright Trie + + +*/ +//Well, the attempt2 uses Trie, but didn't really use find(). It just uses insert() to create Trie, and mainly +//used the end point where string is finished. + +/* +Attemp2: Trie solution. +http://www.jiuzhang.com/solutions/word-search-ii/ + +Here is how Tire works, from my understanding: it creates a new data strucutre that maps all words into a trie structure. Then, based on the given 2D matrix of letters, using each individual letter as starting point, and grab all possible combinations, then save the possibilities into final resuts. + +The magic 'checking point' is the use of 'isString' of trie. + +Note: should also be careful with marking board[x][y] = '#', which helps to prevent re-use used letters. + +About TrieTree: +Each string obtains a particular/unique path. +Different strings could share same prefix path, but at certain index when the two strings are differentiating, they will start the following path on different TrieNode, which leads to completely separate subtree path. +At end of the tree, a string will have isString== true and the real string value stored. + +That is, +insert: for all letter, make sure they are all created as nodes and linked together by using subtree. +find: for loop to iterate through subtrees of nodes, then return target on last index letter. + + +In the search: +node.subtree.get(current).isString: this determines if a string exists or not. +*/ +public class Solution { + class TrieNode { + boolean isString; + String s; + HashMap subtree; + public TrieNode() { + this.isString = false; + this.s = ""; + this.subtree = new HashMap(); + } + } + + class TrieTree { + TrieNode node; + public TrieTree(TrieNode n) { + node = n; + } + public void insert(String s) { + TrieNode curr = node; + for (int i = 0; i < s.length(); i++) { + if (!curr.subtree.containsKey(s.charAt(i))) { + curr.subtree.put(s.charAt(i), new TrieNode()); + } + curr = curr.subtree.get(s.charAt(i)); + } + curr.isString = true; + curr.s = s; + } + public boolean find(String s) { + TrieNode curr = node; + for (int i = 0; i < s.length(); i++) { + if (!curr.subtree.containsKey(s.charAt(i))) { + return false; + } + curr = curr.subtree.get(s.charAt(i)); + } + return curr.isString; + } + } + + public void search(char[][] board, ArrayList rst, int i, int j, TrieNode node) { + if (node.isString) { + if(!rst.contains(node.s)) { + rst.add(node.s); + } + } + if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] == '#' || node == null) { + return; + } + if (node.subtree.containsKey(board[i][j])) { + char temp = board[i][j]; + TrieNode next = node.subtree.get(board[i][j]); + board[i][j] = '#';//Mark it, prevent going back-forth + search(board, rst, i, j + 1, next); + search(board, rst, i, j - 1, next); + search(board, rst, i - 1, j, next); + search(board, rst, i + 1, j, next); + board[i][j] = temp; + } + + } + public ArrayList wordSearchII(char[][] board, ArrayList words) { + ArrayList rst = new ArrayList(); + if (board == null || words == null || words.size() == 0) { + return rst; + } + TrieTree tree = new TrieTree(new TrieNode()); + for (String word : words) { + tree.insert(word); + } + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[i].length; j++) { + search(board, rst, i, j, tree.node); + } + } + + return rst; + } +} + + + + + +/* +Attempt1: +Thoughts: +Use word search1, and do for loop on the words... and that works .........Well, that's not the Trie solution +*/ + +public class Solution { + public ArrayList wordSearchII(char[][] board, ArrayList words) { + ArrayList rst = new ArrayList(); + if (board == null || words == null || words.size() == 0) { + return rst; + } + for (String word : words) { + if (exist(board, word)) { + rst.add(word); + } + } + return rst; + } + //The following are from Word Search I + public boolean exist(char[][] board, String word) { + if (word == null || word.length() == 0) { + return true; + } + if (board == null) { + return false; + } + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == word.charAt(0)) { + boolean rst = search(board, word, i, j, 0); + if (rst) { + return true; + } + } + } + } + return false; + } + + public boolean search(char[][] board, String word, int i, int j, int start) { + if (start == word.length()) { + return true; + } + if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(start)) { + return false; + } + board[i][j] = '#'; + boolean rst = search(board, word, i, j - 1, start + 1) + || search(board, word, i, j + 1, start + 1) + || search(board, word, i + 1, j, start + 1) + || search(board, word, i - 1, j, start + 1); + board[i][j] = word.charAt(start); + return rst; + } +} diff --git a/Others/old records/LintCode-Backup/Word Search.java b/Others/old records/LintCode-Backup/Word Search.java new file mode 100644 index 0000000..013dbb9 --- /dev/null +++ b/Others/old records/LintCode-Backup/Word Search.java @@ -0,0 +1,70 @@ +/* +Given a 2D board and a word, find if the word exists in the grid. + +The word can be constructed from letters of sequentially adjacent cell, where "adjacent" cells are those horizontally or vertically neighboring. The same letter cell may not be used more than once. + +Have you met this question in a real interview? Yes +Example +Given board = + +[ + "ABCE", + "SFCS", + "ADEE" +] +word = "ABCCED", -> returns true, +word = "SEE", -> returns true, +word = "ABCB", -> returns false. + +Tags Expand +Backtracking + +Thoughts: +1. find starting index i,j +2. Start divde&&conqure: each iteration. + In each interation: make sure board[i][j] == word.charAt(currentCheckingIndex); If not match, return false and terminate the interation +3. Therefore, if (start) == word.length(), that means all previous-start indexes are matched, so we have a match; return true in this case. + + +Note: if can use boolean || boolean || boolean, use it and save processing power: once one boolean works, it won't process the rest || elements +*/ + + +public class Solution { + public boolean exist(char[][] board, String word) { + if (word == null || word.length() == 0) { + return true; + } + if (board == null) { + return false; + } + + for (int i = 0; i < board.length; i++) { + for (int j = 0; j < board[0].length; j++) { + if (board[i][j] == word.charAt(0)) { + boolean rst = search(board, word, i, j, 0); + if (rst) { + return true; + } + } + } + } + return false; + } + + public boolean search(char[][] board, String word, int i, int j, int start) { + if (start == word.length()) { + return true; + } + if (i < 0 || i >= board.length || j < 0 || j >= board[0].length || board[i][j] != word.charAt(start)) { + return false; + } + board[i][j] = '#'; + boolean rst = search(board, word, i, j - 1, start + 1) + || search(board, word, i, j + 1, start + 1) + || search(board, word, i + 1, j, start + 1) + || search(board, word, i - 1, j, start + 1); + board[i][j] = word.charAt(start); + return rst; + } +} diff --git "a/Others/\344\272\232\351\272\273/FindFirstDuplicate.java" "b/Others/\344\272\232\351\272\273/FindFirstDuplicate.java" new file mode 100644 index 0000000..aa51f87 --- /dev/null +++ "b/Others/\344\272\232\351\272\273/FindFirstDuplicate.java" @@ -0,0 +1,37 @@ +/* + 给你一个字符串,找出其中第一个重复的单词。比如,He had had a book. 返回had。 + 若真是这么简单, loop through str by ' '。每个单词都尝试在str里面找重复 + + regular expression: "\\s+" +*/ + +public class Solution { + + public static String findFirstDuplicatedWord(String str) { + if (str == null || str.length() == 0) { + return str; + } + + String[] arr = str.split("\\s+"); + ArrayList list = new ArrayList(); + for (int i = 0; i < arr.length; i++) { + if (list.contains(arr[i])) { + return arr[i]; + } + list.add(arr[i]); + } + + return ""; + } + + public static void main(String[] args) { + System.out.println("START"); + + String str = "a is a sample program ok a";// is a a is + + String rst = findFirstDuplicatedWord(str); + + System.out.println(rst); + } + +} \ No newline at end of file diff --git "a/Others/\344\272\232\351\272\273/Slot Machine.java" "b/Others/\344\272\232\351\272\273/Slot Machine.java" new file mode 100644 index 0000000..1a255a2 --- /dev/null +++ "b/Others/\344\272\232\351\272\273/Slot Machine.java" @@ -0,0 +1,102 @@ +M + +检查字符串是否可以组成回文. 用hashmap + +``` +import java.io.*; +import java.util.*; +import java.text.*; +import java.math.*; +import java.util.regex.*; + +/* +Summer has reached a casino in Las Vegas and she wanted to gamble in the slot machines, Casino manager has given her some chances to play (T) ,where T is the no of different slot machines; each one has it's own unique combination of letters and numbers blinking in the panel(N). + +The condition of winning is to have a pallindromic sequence within those N characters provided she can press the randomization switch as many times she wants untill all the combinations with those letters are complete. + +Help summer to identify in which of those slot machines she has a chance of winning. + +Constraints: + +1≤T≤10000 + +1≤N≤100 (characters) + +Input Format + +The first line contains an integer T (The no of slot machines). + +The second line contains the string of characters(N) blinking on the slot machine. + +The string inputed in the second line may cotain any numbers or letters of any length . + +Note : + +The string(N) may have repetitive letters or numbers in it. Capital letters and small letters are considered separate characters. + +Output Format + +if a slot machine has a possibility of winning then display yes, else display no + +Sample Input + +2 + +abac11c + +dkvvfvd + +Sample Output + +yes + +no + +Explanation + +Since the 1st string contains a permutation which is palindromic in nature 'ca1b1ac' therefore output is yes. + +In the 2nd case there is no such permutation hence the output is no. + +*/ +//check pallidromic by counting chars in HashMap. O(n). If more than 1 single char, that is no. +public class Solution { + + public static void main(String[] args) { + /* Enter your code here. Read input from STDIN. Print output to STDOUT. Your class should be named Solution. */ + Scanner in = new Scanner(System.in); + int line = Integer.parseInt(in.nextLine()); + while (in.hasNextLine()) { + if (checkPalindrome(in.nextLine())) { + System.out.println("Yes"); + } else { + System.out.println("No"); + } + } + } + + public static boolean checkPalindrome(String s) { + if (s == null || s.length() == 0) { + return false; + } + HashMap map = new HashMap(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (!map.containsKey(c)) { + map.put(c, 0); + } + map.put(c, map.get(c) + 1); + } + int countSingle = 0; + for (Map.Entry entry : map.entrySet()) { + countSingle += entry.getValue() % 2; + if (countSingle > 1) { + return false; + } + } + return true; + } + + +} +``` \ No newline at end of file diff --git a/README.md b/README.md index 85fdb2e..89ec2cf 100644 --- a/README.md +++ b/README.md @@ -1,252 +1,540 @@ -# LintCode - -To host Java Solutions to problems from LintCode(http://LintCode.com). -I Will try to revise the solutions once new problem or new testing case occurs. - -| Squence | Problem | Level | Language | -|:-------:|:--------------|:---------------|:---------:| -|0|[2 Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/2 Sum.java)| |Java| -|1|[3 Sum Closest.java](https://github.com/shawnfan/LintCode/blob/master/Java/3 Sum Closest.java)| |Java| -|2|[3 Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/3 Sum.java)| |Java| -|3|[4 Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/4 Sum.java)| |Java| -|4|[A+B.java](https://github.com/shawnfan/LintCode/blob/master/Java/A+B.java)| |Java| -|5|[Add and Search Word.java](https://github.com/shawnfan/LintCode/blob/master/Java/Add and Search Word.java)| |Java| -|6|[Add Binary.java](https://github.com/shawnfan/LintCode/blob/master/Java/Add Binary.java)| |Java| -|7|[Add Two Numbers II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Add Two Numbers II.java)| |Java| -|8|[Anagrams.java](https://github.com/shawnfan/LintCode/blob/master/Java/Anagrams.java)| |Java| -|9|[Backpack II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Backpack II.java)| |Java| -|10|[Backpack.java](https://github.com/shawnfan/LintCode/blob/master/Java/Backpack.java)| |Java| -|11|[Balanced Binary Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Balanced Binary Tree.java)| |Java| -|12|[Best Time to Buy and Sell Stock I.java](https://github.com/shawnfan/LintCode/blob/master/Java/Best Time to Buy and Sell Stock I.java)| |Java| -|13|[Best Time to Buy and Sell Stock II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Best Time to Buy and Sell Stock II.java)| |Java| -|14|[Best Time to Buy and Sell Stock III .java](https://github.com/shawnfan/LintCode/blob/master/Java/Best Time to Buy and Sell Stock III .java)| |Java| -|15|[Best Time to Buy and Sell Stock IV.java](https://github.com/shawnfan/LintCode/blob/master/Java/Best Time to Buy and Sell Stock IV.java)| |Java| -|16|[Binary Representation.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Representation.java)| |Java| -|17|[Binary Search Tree Iterator.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Search Tree Iterator.java)| |Java| -|18|[Binary Search.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Search.java)| |Java| -|19|[Binary Tree Inorder Traversal.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Tree Inorder Traversal.java)| |Java| -|20|[Binary Tree Level Order Traversal II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Tree Level Order Traversal II.java)| |Java| -|21|[Binary Tree Level Order Traversal.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Tree Level Order Traversal.java)| |Java| -|22|[Binary Tree Maximum Path Sum II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Tree Maximum Path Sum II.java)| |Java| -|23|[Binary Tree Maximum Path Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Tree Maximum Path Sum.java)| |Java| -|24|[Binary Tree Path Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Tree Path Sum.java)| |Java| -|25|[Binary Tree Paths.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Tree Paths.java)| |Java| -|26|[Binary Tree Postorder Traversal.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Tree Postorder Traversal.java)| |Java| -|27|[Binary Tree Preorder Traversal.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Tree Preorder Traversal.java)| |Java| -|28|[Binary Tree Zigzag Level Order Traversal.java](https://github.com/shawnfan/LintCode/blob/master/Java/Binary Tree Zigzag Level Order Traversal.java)| |Java| -|29|[Building Outline.java](https://github.com/shawnfan/LintCode/blob/master/Java/Building Outline.java)| |Java| -|30|[Classical Binary Search.java](https://github.com/shawnfan/LintCode/blob/master/Java/Classical Binary Search.java)| |Java| -|31|[Climbing Stairs.java](https://github.com/shawnfan/LintCode/blob/master/Java/Climbing Stairs.java)| |Java| -|32|[Clone Graph.java](https://github.com/shawnfan/LintCode/blob/master/Java/Clone Graph.java)| |Java| -|33|[Closest Number in Sorted Array.java](https://github.com/shawnfan/LintCode/blob/master/Java/Closest Number in Sorted Array.java)| |Java| -|34|[Coins in a Line.java](https://github.com/shawnfan/LintCode/blob/master/Java/Coins in a Line.java)| |Java| -|35|[Combination Sum II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Combination Sum II.java)| |Java| -|36|[Combination Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/Combination Sum.java)| |Java| -|37|[Combinations.java](https://github.com/shawnfan/LintCode/blob/master/Java/Combinations.java)| |Java| -|38|[Compare Strings.java](https://github.com/shawnfan/LintCode/blob/master/Java/Compare Strings.java)| |Java| -|39|[Complete Binary Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Complete Binary Tree.java)| |Java| -|40|[Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/shawnfan/LintCode/blob/master/Java/Construct Binary Tree from Inorder and Postorder Traversal.java)| |Java| -|41|[Construct Binary Tree from Inorder and Preorder Traversal.java](https://github.com/shawnfan/LintCode/blob/master/Java/Construct Binary Tree from Inorder and Preorder Traversal.java)| |Java| -|42|[Container With Most Water.java](https://github.com/shawnfan/LintCode/blob/master/Java/Container With Most Water.java)| |Java| -|43|[Convert Binary Search Tree to Doubly Linked List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Convert Binary Search Tree to Doubly Linked List.java)| |Java| -|44|[Convert Expression to Polish Notation.java](https://github.com/shawnfan/LintCode/blob/master/Java/Convert Expression to Polish Notation.java)| |Java| -|45|[Convert Expression to Reverse Polish Notation.java](https://github.com/shawnfan/LintCode/blob/master/Java/Convert Expression to Reverse Polish Notation.java)| |Java| -|46|[Convert Integer A to Integer B.java](https://github.com/shawnfan/LintCode/blob/master/Java/Convert Integer A to Integer B.java)| |Java| -|47|[Convert Sorted Array to Binary Search Tree With Minimal Height.java](https://github.com/shawnfan/LintCode/blob/master/Java/Convert Sorted Array to Binary Search Tree With Minimal Height.java)| |Java| -|48|[Convert Sorted List to Binary Search Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Convert Sorted List to Binary Search Tree.java)| |Java| -|49|[Copy List with Random Pointer.java](https://github.com/shawnfan/LintCode/blob/master/Java/Copy List with Random Pointer.java)| |Java| -|50|[Cosine Similarity.java](https://github.com/shawnfan/LintCode/blob/master/Java/Cosine Similarity.java)| |Java| -|51|[Count 1 in Binary.java](https://github.com/shawnfan/LintCode/blob/master/Java/Count 1 in Binary.java)| |Java| -|52|[Count and Say.java](https://github.com/shawnfan/LintCode/blob/master/Java/Count and Say.java)| |Java| -|53|[Count of Smaller Number before itself.java](https://github.com/shawnfan/LintCode/blob/master/Java/Count of Smaller Number before itself.java)| |Java| -|54|[Count of Smaller Number.java](https://github.com/shawnfan/LintCode/blob/master/Java/Count of Smaller Number.java)| |Java| -|55|[Data Stream Median.java](https://github.com/shawnfan/LintCode/blob/master/Java/Data Stream Median.java)| |Java| -|56|[Delete Digits.java](https://github.com/shawnfan/LintCode/blob/master/Java/Delete Digits.java)| |Java| -|57|[Delete Node in the Middle of Singly Linked List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Delete Node in the Middle of Singly Linked List.java)| |Java| -|58|[Distinct Subsequences.java](https://github.com/shawnfan/LintCode/blob/master/Java/Distinct Subsequences.java)| |Java| -|59|[Easy Reverse Linked List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Easy Reverse Linked List.java)| |Java| -|60|[Edit Distance.java](https://github.com/shawnfan/LintCode/blob/master/Java/Edit Distance.java)| |Java| -|61|[Expression Evaluation.java](https://github.com/shawnfan/LintCode/blob/master/Java/Expression Evaluation.java)| |Java| -|62|[Expression Tree Build.java](https://github.com/shawnfan/LintCode/blob/master/Java/Expression Tree Build.java)| |Java| -|63|[Fast Power.java](https://github.com/shawnfan/LintCode/blob/master/Java/Fast Power.java)| |Java| -|64|[Fibonacci.java](https://github.com/shawnfan/LintCode/blob/master/Java/Fibonacci.java)| |Java| -|65|[Find Minimum in Rotated Sorted Array II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Find Minimum in Rotated Sorted Array II.java)| |Java| -|66|[Find Minimum in Rotated Sorted Array.java](https://github.com/shawnfan/LintCode/blob/master/Java/Find Minimum in Rotated Sorted Array.java)| |Java| -|67|[Find Peak Element II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Find Peak Element II.java)| |Java| -|68|[Find Peak Element.java](https://github.com/shawnfan/LintCode/blob/master/Java/Find Peak Element.java)| |Java| -|69|[Find the Connected Component in the Undirected Graph.java](https://github.com/shawnfan/LintCode/blob/master/Java/Find the Connected Component in the Undirected Graph.java)| |Java| -|70|[Find the Weak Connected Component in the Directed Graph.java](https://github.com/shawnfan/LintCode/blob/master/Java/Find the Weak Connected Component in the Directed Graph.java)| |Java| -|71|[First Bad Version.java](https://github.com/shawnfan/LintCode/blob/master/Java/First Bad Version.java)| |Java| -|72|[First Missing Positive.java](https://github.com/shawnfan/LintCode/blob/master/Java/First Missing Positive.java)| |Java| -|73|[Flatten Binary Tree to Linked List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Flatten Binary Tree to Linked List.java)| |Java| -|74|[Gas Station.java](https://github.com/shawnfan/LintCode/blob/master/Java/Gas Station.java)| |Java| -|75|[Generate Parentheses.java](https://github.com/shawnfan/LintCode/blob/master/Java/Generate Parentheses.java)| |Java| -|76|[Graph Valid Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Graph Valid Tree.java)| |Java| -|77|[Hash Function.java](https://github.com/shawnfan/LintCode/blob/master/Java/Hash Function.java)| |Java| -|78|[HashWithArray.java](https://github.com/shawnfan/LintCode/blob/master/Java/HashWithArray.java)| |Java| -|79|[Heapify.java](https://github.com/shawnfan/LintCode/blob/master/Java/Heapify.java)| |Java| -|80|[House Robber.java](https://github.com/shawnfan/LintCode/blob/master/Java/House Robber.java)| |Java| -|81|[Identical Binary Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Identical Binary Tree.java)| |Java| -|82|[Implement Queue by Two Stacks.java](https://github.com/shawnfan/LintCode/blob/master/Java/Implement Queue by Two Stacks.java)| |Java| -|83|[Implement Trie.java](https://github.com/shawnfan/LintCode/blob/master/Java/Implement Trie.java)| |Java| -|84|[Insert Interval.java](https://github.com/shawnfan/LintCode/blob/master/Java/Insert Interval.java)| |Java| -|85|[Insert Node in a Binary Search Tree .java](https://github.com/shawnfan/LintCode/blob/master/Java/Insert Node in a Binary Search Tree .java)| |Java| -|86|[Insertion Sort List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Insertion Sort List.java)| |Java| -|87|[Interleaving Positive and Negative Numbers.java](https://github.com/shawnfan/LintCode/blob/master/Java/Interleaving Positive and Negative Numbers.java)| |Java| -|88|[Interleaving String.java](https://github.com/shawnfan/LintCode/blob/master/Java/Interleaving String.java)| |Java| -|89|[Intersection of Two Linked Lists.java](https://github.com/shawnfan/LintCode/blob/master/Java/Intersection of Two Linked Lists.java)| |Java| -|90|[Interval Minimum Number.java](https://github.com/shawnfan/LintCode/blob/master/Java/Interval Minimum Number.java)| |Java| -|91|[Interval Sum II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Interval Sum II.java)| |Java| -|92|[Interval Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/Interval Sum.java)| |Java| -|93|[Invert Binary Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Invert Binary Tree.java)| |Java| -|94|[Jump Game II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Jump Game II.java)| |Java| -|95|[Jump Game.java](https://github.com/shawnfan/LintCode/blob/master/Java/Jump Game.java)| |Java| -|96|[Kth Largest Element.java](https://github.com/shawnfan/LintCode/blob/master/Java/Kth Largest Element.java)| |Java| -|97|[Kth Smallest Sum In Two Sorted Arrays.java](https://github.com/shawnfan/LintCode/blob/master/Java/Kth Smallest Sum In Two Sorted Arrays.java)| |Java| -|98|[Largest Number.java](https://github.com/shawnfan/LintCode/blob/master/Java/Largest Number.java)| |Java| -|99|[Largest Rectangle in Histogram.java](https://github.com/shawnfan/LintCode/blob/master/Java/Largest Rectangle in Histogram.java)| |Java| -|100|[Last Position of Target.java](https://github.com/shawnfan/LintCode/blob/master/Java/Last Position of Target.java)| |Java| -|101|[Length of Last Word.java](https://github.com/shawnfan/LintCode/blob/master/Java/Length of Last Word.java)| |Java| -|102|[Letter Combinations of a Phone Number.java](https://github.com/shawnfan/LintCode/blob/master/Java/Letter Combinations of a Phone Number.java)| |Java| -|103|[Linked List Cycle II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Linked List Cycle II.java)| |Java| -|104|[Linked List Cycle.java](https://github.com/shawnfan/LintCode/blob/master/Java/Linked List Cycle.java)| |Java| -|105|[Longest Common Prefix.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Common Prefix.java)| |Java| -|106|[Longest Common Subsequence.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Common Subsequence.java)| |Java| -|107|[Longest Common Substring.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Common Substring.java)| |Java| -|108|[Longest Consecutive Sequence.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Consecutive Sequence.java)| |Java| -|109|[Longest Increasing Continuous subsequence II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Increasing Continuous subsequence II.java)| |Java| -|110|[Longest Increasing Continuous subsequence.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Increasing Continuous subsequence.java)| |Java| -|111|[Longest Increasing Subsequence.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Increasing Subsequence.java)| |Java| -|112|[Longest Palindromic Substring.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Palindromic Substring.java)| |Java| -|113|[Longest Substring with At Most K Distinct Characters.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Substring with At Most K Distinct Characters.java)| |Java| -|114|[Longest Substring Without Repeating Characters.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Substring Without Repeating Characters.java)| |Java| -|115|[Longest Words.java](https://github.com/shawnfan/LintCode/blob/master/Java/Longest Words.java)| |Java| -|116|[Lowest Common Ancestor II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Lowest Common Ancestor II.java)| |Java| -|117|[Lowest Common Ancestor.java](https://github.com/shawnfan/LintCode/blob/master/Java/Lowest Common Ancestor.java)| |Java| -|118|[Majority Number II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Majority Number II.java)| |Java| -|119|[Majority Number III.java](https://github.com/shawnfan/LintCode/blob/master/Java/Majority Number III.java)| |Java| -|120|[Majority Number.java](https://github.com/shawnfan/LintCode/blob/master/Java/Majority Number.java)| |Java| -|121|[Matrix Zigzag Traversal.java](https://github.com/shawnfan/LintCode/blob/master/Java/Matrix Zigzag Traversal.java)| |Java| -|122|[Max Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Max Tree.java)| |Java| -|123|[Maximal Square.java](https://github.com/shawnfan/LintCode/blob/master/Java/Maximal Square.java)| |Java| -|124|[Maximum Depth of Binary Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Maximum Depth of Binary Tree.java)| |Java| -|125|[Maximum Product Subarray.java](https://github.com/shawnfan/LintCode/blob/master/Java/Maximum Product Subarray.java)| |Java| -|126|[Maximum Subarray III.java](https://github.com/shawnfan/LintCode/blob/master/Java/Maximum Subarray III.java)| |Java| -|127|[MaximumSubarray.java](https://github.com/shawnfan/LintCode/blob/master/Java/MaximumSubarray.java)| |Java| -|128|[MaximumSubarrayII.java](https://github.com/shawnfan/LintCode/blob/master/Java/MaximumSubarrayII.java)| |Java| -|129|[Median of two Sorted Arrays.java](https://github.com/shawnfan/LintCode/blob/master/Java/Median of two Sorted Arrays.java)| |Java| -|130|[Median.java](https://github.com/shawnfan/LintCode/blob/master/Java/Median.java)| |Java| -|131|[Merge Intervals.java](https://github.com/shawnfan/LintCode/blob/master/Java/Merge Intervals.java)| |Java| -|132|[Merge k Sorted Arrays.java](https://github.com/shawnfan/LintCode/blob/master/Java/Merge k Sorted Arrays.java)| |Java| -|133|[Merge k Sorted Lists.java](https://github.com/shawnfan/LintCode/blob/master/Java/Merge k Sorted Lists.java)| |Java| -|134|[Merge Sorted Array II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Merge Sorted Array II.java)| |Java| -|135|[Merge Sorted Array.java](https://github.com/shawnfan/LintCode/blob/master/Java/Merge Sorted Array.java)| |Java| -|136|[Merge Two Sorted List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Merge Two Sorted List.java)| |Java| -|137|[Middle of Linked List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Middle of Linked List.java)| |Java| -|138|[Min Stack.java](https://github.com/shawnfan/LintCode/blob/master/Java/Min Stack.java)| |Java| -|139|[Minimum Path Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/Minimum Path Sum.java)| |Java| -|140|[Minimum Size Subarray Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/Minimum Size Subarray Sum.java)| |Java| -|141|[Minimum Subarray.java](https://github.com/shawnfan/LintCode/blob/master/Java/Minimum Subarray.java)| |Java| -|142|[Minimum Window Substring.java](https://github.com/shawnfan/LintCode/blob/master/Java/Minimum Window Substring.java)| |Java| -|143|[MinimumDepthOfBinaryTree.java](https://github.com/shawnfan/LintCode/blob/master/Java/MinimumDepthOfBinaryTree.java)| |Java| -|144|[Next Permutation.java](https://github.com/shawnfan/LintCode/blob/master/Java/Next Permutation.java)| |Java| -|145|[NQueens.java](https://github.com/shawnfan/LintCode/blob/master/Java/NQueens.java)| |Java| -|146|[NQueensII.java](https://github.com/shawnfan/LintCode/blob/master/Java/NQueensII.java)| |Java| -|147|[Nth to Last Node in List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Nth to Last Node in List.java)| |Java| -|148|[Number of Airplane in the sky.java](https://github.com/shawnfan/LintCode/blob/master/Java/Number of Airplane in the sky.java)| |Java| -|149|[Number of Islands II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Number of Islands II.java)| |Java| -|150|[Number of Islands.java](https://github.com/shawnfan/LintCode/blob/master/Java/Number of Islands.java)| |Java| -|151|[Number Triangles.java](https://github.com/shawnfan/LintCode/blob/master/Java/Number Triangles.java)| |Java| -|152|[O(1) Check Power of 2.java](https://github.com/shawnfan/LintCode/blob/master/Java/O(1) Check Power of 2.java)| |Java| -|153|[Palindrome Linked List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Palindrome Linked List.java)| |Java| -|154|[Palindrome Partitioning II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Palindrome Partitioning II.java)| |Java| -|155|[Palindrome Partitioning.java](https://github.com/shawnfan/LintCode/blob/master/Java/Palindrome Partitioning.java)| |Java| -|156|[Partition Array by Odd and Even.java](https://github.com/shawnfan/LintCode/blob/master/Java/Partition Array by Odd and Even.java)| |Java| -|157|[Partition Array.java](https://github.com/shawnfan/LintCode/blob/master/Java/Partition Array.java)| |Java| -|158|[Partition List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Partition List.java)| |Java| -|159|[Permutation Index.java](https://github.com/shawnfan/LintCode/blob/master/Java/Permutation Index.java)| |Java| -|160|[Permutations II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Permutations II.java)| |Java| -|161|[Permutations.java](https://github.com/shawnfan/LintCode/blob/master/Java/Permutations.java)| |Java| -|162|[Plus One.java](https://github.com/shawnfan/LintCode/blob/master/Java/Plus One.java)| |Java| -|163|[Pow(x,n).java](https://github.com/shawnfan/LintCode/blob/master/Java/Pow(x,n).java)| |Java| -|164|[Product of Array Exclude Itself.java](https://github.com/shawnfan/LintCode/blob/master/Java/Product of Array Exclude Itself.java)| |Java| -|165|[QuickSort.java](https://github.com/shawnfan/LintCode/blob/master/Java/QuickSort.java)| |Java| -|166|[Recover Rotated Sorted Array.java](https://github.com/shawnfan/LintCode/blob/master/Java/Recover Rotated Sorted Array.java)| |Java| -|167|[Rehashing.java](https://github.com/shawnfan/LintCode/blob/master/Java/Rehashing.java)| |Java| -|168|[Remove Duplicates from Sorted Array.java](https://github.com/shawnfan/LintCode/blob/master/Java/Remove Duplicates from Sorted Array.java)| |Java| -|169|[Remove Duplicates from Sorted List II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Remove Duplicates from Sorted List II.java)| |Java| -|170|[Remove Duplicates from Sorted List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Remove Duplicates from Sorted List.java)| |Java| -|171|[Remove Duplicates from Unsorted List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Remove Duplicates from Unsorted List.java)| |Java| -|172|[Remove Linked List Elements.java](https://github.com/shawnfan/LintCode/blob/master/Java/Remove Linked List Elements.java)| |Java| -|173|[Remove Node in Binary Search Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Remove Node in Binary Search Tree.java)| |Java| -|174|[Remove Nth Node From End of List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Remove Nth Node From End of List.java)| |Java| -|175|[Reorder List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Reorder List.java)| |Java| -|176|[Reverse Integer.java](https://github.com/shawnfan/LintCode/blob/master/Java/Reverse Integer.java)| |Java| -|177|[Reverse Linked List II .java](https://github.com/shawnfan/LintCode/blob/master/Java/Reverse Linked List II .java)| |Java| -|178|[Reverse Linked List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Reverse Linked List.java)| |Java| -|179|[Reverse Words in a String.java](https://github.com/shawnfan/LintCode/blob/master/Java/Reverse Words in a String.java)| |Java| -|180|[Rotate List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Rotate List.java)| |Java| -|181|[Rotate String.java](https://github.com/shawnfan/LintCode/blob/master/Java/Rotate String.java)| |Java| -|182|[Search a 2D Matrix II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Search a 2D Matrix II.java)| |Java| -|183|[Search a 2D Matrix.java](https://github.com/shawnfan/LintCode/blob/master/Java/Search a 2D Matrix.java)| |Java| -|184|[Search for a Range.java](https://github.com/shawnfan/LintCode/blob/master/Java/Search for a Range.java)| |Java| -|185|[Search Insert Position.java](https://github.com/shawnfan/LintCode/blob/master/Java/Search Insert Position.java)| |Java| -|186|[Search Range in Binary Search Tree .java](https://github.com/shawnfan/LintCode/blob/master/Java/Search Range in Binary Search Tree .java)| |Java| -|187|[Search Rotated in Sorted Array II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Search Rotated in Sorted Array II.java)| |Java| -|188|[Search Rotated in Sorted Array.java](https://github.com/shawnfan/LintCode/blob/master/Java/Search Rotated in Sorted Array.java)| |Java| -|189|[Segment Tree Build II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Segment Tree Build II.java)| |Java| -|190|[Segment Tree Build.java](https://github.com/shawnfan/LintCode/blob/master/Java/Segment Tree Build.java)| |Java| -|191|[Segment Tree Modify.java](https://github.com/shawnfan/LintCode/blob/master/Java/Segment Tree Modify.java)| |Java| -|192|[Segment Tree Query II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Segment Tree Query II.java)| |Java| -|193|[Segment Tree Query.java](https://github.com/shawnfan/LintCode/blob/master/Java/Segment Tree Query.java)| |Java| -|194|[Serilization and Deserialization Of Binary Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Serilization and Deserialization Of Binary Tree.java)| |Java| -|195|[Single Number II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Single Number II.java)| |Java| -|196|[Single Number III.java](https://github.com/shawnfan/LintCode/blob/master/Java/Single Number III.java)| |Java| -|197|[Single Number.java](https://github.com/shawnfan/LintCode/blob/master/Java/Single Number.java)| |Java| -|198|[Singleton.java](https://github.com/shawnfan/LintCode/blob/master/Java/Singleton.java)| |Java| -|199|[Sliding Window Maximum.java](https://github.com/shawnfan/LintCode/blob/master/Java/Sliding Window Maximum.java)| |Java| -|200|[Sliding Window Median.java](https://github.com/shawnfan/LintCode/blob/master/Java/Sliding Window Median.java)| |Java| -|201|[Sort Color.java](https://github.com/shawnfan/LintCode/blob/master/Java/Sort Color.java)| |Java| -|202|[Sort Colors II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Sort Colors II.java)| |Java| -|203|[Sort Letters by Case.java](https://github.com/shawnfan/LintCode/blob/master/Java/Sort Letters by Case.java)| |Java| -|204|[Sort List.java](https://github.com/shawnfan/LintCode/blob/master/Java/Sort List.java)| |Java| -|205|[Space Replacement.java](https://github.com/shawnfan/LintCode/blob/master/Java/Space Replacement.java)| |Java| -|206|[Sqrt(x).java](https://github.com/shawnfan/LintCode/blob/master/Java/Sqrt(x).java)| |Java| -|207|[Stone Game.java](https://github.com/shawnfan/LintCode/blob/master/Java/Stone Game.java)| |Java| -|208|[String to Integer(atoi).java](https://github.com/shawnfan/LintCode/blob/master/Java/String to Integer(atoi).java)| |Java| -|209|[StrStr.java](https://github.com/shawnfan/LintCode/blob/master/Java/StrStr.java)| |Java| -|210|[Subarray Sum Closest.java](https://github.com/shawnfan/LintCode/blob/master/Java/Subarray Sum Closest.java)| |Java| -|211|[Subarray Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/Subarray Sum.java)| |Java| -|212|[Subset.java](https://github.com/shawnfan/LintCode/blob/master/Java/Subset.java)| |Java| -|213|[Subsets II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Subsets II.java)| |Java| -|214|[Subtree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Subtree.java)| |Java| -|215|[Swap Nodes in Pairs.java](https://github.com/shawnfan/LintCode/blob/master/Java/Swap Nodes in Pairs.java)| |Java| -|216|[Symmetric Binary Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Symmetric Binary Tree.java)| |Java| -|217|[The Smallest Difference.java](https://github.com/shawnfan/LintCode/blob/master/Java/The Smallest Difference.java)| |Java| -|218|[Topological Sorting.java](https://github.com/shawnfan/LintCode/blob/master/Java/Topological Sorting.java)| |Java| -|219|[Total Occurrence of Target.java](https://github.com/shawnfan/LintCode/blob/master/Java/Total Occurrence of Target.java)| |Java| -|220|[Trailing Zeros.java](https://github.com/shawnfan/LintCode/blob/master/Java/Trailing Zeros.java)| |Java| -|221|[Trapping Rain Water II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Trapping Rain Water II.java)| |Java| -|222|[Trapping Rain Water.java](https://github.com/shawnfan/LintCode/blob/master/Java/Trapping Rain Water.java)| |Java| -|223|[Triangle Count.java](https://github.com/shawnfan/LintCode/blob/master/Java/Triangle Count.java)| |Java| -|224|[Tweaked Identical Binary Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Tweaked Identical Binary Tree.java)| |Java| -|225|[Two Lists Sum.java](https://github.com/shawnfan/LintCode/blob/master/Java/Two Lists Sum.java)| |Java| -|226|[Two Strings Are Anagrams.java](https://github.com/shawnfan/LintCode/blob/master/Java/Two Strings Are Anagrams.java)| |Java| -|227|[Two Sum II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Two Sum II.java)| |Java| -|228|[Ugly Number.java](https://github.com/shawnfan/LintCode/blob/master/Java/Ugly Number.java)| |Java| -|229|[Unique Binary Search Tree II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Unique Binary Search Tree II.java)| |Java| -|230|[Unique Binary Search Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Unique Binary Search Tree.java)| |Java| -|231|[Unique Characters.java](https://github.com/shawnfan/LintCode/blob/master/Java/Unique Characters.java)| |Java| -|232|[Unique Path.java](https://github.com/shawnfan/LintCode/blob/master/Java/Unique Path.java)| |Java| -|233|[Unique Paths II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Unique Paths II.java)| |Java| -|234|[Update Bits.java](https://github.com/shawnfan/LintCode/blob/master/Java/Update Bits.java)| |Java| -|235|[Valid Palindrome.java](https://github.com/shawnfan/LintCode/blob/master/Java/Valid Palindrome.java)| |Java| -|236|[Valid Parentheses.java](https://github.com/shawnfan/LintCode/blob/master/Java/Valid Parentheses.java)| |Java| -|237|[Valid Sudoku.java](https://github.com/shawnfan/LintCode/blob/master/Java/Valid Sudoku.java)| |Java| -|238|[Validate Binary Search Tree.java](https://github.com/shawnfan/LintCode/blob/master/Java/Validate Binary Search Tree.java)| |Java| -|239|[Wood Cut.java](https://github.com/shawnfan/LintCode/blob/master/Java/Wood Cut.java)| |Java| -|240|[Word Break.java](https://github.com/shawnfan/LintCode/blob/master/Java/Word Break.java)| |Java| -|241|[Word Ladder II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Word Ladder II.java)| |Java| -|242|[Word Ladder.java](https://github.com/shawnfan/LintCode/blob/master/Java/Word Ladder.java)| |Java| -|243|[Word Search II.java](https://github.com/shawnfan/LintCode/blob/master/Java/Word Search II.java)| |Java| -|244|[Word Search.java](https://github.com/shawnfan/LintCode/blob/master/Java/Word Search.java)| |Java| +# Java Algorithm Problems + +### 程序员的一天 +从开始这个Github已经有将近两年时间, 很高兴这个repo可以帮到有需要的人. 我一直认为, 知识本身是无价的, 因此每逢闲暇, 我就会来维护这个repo, 给刷题的朋友们一些我的想法和见解. 下面来简单介绍一下这个repo: + +**README.md**: 所有所做过的题目 + +**ReviewPage.md**: 所有题目的总结和归纳(不断完善中) + +**KnowledgeHash2.md**: 对所做过的知识点的一些笔记 + +**SystemDesign.md**: 对系统设计的一些笔记 + +**Future Milestone**: 我准备将一些有意思的题目,做成视频的形式给大家参考 + +### 在这里! 安利一下自己的副业 + +介绍一下自己! + +* [反驳: 赚的多就该996.icu? 盘一盘程序员的三六九等! (2019)](https://youtu.be/gPELHMkHEpE) +* [996.icu 今日敏感话题: 强制加班的背后 (2019)](https://youtu.be/QR7vi3G5M2Q) +* [如何拿到亚马逊的工作! How succeed in Amazon Interview! (2019)](https://youtu.be/NIuOjjKaK9M) +* [与PM项目经理战斗 - 如何取得胜利? (2019)](https://youtu.be/KyFMNRKN2Rs) +* [十分钟学会Python? (2019)](https://youtu.be/DRQYOdO9BAU) +* [刚到美国到底怎样开口说英文? (2019)](https://youtu.be/pd3WR5K-bLs) + +最近我开始在做自己的[Youtube Channel](https://www.youtube.com/channel/UCQNPegv0VqempHNYPWKkVNw/featured?view_as=subscriber), 虽然还没有定下任何方向, 但是目前我会做几部分的内容: +* 工作经验的分享/目前学习经历: 当然会跟Software Engineer比较相关 +* 在美国的生活/学生时代的经历, vlog等 +* 个人的兴趣爱好: 唱歌!!! + +希望在这里参考刷题经验时, 可以去关注我的Channel! 有任何对在美国工作的疑问, 疑惑, 都可以给我留言/私信/邮件. + +大家在YouTube或者B站都可以搜到我: "张土汪" + +* [Youtube: 张土汪](https://www.youtube.com/channel/UCQNPegv0VqempHNYPWKkVNw/featured?view_as=subscriber) +* [Bilibili: 张土汪](https://space.bilibili.com/249496206) + +希望大家学习顺利, 对未来充满希望! +有问题可以给我写邮件(wangdeve@gmail.com), 或者在GitHub上发issue给我. + + +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Count of Smaller Number before itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number%20before%20itself.java)|Hard|Java|[]|| +|1|[Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|2|[Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)|Medium|Java|[Hash Table, Math]|| +|3|[Gray Code.java](https://github.com/awangdev/LintCode/blob/master/Java/Gray%20Code.java)|Medium|Java|[Backtracking]|| +|4|[Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Hamming%20Distance.java)|Easy|Java|[]|| +|5|[Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Happy%20Number.java)|Easy|Java|[]|| +|6|[HashWithArray.java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithArray.java)|Easy|Java|[]|| +|7|[Heaters.java](https://github.com/awangdev/LintCode/blob/master/Java/Heaters.java)|Easy|Java|[]|| +|8|[IndexMatch.java](https://github.com/awangdev/LintCode/blob/master/Java/IndexMatch.java)|Easy|Java|[]|| +|9|[Insert Node in a Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Node%20in%20a%20Binary%20Search%20Tree%20.java)|Easy|Java|[BST]|| +|10|[Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/Jewels%20and%20Stones.java)|Easy|Java|[Hash Table]|| +|11|[Kth Smallest Sum In Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Sum%20In%20Two%20Sorted%20Arrays.java)|Hard|Java|[]|| +|12|[LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)|Hard|Java|[Design, Hash Table]|| +|13|[Longest Univalue Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Univalue%20Path.java)|Easy|Java|[]|| +|14|[Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)|Medium|Java|[Enumeration, Greedy]|| +|15|[Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)|Medium|Java|[Hash Table, Linked List]|| +|16|[Matrix Zigzag Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Matrix%20Zigzag%20Traversal.java)|Easy|Java|[]|| +|17|[Maximum Subarray III.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20III.java)|Review|Java|[]|| +|18|[Minimum Absolute Difference in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Absolute%20Difference%20in%20BST.java)|Easy|Java|[BST]|| +|19|[Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)|Medium|Java|[BFS, Graph]|| +|20|[Missing Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Ranges.java)|Medium|Java|[Array]|| +|21|[Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Permutation.java)|Medium|Java|[Array]|| +|22|[O(1) Check Power of 2.java](https://github.com/awangdev/LintCode/blob/master/Java/O(1)%20Check%20Power%20of%202.java)|Easy|Java|[Bit Manipulation]|| +|23|[Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)|Medium|Java|[Backtracking, Permutation]|| +|24|[Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)|Easy|Java|[Array, Two Pointers]|| +|25|[Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Pascal's%20Triangle%20II.java)|Easy|Java|[]|| +|26|[Permutation Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Index.java)|Easy|Java|[]|| +|27|[Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)|Medium|Java|[Backtracking, Math]|| +|28|[Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Prefix%20and%20Suffix%20Search.java)|Hard|Java|[Trie]|| +|29|[Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Exclude%20Itself.java)|Medium|Java|[Array]|| +|30|[Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Rotated%20Sorted%20Array.java)|Easy|Java|[Array]|| +|31|[Remove Duplicates from Unsorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Unsorted%20List.java)|Medium|Java|[Linked List]|| +|32|[Remove Node in Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Node%20in%20Binary%20Search%20Tree.java)|Hard|Java|[BST]|| +|33|[Reshape the Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Reshape%20the%20Matrix.java)|Easy|Java|[]|| +|34|[Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)|Easy|Java|[]|| +|35|[Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)|Medium|Java|[Array, Enumeration]|| +|36|[Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array%20II.java)|Medium|Java|[Array, Binary Search]|| +|37|[Search Insert Position.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Insert%20Position.java)|Easy|Java|[]|| +|38|[Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Word%20Distance.java)|Easy|Java|[]|| +|39|[Single Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20II.java)|Medium|Java|[Bit Manipulation]|| +|40|[Single Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20III.java)|Medium|Java|[Bit Manipulation]|| +|41|[Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number.java)|Easy|Java|[]|| +|42|[Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)|Medium|Java|[String]|| +|43|[Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)|Medium|Java|[DP]|| +|44|[String Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/String%20Permutation.java)|Easy|Java|[]|| +|45|[Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)|Hard|Java|[Array, Binary Search, Two Pointers]|| +|46|[The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)|Medium|Java|[Array, Sort, Two Pointers]|| +|47|[Total Occurrence of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Occurrence%20of%20Target.java)|Medium|Java|[]|| +|48|[Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)|Easy|Java|[Math]|| +|49|[Two Lists Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Lists%20Sum.java)|Medium|Java|[Linked List]|| +|50|[Two Strings Are Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Strings%20Are%20Anagrams.java)|Easy|Java|[]|| +|51|[Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Sudoku.java)|Easy|Java|[Enumeration, Hash Table]|| +|52|[Word Pattern.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Pattern.java)|Easy|Java|[]|| +|53|[Zigzag Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Zigzag%20Iterator.java)|Medium|Java|[BST]|| +|54|[Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Anagram%20Mappings.java)|Easy|Java|[Hash Table]|| +|55|[Judge Route Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/Judge%20Route%20Circle.java)|Easy|Java|[String]|| +|56|[Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/Island%20Perimeter.java)|Easy|Java|[Hash Table]|| +|57|[Power of Three.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Three.java)|Easy|Java|[Math]|| +|58|[Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)|Easy|Java|[Array, Math]|| +|59|[Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)|Easy|Java|[Bit Manipulation, Math]|| +|60|[Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Vowels%20of%20a%20String.java)|Easy|Java|[String, Two Pointers]|| +|61|[Guess Number Higher or Lower.java](https://github.com/awangdev/LintCode/blob/master/Java/Guess%20Number%20Higher%20or%20Lower.java)|Easy|Java|[Binary Search]|| +|62|[Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)|Medium|Java|[Hash Table, Math]|| +|63|[Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)|Medium|Java|[Array, Sort]|| +|64|[Queue Reconstruction by Height.java](https://github.com/awangdev/LintCode/blob/master/Java/Queue%20Reconstruction%20by%20Height.java)|Medium|Java|[Greedy]|| +|65|[Two Sum II - Input array is sorted.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20II%20-%20Input%20array%20is%20sorted.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|66|[2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum%20II.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|67|[Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)|Medium|Java|[Backpack DP, DP, Memoization]|| +|68|[Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)|Medium|Java|[Array, DP, Subarray]|| +|69|[3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/3%20Sum%20Closest.java)|Medium|Java|[Array, Two Pointers]|| +|70|[Triangle Count.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangle%20Count.java)|Medium|Java|[Array]|| +|71|[3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum.java)|Medium|Java|[Array, Two Pointers]|| +|72|[k Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/k%20Sum.java)|Hard|Java|[DP]|| +|73|[Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DP, Tree]|| +|74|[Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)|Easy|Java|[BST, Tree]|| +|75|[Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|76|[Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/Bomb%20Enemy.java)|Medium|Java|[Coordinate DP, DP]|| +|77|[3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum%20Smaller.java)|Medium|Java|[Array, Two Pointers]|| +|78|[Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/Array%20Partition%20I.java)|Easy|Java|[Array]|| +|79|[1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/1-bit%20and%202-bit%20Characters.java)|Easy|Java|[Array]|| +|80|[Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Non-decreasing%20Array.java)|Easy|Java|[Array]|| +|81|[Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Consecutive%20Ones.java)|Easy|Java|[Array]|| +|82|[Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)|Easy|Java|[Array]|| +|83|[Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)|Easy|Java|[Array, Subarray]|| +|84|[Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number%20At%20Least%20Twice%20of%20Others.java)|Easy|Java|[Array]|| +|85|[Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Toeplitz%20Matrix.java)|Easy|Java|[Array]|| +|86|[Sum of Two Integers.java](https://github.com/awangdev/LintCode/blob/master/Java/Sum%20of%20Two%20Integers.java)|Easy|Java|[Bit Manipulation]|| +|87|[Swap Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Bits.java)|Easy|Java|[Bit Manipulation]|| +|88|[Update Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Update%20Bits.java)|Medium|Java|[Bit Manipulation]|| +|89|[Maximum XOR of Two Numbers in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20XOR%20of%20Two%20Numbers%20in%20an%20Array.java)|Medium|Java|[Bit Manipulation, Trie]|| +|90|[Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)|Medium|Java|[BFS, DP, Math, Partition DP]|| +|91|[Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)|Medium|Java|[Backpack DP, DP]|| +|92|[Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)|Hard|Java|[Binary Search, DP, Partition DP]|| +|93|[Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Perfect%20Square.java)|Review|Java|[Binary Search, Math]|| +|94|[Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)|Easy|Java|[Binary Search, Hash Table, Sort, Two Pointers]|| +|95|[Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)|Hard|Java|[DP, Interval DP, String]|| +|96|[Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)|Medium|Java|[BST, Design, Stack, Tree]|| +|97|[Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Nested%20List%20Iterator.java)|Medium|Java|[Design, Stack]|| +|98|[Best Time to Buy and Sell Stock with Cooldown.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Cooldown.java)|Medium|Java|[DP]|| +|99|[Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)|Medium|Java|[Array, Binary Search]|| +|100|[Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)|Medium|Java|[DP, Double Sequence DP, Sequence DP]|| +|101|[Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)|Hard|Java|[DP, String]|| +|102|[Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)|Medium|Java|[Backtracking, String]|| +|103|[Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)|Hard|Java|[DP, Double Sequence DP, Sequence DP, String]|| +|104|[Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)|Hard|Java|[DP, String]|| +|105|[Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)|Easy|Java|[Array, Bit Manipulation, Divide and Conquer]|| +|106|[Ones and Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Ones%20and%20Zeroes.java)|Hard|Java|[DP]|| +|107|[Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)|Medium|Java|[Binary Search, Math]|| +|108|[Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)|Hard|Java|[Backtracking, DFS, DP, Hash Table, Memoization]|| +|109|[Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Nested%20List%20Weight%20Sum.java)|Easy|Java|[BFS, DFS]|| +|110|[Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Same%20Tree.java)|Easy|Java|[DFS, Tree]|| +|111|[Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)|Easy|Java|[DFS, Divide and Conquer, Tree]|| +|112|[Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Hash Table, Tree]|| +|113|[Add Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Digits.java)|Easy|Java|[Math]|| +|114|[Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers.java)|Medium|Java|[Linked List, Math]|| +|115|[Add Two Numbers II.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers%20II.java)|Medium|Java|[Linked List]|| +|116|[Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Balanced%20Binary%20Tree.java)|Medium|Java|[DFS, Tree]|| +|117|[Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Anagram.java)|Easy|Java|[Hash Table, Sort]|| +|118|[Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|119|[Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Divide and Conquer, Tree]|| +|120|[Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List]|| +|121|[Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Binary%20Tree%20to%20Linked%20List.java)|Medium|Java|[Binary Tree, DFS]|| +|122|[Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)|Easy|Java|[Backtracking, Binary Tree, DFS]|| +|123|[Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)|Medium|Java|[Array, Binary Search, Subarray, Two Pointers]|| +|124|[Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)|Medium|Java|[Hash Table, String, Two Pointers]|| +|125|[Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)|Hard|Java|[Hash Table, String, Two Pointers]|| +|126|[Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle.java)|Easy|Java|[Linked List, Two Pointers]|| +|127|[Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Nth%20Node%20From%20End%20of%20List.java)|Medium|Java|[Linked List, Two Pointers]|| +|128|[Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)|Hard|Java|[Hash Table, Sliding Window, String]|| +|129|[Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)|Medium|Java|[Linked List, Math, Two Pointers]|| +|130|[Kth Smallest Element in a Sorted Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20Sorted%20Matrix.java)|Medium|Java|[Binary Search, Heap]|| +|131|[Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)|Medium|Java|[Array, Binary Search]|| +|132|[Find Minimum in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array%20II.java)|Hard|Java|[Array, Binary Search]|| +|133|[Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)|Medium|Java|[Union Find]|| +|134|[Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)|Medium|Java|[Union Find]|| +|135|[Connecting Graph III.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20III.java)|Medium|Java|[Union Find]|| +|136|[Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)|Medium|Java|[BFS, DFS, Matrix DFS, Union Find]|| +|137|[Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands%20II.java)|Hard|Java|[Union Find]|| +|138|[Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)|Medium|Java|[BFS, DFS, Matrix DFS, Union Find]|| +|139|[Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Trie%20(Prefix%20Tree).java)|Medium|Java|[Design, Trie]|| +|140|[Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)|Medium|Java|[Backtracking, Design, Trie]|| +|141|[Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)|Hard|Java|[Backtracking, DFS, Trie]|| +|142|[Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)|Medium|Java|[Array, Backtracking, DFS]|| +|143|[Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)|Hard|Java|[Backtracking, Trie]|| +|144|[Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)|Hard|Java|[Array, Stack, Two Pointers]|| +|145|[Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)|Easy|Java|[Design, Stack]|| +|146|[Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)|Easy|Java|[Design, Stack]|| +|147|[Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)|Medium|Java|[DFS, Divide and Conquer, Stack]|| +|148|[Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)|Hard|Java|[Array, Monotonous Stack, Stack]|| +|149|[Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)|Medium|Java|[Stack, Tree]|| +|150|[Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Integer.java)|Easy|Java|[Math]|| +|151|[Swap Nodes in Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Nodes%20in%20Pairs.java)|Medium|Java|[Linked List]|| +|152|[Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)|Hard|Java|[Binary Search, DFS, Divide and Conquer]|| +|153|[Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/Sqrt(x).java)|Easy|Java|[Binary Search, Math]|| +|154|[First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Bad%20Version.java)|Easy|Java|[Binary Search]|| +|155|[Wood Cut.java](https://github.com/awangdev/LintCode/blob/master/Java/Wood%20Cut.java)|Medium|Java|[Binary Search]|| +|156|[Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|157|[Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)|Hard|Java|[Hash Table, String, Trie]|| +|158|[Game of Life.java](https://github.com/awangdev/LintCode/blob/master/Java/Game%20of%20Life.java)|Medium|Java|[Array]|| +|159|[Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)|Review|Java|[Array, Binary Search, PreSum]|| +|160|[Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)|Easy|Java|[PriorityQueue, Sort, Sweep Line]|| +|161|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|Java|[Array, Interval, PriorityQueue, Sort, Sweep Line]|| +|162|[Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)|Medium|Java|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|| +|163|[The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)|Review|Java|[Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line]|| +|164|[Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|165|[Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)|Hard|Java|[Array, DP, Hash Table, Stack]|| +|166|[Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Square.java)|Medium|Java|[Coordinate DP, DP]|| +|167|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|Java|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|| +|168|[Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)|Medium|Java|[DP, Game Theory, Greedy]|| +|169|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|Java|[Array, DP, Game Theory, Memoization, MiniMax]|| +|170|[Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)|Easy|Java|[Hash Table, Stack, Tree]|| +|171|[Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)|Medium|Java|[Stack, Tree, Two Stacks]|| +|172|[Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Change%20to%20Anagram.java)|Easy|Java|[String]|| +|173|[Classical Binary Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Classical%20Binary%20Search.java)|Easy|Java|[Binary Search]|| +|174|[Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)|Easy|Java|[DP, Memoization, Sequence DP]|| +|175|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|Java|[Array, DP, Game Theory, Interval DP, Memoization]|| +|176|[Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)|Easy|Java|[BST, Binary Search, Tree]|| +|177|[Compare Version Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Version%20Numbers.java)|Medium|Java|[String]|| +|178|[Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Complete%20Tree%20Nodes.java)|Medium|Java|[Binary Search, Tree]|| +|179|[Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)|Medium|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|180|[Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)|Medium|Java|[BFS, DFS, Graph, Topological Sort]|| +|181|[Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)|Easy|Java|[BFS, DFS, Stack, Tree]|| +|182|[Closest Number in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Number%20in%20Sorted%20Array.java)|Easy|Java|[Binary Search]|| +|183|[Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)|Easy|Java|[BFS, Tree]|| +|184|[Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Strings.java)|Easy|Java|[String]|| +|185|[Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate.java)|Easy|Java|[Array, Hash Table]|| +|186|[Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20II.java)|Easy|Java|[Array, Hash Table]|| +|187|[Contains Duplicate III.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20III.java)|Medium|Java|[BST]|| +|188|[Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)|Hard|Java|[DP, Divide and Conquer, Interval DP, Memoization]|| +|189|[Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)|Easy|Java|[Brainteaser, DP, Game Theory]|| +|190|[Convert Integer A to Integer B.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Integer%20A%20to%20Integer%20B.java)|Easy|Java|[Bit Manipulation]|| +|191|[Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)|Easy|Java|[Basic Implementation]|| +|192|[Count 1 in Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%201%20in%20Binary.java)|Easy|Java|[Bit Manipulation]|| +|193|[Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)|Easy|Java|[Basic Implementation, String]|| +|194|[K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)|Hard|Java|[DP, Double Sequence DP, Sequence DP, Trie]|| +|195|[Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)|Medium|Java|[Array, DP, Greedy]|| +|196|[Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change%202.java)|Medium|Java|[Backpack DP, DP]|| +|197|[Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)|Easy|Java|[DP, Sequence DP, Status DP]|| +|198|[Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)|Medium|Java|[DP, Partition DP, String]|| +|199|[Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)|Easy|Java|[Array, Coordinate DP, DP]|| +|200|[Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|201|[Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)|Medium|Java|[Bit Manipulation, Bitwise DP, DP]|| +|202|[Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)|Medium|Java|[Coordinate DP, DP, Math, Subarray]|| +|203|[House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber.java)|Easy|Java|[DP, Sequence DP]|| +|204|[House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)|Medium|Java|[DP, Sequence DP, Status DP]|| +|205|[House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)|Medium|Java|[DFS, DP, Status DP, Tree]|| +|206|[Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)|Hard|Java|[DP, Sequence DP, Status DP]|| +|207|[Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)|Hard|Java|[Array, DP, Sequence DP]|| +|208|[Best Time to Buy and Sell Stock IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20IV.java)|Hard|Java|[DP, Sequence DP]|| +|209|[Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)|Hard|Java|[Binary Search, Coordinate DP, DP]|| +|210|[Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20in%20String.java)|Medium|Java|[Two Pointers]|| +|211|[Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations%20II.java)|Medium|Java|[Backtracking]|| +|212|[Shuffle an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Shuffle%20an%20Array.java)|Medium|Java|[Permutation]|| +|213|[Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Anagrams%20in%20a%20String.java)|Easy|Java|[Hash Table, Sliding Window]|| +|214|[Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Anagrams.java)|Medium|Java|[Hash Table, String]|| +|215|[Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)|Medium|Java|[Backpack DP, DP]|| +|216|[Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)|Medium|Java|[Backpack DP, DP]|| +|217|[Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)|Medium|Java|[Backpack DP, DP]|| +|218|[Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Primes.java)|Easy|Java|[Hash Table, Math]|| +|219|[Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Node%20in%20a%20Linked%20List.java)|Easy|Java|[Linked List]|| +|220|[Excel Sheet Column Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Number.java)|Easy|Java|[Math]|| +|221|[Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Title.java)|Easy|Java|[Math]|| +|222|[Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game.java)|Easy|Java|[String]|| +|223|[Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)|Hard|Java|[Binary Tree, Expression Tree, Minimum Binary Tree, Stack]|| +|224|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|| +|225|[Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Stack]|| +|226|[Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Stack]|| +|227|[Evaluate Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Reverse%20Polish%20Notation.java)|Medium|Java|[Stack]|| +|228|[Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)|Hard|Java|[DP, Enumeration, Partition DP]|| +|229|[Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)|Hard|Java|[DP, Partition DP]|| +|230|[Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)|Hard|Java|[Backpack DP, DP]|| +|231|[First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Missing%20Positive.java)|Hard|Java|[Array]|| +|232|[Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20strStr().java)|Easy|Java|[String, Two Pointers]|| +|233|[Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)|Medium|Java|[Linked List, Sort]|| +|234|[Interleaving Positive and Negative Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20Positive%20and%20Negative%20Numbers.java)|Medium|Java|[Two Pointers]|| +|235|[Largest Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number.java)|Medium|Java|[Sort]|| +|236|[Last Position of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Last%20Position%20of%20Target.java)|Easy|Java|[Binary Search]|| +|237|[Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/Length%20of%20Last%20Word.java)|Easy|Java|[String]|| +|238|[Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)|Medium|Java|[DP, Double Sequence DP, Sequence DP, String]|| +|239|[Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)|Easy|Java|[Array, Coordinate DP, DP]|| +|240|[Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)|Medium|Java|[Array, Coordinate DP, DP, Memoization]|| +|241|[N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens.java)|Hard|Java|[Backtracking]|| +|242|[N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens%20II.java)|Hard|Java|[Backtracking]|| +|243|[Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)|Easy|Java|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|| +|244|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|Java|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|| +|245|[Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)|Easy|Java|[Array, Quick Select, Quick Sort]|| +|246|[Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Middle%20of%20Linked%20List.java)|Easy|Java|[Linked List]|| +|247|[Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)|Easy|Java|[Design]|| +|248|[Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Linked%20List%20Elements.java)|Easy|Java|[Linked List]|| +|249|[Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)|Easy|Java|[DP, Math, Memoization]|| +|250|[Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Linked%20List.java)|Easy|Java|[Linked List, Two Pointers]|| +|251|[Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List.java)|Easy|Java|[Linked List]|| +|252|[Reverse Linked List II .java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List%20II%20.java)|Medium|Java|[Linked List]|| +|253|[Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Linked%20Lists.java)|Easy|Java|[Linked List]|| +|254|[Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation.java)|Easy|Java|[Hash Table]|| +|255|[Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome.java)|Easy|Java|[String, Two Pointers]|| +|256|[Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)|Easy|Java|[Design, Stack]|| +|257|[Implement Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack.java)|Easy|Java|[Stack]|| +|258|[Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|259|[Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Depth%20of%20Binary%20Tree.java)|Easy|Java|[DFS, Tree]|| +|260|[Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|261|[Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|262|[Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)|Easy|Java|[DFS, Tree]|| +|263|[Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)|Easy|Java|[DFS, Tree]|| +|264|[Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)|Easy|Java|[DFS, Tree]|| +|265|[Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)|Medium|Java|[DFS, Tree]|| +|266|[Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20II.java)|Easy|Java|[Hash Table, Tree]|| +|267|[Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Tree]|| +|268|[Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/Hash%20Function.java)|Easy|Java|[Hash Table]|| +|269|[Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Sorted%20Lists.java)|Easy|Java|[Linked List]|| +|270|[Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)|Easy|Java|[Array, Bit Manipulation, Math]|| +|271|[LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)|Hard|Java|[Design, Hash Table, Linked List]|| +|272|[Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array.java)|Easy|Java|[Array, Two Pointers]|| +|273|[Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array%20II.java)|Medium|Java|[Array, Two Pointers]|| +|274|[Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List.java)|Easy|Java|[Linked List]|| +|275|[Remove Duplicates from Sorted List II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List%20II.java)|Medium|Java|[Linked List]|| +|276|[QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)|Medium|Java|[Quick Sort, Sort]|| +|277|[MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/MergeSort.java)|Medium|Java|[Merge Sort, Sort]|| +|278|[Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Word%20in%20Dictionary.java)|Easy|Java|[Hash Table, Trie]|| +|279|[Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)|Medium|Java|[BFS, DFS, Tree]|| +|280|[Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)|Medium|Java|[BFS, Tree]|| +|281|[Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)|Medium|Java|[DFS, Divide and Conquer, Double Recursive, Tree]|| +|282|[Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)|Hard|Java|[DFS, DP, Tree, Tree DP]|| +|283|[Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum.java)|Easy|Java|[DFS, Tree]|| +|284|[Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)|Easy|Java|[Backtracking, DFS, Tree]|| +|285|[Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)|Easy|Java|[DFS, Double Recursive, Tree]|| +|286|[Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20String.java)|Easy|Java|[String]|| +|287|[Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)|Medium|Java|[Backtracking, Combination, DFS]|| +|288|[Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)|Medium|Java|[Array, Backpack DP, DP]|| +|289|[Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)|Medium|Java|[BFS, DFS, Tree]|| +|290|[Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)|Medium|Java|[DFS, Tree]|| +|291|[Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)|Medium|Java|[Linked List, Two Pointers]|| +|292|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|Java|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|| +|293|[Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)|Hard|Java|[Array, Hash Table, Union Find]|| +|294|[Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|295|[Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|296|[Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)|Medium|Java|[Basic Implementation, Enumeration, String]|| +|297|[Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|Java|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|| +|298|[Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)|Medium|Java|[Array, Quick Sort, Sort, Two Pointers]|| +|299|[Word Ladder.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder.java)|Medium|Java|[BFS]|| +|300|[Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)|Medium|Java|[Design, Hash Table]|| +|301|[Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)|Medium|Java|[BST, DP, Divide and Conquer, Tree]|| +|302|[Ugly Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number.java)|Medium|Java|[Math]|| +|303|[Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|| +|304|[Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)|Medium|Java|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|| +|305|[Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)|Medium|Java|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|| +|306|[Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|307|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|308|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|309|[Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|Java|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|| +|310|[ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)|Medium|Java|[Design, Hash Table]|| +|311|[Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)|Medium|Java|[Array, Two Pointers]|| +|312|[Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20List%20with%20Random%20Pointer.java)|Medium|Java|[Hash Table, Linked List]|| +|313|[Encode and Decode Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20Strings.java)|Medium|Java|[String]|| +|314|[Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)|Medium|Java|[DFS, Divide and Conquer]|| +|315|[Find the Connected Component in the Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Connected%20Component%20in%20the%20Undirected%20Graph.java)|Medium|Java|[BFS, DFS]|| +|316|[HashWithCustomizedClass(LinkedList).java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithCustomizedClass(LinkedList).java)|Medium|Java|[Hash Table]|| +|317|[Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)|Medium|Java|[Binary Search, Divide and Conquer, Lint, Segment Tree]|| +|318|[Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)|Medium|Java|[Binary Search, Lint, Segment Tree]|| +|319|[Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)|Medium|Java|[BST, DFS, Stack, Tree]|| +|320|[Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Prefix.java)|Easy|Java|[String]|| +|321|[Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element%20II.java)|Medium|Java|[Array]|| +|322|[Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)|Medium|Java|[Linked List, Two Pointers]|| +|323|[Peeking Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Peeking%20Iterator.java)|Medium|Java|[Design]|| +|324|[Rehashing.java](https://github.com/awangdev/LintCode/blob/master/Java/Rehashing.java)|Medium|Java|[Hash Table]|| +|325|[Reorder List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reorder%20List.java)|Medium|Java|[Linked List]|| +|326|[Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)|Medium|Java|[Backtracking, DFS, String]|| +|327|[Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String.java)|Medium|Java|[String]|| +|328|[Reverse Words in a String II.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20II.java)|Medium|Java|[String]|| +|329|[Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20III.java)|Easy|Java|[String]|| +|330|[Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)|Medium|Java|[Array, Binary Search]|| +|331|[Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)|Medium|Java|[Binary Search, Divide and Conquer]|| +|332|[Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)|Medium|Java|[Array, Binary Search]|| +|333|[Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)|Medium|Java|[BST, Binary Tree]|| +|334|[Merge Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array%20II.java)|Easy|Java|[Array]|| +|335|[Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/Nth%20to%20Last%20Node%20in%20List.java)|Easy|Java|[Linked List]|| +|336|[Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)|Medium|Java|[Divide and Conquer, Linked List, Merge Sort, Sort]|| +|337|[Summary Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Summary%20Ranges.java)|Medium|Java|[Array]|| +|338|[Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)|Medium|Java|[BFS, DFS, Topological Sort]|| +|339|[Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)|Hard|Java|[Greedy, Hash Table, Stack]|| +|340|[Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)|Medium|Java|[Array, Enumeration]|| +|341|[Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)|Hard|Java|[Backtracking, DFS, Divide and Conquer, String]|| +|342|[Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)|Hard|Java|[Array, PriorityQueue, Sort]|| +|343|[Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)|Hard|Java|[KMP, String]|| +|344|[Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum.java)|Easy|Java|[Array, Hash Table]|| +|345|[K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)|Hard|Java|[Array, BST, TreeSet]|| +|346|[Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)|Hard|Java|[BST, Divide and Conquer, Merge Sort, PreSum]|| +|347|[Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)|Hard|Java|[Array, BST, Binary Search, DP, Queue, TreeSet]|| +|348|[Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)|Hard|Java|[Design, Geometry, Hash Table]|| +|349|[Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Tree]|| +|350|[Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)|Medium|Java|[Backtracking, DFS, Sequence DFS, String]|| +|351|[Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)|Medium|Java|[DFS, Enumeration, Math, Sequence DFS]|| +|352|[Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)|Medium|Java|[Backtracking, DFS, DP]|| +|353|[Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)|Easy|Java|[Array, DFS]|| +|354|[Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)|Hard|Java|[Array, Geometry, Hash Table, Math]|| +|355|[Number of Digit One.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Digit%20One.java)|Hard|Java|[Math]|| +|356|[Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)|Hard|Java|[Bit Manipulation, String]|| +|357|[Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning.java)|Medium|Java|[Backtracking, DFS]|| +|358|[Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)|Hard|Java|[BST, DFS, Tree]|| +|359|[Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)|Easy|Java|[Array, Hash Table, PreSum, Subarray]|| +|360|[Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)|Medium|Java|[Array, Hash Table, PreSum]|| +|361|[Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Substring.java)|Medium|Java|[DP, String]|| +|362|[Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)|Medium|Java|[DFS, DP, Interval DP, Memoization]|| +|363|[Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)|Hard|Java|[Array, Coordinate DP, DP, Greedy]|| +|364|[Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/Gas%20Station.java)|Medium|Java|[Greedy]|| +|365|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|Java|[Array, Coordinate DP, DFS, DP, Memoization]|| +|366|[Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/Range%20Sum%20Query%20-%20Immutable.java)|Easy|Java|[DP, PreSum]|| +|367|[Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)|Hard|Java|[Coordinate DP, Stack, String]|| +|368|[Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)|Review|Java|[BFS, DFS, DP]|| +|369|[Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)|Medium|Java|[Array, PriorityQueue, Sort, Sweep Line]|| +|370|[H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)|Medium|Java|[Bucket Sort, Hash Table, Sort]|| +|371|[H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index%20II.java)|Medium|Java|[Binary Search]|| +|372|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|Java|[Array, Partition, Quick Sort, Sort, Two Pointers]|| +|373|[Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)|Medium|Java|[Partition, Quick Sort, Sort, Two Pointers]|| +|374|[Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)|Medium|Java|[Partition, Sort, String, Two Pointers]|| +|375|[Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)|Medium|Java|[PreSum, PriorityQueue, Sort, Subarray]|| +|376|[Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)|Medium|Java|[Array, Enumeration, Greedy, PriorityQueue, Queue]|| +|377|[Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)|Hard|Java|[Greedy, Hash Table, Heap]|| +|378|[Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/Exam%20Room.java)|Medium|Java|[PriorityQueue, Sort]|| +|379|[Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Anagrams.java)|Medium|Java|[Array, Hash Table]|| +|380|[Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)|Medium|Java|[DFS, Hash Table, Tree]|| +|381|[Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Words.java)|Easy|Java|[Hash Table, String]|| +|382|[Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Characters.java)|Easy|Java|[Array, String]|| +|383|[Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)|Medium|Java|[DP, Math]|| +|384|[Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)|Medium|Java|[DP, String]|| +|385|[Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Multiply%20Strings.java)|Medium|Java|[Math, String]|| +|386|[Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)|Medium|Java|[Array, BFS, Backtracking, Bit Manipulation, DFS]|| +|387|[Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)|Medium|Java|[Array, BFS, Backtracking, DFS]|| +|388|[Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|389|[Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|390|[Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|391|[Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Except%20Self.java)|Medium|Java|[Array, PreProduct]|| +|392|[Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)|Medium|Java|[Bit Manipulation]|| +|393|[Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|394|[Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)|Easy|Java|[Bit Manipulation]|| +|395|[Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)|Medium|Java|[Array, Hash Table, PreSum, Subarray]|| +|396|[Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximize%20Distance%20to%20Closest%20Person.java)|Easy|Java|[Array]|| +|397|[Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Simplify%20Path.java)|Medium|Java|[Stack, String]|| +|398|[Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)|Medium|Java|[Linked List, Stack, Tree]|| +|399|[Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)|Easy|Java|[DP, Sequence DP]|| +|400|[Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)|Medium|Java|[BFS, Stack, Tree]|| +|401|[Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)|Medium|Java|[DP, Hash Table, Sequence DP]|| +|402|[Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)|Easy|Java|[Array, DP, Sequence DP]|| +|403|[Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|404|[Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)|Medium|Java|[Binary Search, Coordinate DP, DP, Memoization]|| +|405|[Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)|Medium|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|406|[Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Random%20Pick%20Index.java)|Medium|Java|[Reservior Sampling]|| +|407|[Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Celebrity.java)|Medium|Java|[Array, Greedy]|| +|408|[Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/Sparse%20Matrix%20Multiplication.java)|Medium|Java|[Hash Table]|| +|409|[Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)|Medium|Java|[Hash Table]|| +|410|[Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/Exclusive%20Time%20of%20Functions.java)|Medium|Java|[Stack]|| +|411|[Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)|Medium|Java|[Array, Math]|| +|412|[Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)|Medium|Java|[DFS, DP]|| +|413|[Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)|Medium|Java|[Hash Table, PreSum, Subarray]|| +|414|[Contiguous Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Contiguous%20Array.java)|Medium|Java|[Hash Table]|| +|415|[Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)|Medium|Java|[Hash Table, Math]|| +|416|[Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)|Medium|Java|[Array, Design, Hash Table]|| +|417|[Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)|Medium|Java|[Coordinate DP, DP]|| +|418|[Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)|Medium|Java|[Coordinate DP, DP, Status DP]|| +|419|[Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)|Medium|Java|[BFS, DFS, Hash Table, Tree]|| +|420|[Populating Next Right Pointers in Each Node II.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node%20II.java)|Medium|Java|[DFS, Tree]|| +|421|[Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array.java)|Medium|Java|[Array, Binary Search]|| +|422|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Subarray]|| +|423|[Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)|Hard|Java|[Enumeration, Math, String]|| +|424|[Find the Weak Connected Component in the Directed Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Weak%20Connected%20Component%20in%20the%20Directed%20Graph.java)|Medium|Java|[Union Find]|| +|425|[Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)|Medium|Java|[DFS, Hash Table, Hash Table, Union Find]|| +|426|[Bricks Falling When Hit.java](https://github.com/awangdev/LintCode/blob/master/Java/Bricks%20Falling%20When%20Hit.java)|Hard|Java|[Union Find]|| +|427|[Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)|Hard|Java|[Binary Search, Lint, Segment Tree]|| +|428|[Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)|Medium|Java|[Binary Search, Lint, Segment Tree]|| +|429|[HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/HashHeap.java)|Hard|Java|[HashHeap, Heap]|| +|430|[My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)|Medium|Java|[Array, TreeMap]|| +|431|[Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)|Medium|Java|[Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree]|| +|432|[Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)|Hard|Java|[BFS, Heap, MinHeap, PriorityQueue]|| +|433|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|Java|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|| +|434|[Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Lists.java)|Medium|Java|[Divide and Conquer, Heap, Linked List, PriorityQueue]|| +|435|[Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)|Medium|Java|[Heap, MinHeap, PriorityQueue]|| +|436|[Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/Heapify.java)|Medium|Java|[Heap, MinHeap]|| +|437|[Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|| +|438|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|Java|[DP, Enumeration, Heap, Math, PriorityQueue]|| +|439|[Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap]|| +|440|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|| +|441|[Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)|Medium|Java|[BST, Tree]|| +|442|[Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)|Easy|Java|[DFS, Divide and Conquer, Tree]|| +|443|[Two Sum IV - Input is a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20IV%20-%20Input%20is%20a%20BST.java)|Easy|Java|[Tree]|| +|444|[Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4.java)|Easy|Java|[Enumeration, String]|| +|445|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|Java|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|| +|446|[Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)|Medium|Java|[BFS, DFS]|| +|447|[Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array.java)|Easy|Java|[Array, Two Pointers]|| +|448|[Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)|Hard|Java|[Enumeration, Math, String]|| +|449|[Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)|Hard|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|450|[Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome%20II.java)|Easy|Java|[String]|| +|451|[Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List, Tree]|| +|452|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|Java|[Array, BFS, Backtracking, DFS, Hash Table, String]|| +|453|[Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)|Easy|Java|[Design, Queue, Sliding Window]|| +|454|[Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Move%20Zeroes.java)|Easy|Java|[Array, Two Pointers]|| +|455|[Flood Fill.java](https://github.com/awangdev/LintCode/blob/master/Java/Flood%20Fill.java)|Easy|Java|[DFS]|| +|456|[Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Diameter%20of%20Binary%20Tree.java)|Easy|Java|[Tree]|| +|457|[Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)|Easy|Java|[Stack, Two Pointers]|| +|458|[Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/Text%20Justification.java)|Hard|Java|[Enumeration, String]|| +|459|[Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)|Hard|Java|[Enumeration, String]|| +|460|[Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)|Hard|Java|[DP, Hash Table]|| +|461|[Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)|Hard|Java|[Hash Table, Sliding Window, String, Two Pointers]|| +|462|[Shortest Distance from All Buildings.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Distance%20from%20All%20Buildings.java)|Hard|Java|[BFS]|| +|463|[String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/String%20to%20Integer%20(atoi).java)|Medium|Java|[Math, String]|| +|464|[Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Roman%20to%20Integer.java)|Easy|Java|[Math, String]|| +|465|[Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)|Easy|Java|[Binary Search, Hash Table, Sort, Two Pointers]|| +|466|[Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)|Easy|Java|[Enumeration, Hash Table, Math]|| +|467|[Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Parentheses.java)|Easy|Java|[Stack, String]|| +|468|[First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Unique%20Character%20in%20a%20String.java)|Easy|Java|[Hash Table, String]|| +|469|[Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)|Easy|Java|[Math, String, Two Pointers]|| +|470|[Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)|Medium|Java|[BFS, DFS, Graph]|| +|471|[Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)|Hard|Java|[Deque, Heap, Sliding Window]|| +|472|[Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)|Hard|Java|[Array, Binary Search, DFS, Divide and Conquer]|| +|473|[Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)|Medium|Java|[Backtracking, DFS, Permutation]|| +|474|[One Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/One%20Edit%20Distance.java)|Medium|Java|[String]|| +|475|[4Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/4Sum.java)|Medium|Java|[Hash Table]|| +|476|[Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)|Hard|Java|[BFS]|| +|477|[Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)|Hard|Java|[BFS, Graph]|| +|478|[Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Isomorphic%20Strings.java)|Easy|Java|[Hash Table]|| +|479|[Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)|Hard|Java|[DFS, Greedy, Math]|| +|480|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|Java|[BFS, DFS, Graph, Tree, Union Find]|| +|481|[Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|482|[Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)|Hard|Java|[DFS, Graph, Tree, Union Find]|| +|483|[The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)|Medium|Java|[BFS, DFS]|| +|484|[The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)|Medium|Java|[BFS, DFS, PriorityQueue]|| +|485|[The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)|Hard|Java|[BFS, DFS, PriorityQueue]|| +|486|[Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)|Medium|Java|[DP, MiniMax]|| +|487|[Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Greater%20Element%20I.java)|Easy|Java|[Hash Table, Stack]|| +|488|[Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)|Medium|Java|[Hash Table, String]|| +|489|[Delete Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Digits.java)|Medium|Java|[Greedy, Priority Queue]|| +|490|[Flatten 2D Vector.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%202D%20Vector.java)|Medium|Java|[Design]|| +|491|[The Spiral Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Spiral%20Matrix%20II.java)|Medium|Java|[Array]|| +|492|[Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|| +|493|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|| +|494|[Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)|Hard|Java|[Backtracking, DFS]|| +|495|[Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)|Hard|Java|[DP]|| diff --git a/ReviewPage.md b/ReviewPage.md new file mode 100644 index 0000000..8a04bdc --- /dev/null +++ b/ReviewPage.md @@ -0,0 +1,9749 @@ +# Review Page + +This page summarize the solutions of all problems. For thoughts,ideas written in English, refer to deach individual solution. +New problems will be automatically updated once added. + +**0. [Count of Smaller Number before itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number%20before%20itself.java)** Level: Hard Tags: [] + +与Count of Smaller Number非常类似。以实际的value来构成segment tree,leaf上存(count of smaller number)。 + +Trick: 先Query,再modify. +每次Query时候,A[i]都还没有加入到Segment Tree 里面,而A[i+1,...etc]自然也还没有加进去。 +那么就自然是coutning smaller number before itself. +刁钻啊! + +另外注意: +在modify里面:多Check了root.start <= index 和 index <= root.end。 过去都忽略了。以后可以把这个也写上。 +(其实是Make sense的,就是更加严格地check了index再 root.left 或者 root.right里面的站位) + + + +--- + +**1. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**2. [Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)** Level: Medium Tags: [Hash Table, Math] + + +TODO: no need of hashMap, just use set to store the existing + +不难想到处理除法:考虑正负,考虑小数点前后。主要是小数点以后的要着重考虑。 +很容易忽略的是integer的益处。 + + +--- + +**3. [Gray Code.java](https://github.com/awangdev/LintCode/blob/master/Java/Gray%20Code.java)** Level: Medium Tags: [Backtracking] + + +TODO: +1. backtracking, using set to perform contains() +2. BFS: use queue to keep the mutations + +题目蛋疼,目前只接受一种结果。 + +BackTracking + DFS: + Recursive helper里每次flip一个 自己/左边/右边. Flip过后还要恢复原样.遍历所有. + +曾用法(未仔细验证): +基本想法就是从一个点开始往一个方向走,每次flip一个bit, 碰壁的时候就回头走。 + + + +--- + +**4. [Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Hamming%20Distance.java)** Level: Easy Tags: [] + +bit: XOR, &, shift>> + + + +--- + +**5. [Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Happy%20Number.java)** Level: Easy Tags: [] + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + + + +--- + +**6. [HashWithArray.java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithArray.java)** Level: Easy Tags: [] + + + + +--- + +**7. [Heaters.java](https://github.com/awangdev/LintCode/blob/master/Java/Heaters.java)** Level: Easy Tags: [] + +第一步: +生题型, 理解题意需要时间: +从字面和画图而言, 就是定住房子一个个过,房子左右的distance需要足够达到heater. 目标是招尽可能小的radius, 所以house和heater紧贴着是必要的. +在for loop里面定下house,把heater当作一个区间移动, 达到的第一个合适区间,这就是当下最小的理想radius,取这个值跟既定radius作比较。 +比较之后,继续移动house,再试着移动heater区间去match。 + +第二步: +Binary Search + +注意! +题目没有说given array是否sort, 我们必须sort才能够区间移动或者binary search. +TODO: +http://www.cnblogs.com/grandyang/p/6181626.html + + + +--- + +**8. [IndexMatch.java](https://github.com/awangdev/LintCode/blob/master/Java/IndexMatch.java)** Level: Easy Tags: [] + +有序, 假设有这样的数字:target. +target 左边的数字,一定不比index大,target右边的数字,一定比index大。 +这样可以binary search.O(logn) + + + +--- + +**9. [Insert Node in a Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Node%20in%20a%20Binary%20Search%20Tree%20.java)** Level: Easy Tags: [BST] + + +往Binary Search Tree里面加东西,一定会找到一个合适的leaf加上去。 + +那么:就是说someNode.left or someNode.right是null时,就是insert node的地方。 + +找到那个someNode就按照正常的Binary Search Tree规律。 + + + +--- + +**10. [Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/Jewels%20and%20Stones.java)** Level: Easy Tags: [Hash Table] + +1524017454 + +给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 + +#### Basic HashSet + + + +--- + +**11. [Kth Smallest Sum In Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Sum%20In%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [] + + +用priority queue. 每次把最小的展开,移位。分别x+1,或者y+1: +因为当下的Min里面x,y都是最小的。所以下一个最小的不是(x+1,y),就是(x,y+1)。 + +每次就poll()一个,放2个新candidate进去就好了。 +注意,这样的做法会用重复,比如例子(7,4)会出现两次。用一个HashSet挡一下。 + +注意,HashSet的唯一性,用一个"x,y"的string就可以代为解决。 + + + +--- + +**12. [LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)** Level: Hard Tags: [Design, Hash Table] + + +#### Hash Table +- 具体看thoughts, 几种不同的方式使用map +- `regular object map`: map of , where `item : {int val; int count}` +- Use a Map to track the frequency +- Track constant capacity, and minimum frequency +- Every get(): update all frequency map as well +- Every put(): update all frequency map as well, with optional removal (if over capacity) + +- Original post: http://www.cnblogs.com/grandyang/p/6258459.html +- TODO: one doubly linked list might be good enough to replace below: +- `frequency list map`: map of >, where the list preserves `recency` +- `item location in frequency map`: map of : +- index relative to the item in a particular list, not tracking which list here + + + +--- + +**13. [Longest Univalue Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Univalue%20Path.java)** Level: Easy Tags: [] + +弄明白path的意义: 连接node的edge. +要找MAX, 可以在class scope里面定义一个max variable. + +用minimum amount of code来概括几种不同的情况: left == root, right == root, or left == root == right. + + + +--- + +**14. [Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)** Level: Medium Tags: [Enumeration, Greedy] + + +#### Array +- 分三份:a b c考虑 +- 若a: countA++; 或b: countB++ +- 或c:countA--, countB-- +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 +- 最后出现的两个count>0的a和b,自然是potentially大于1/3的。其中有一个大于1/3. +- 比较countA和countB哪个大,就return哪一个。 + + + +--- + +**15. [Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)** Level: Medium Tags: [Hash Table, Linked List] + + +TODO: +1. hash table solution not passing +2. Find O(n) solution + +#### Hash Table +- 与其他Majority Number一样。 +- 出现次数多余1/k,就要分成k份count occurance.用HashMap。 存在的+1;不存在map里的,分情况: +- 若map.size() == k,说明candidate都满了,要在map里把所有现存的都-1; +- 若map.size() < k, 说明该加新candidate,那么map.put(xxx, 1); +- 最后在HashMap里找出所留下的occurance最大的那个数。 +- 但这样的worst case是 O(nk) + + + +--- + +**16. [Matrix Zigzag Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Matrix%20Zigzag%20Traversal.java)** Level: Easy Tags: [] + +分析4个step:right, left-bottom,down,right-up +implement时注意index.有点耐心 + + + +--- + +**17. [Maximum Subarray III.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20III.java)** Level: Review Tags: [] + + + +--- + +**18. [Minimum Absolute Difference in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Absolute%20Difference%20in%20BST.java)** Level: Easy Tags: [BST] + + +BST: inorder-traversal: 先left node(adding to stack till left leav), 再process stack.peek (mid node), 再 add rightNode && dive to rightNode.left leaf + + + +--- + +**19. [Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)** Level: Medium Tags: [BFS, Graph] + + +#### Graph + BFS +- Build graph `map` +- BFS to find the shortest path: when the neibhbor has the curr node as the only one neighbor, it is leaf. +- record shortest path in Map> as result +- TODO: code it up. + +#### Previous Solution +- removing leaf && edge + + + +--- + +**20. [Missing Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Ranges.java)** Level: Medium Tags: [Array] + + +#### Basic Implementation +- O(n) +- 两个pointer, 每次计较prev和curr之间的部分. +- 然后prev = curr,向前移动一格 +- TODO: check the edge case and make sure max/min of int are checked + + + +--- + +**21. [Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Permutation.java)** Level: Medium Tags: [Array] + + +需斟酌: why reverse is need? why we are looking for k? + +Permutation的规律: +1. 从小的数字开始变化因为都是从小的数字开始recursive遍历。 +2. 正因为1的规律,所以找大的断点数字要从末尾开始: 确保swap过后的permutation依然是 前缀固定时 当下最小的。 + +steps: +1. 找到最后一个上升点,k +2. 从后往前,找到第一个比k大的点, bigIndex +3. swap k && bigIndex +4. 最后反转 (k+1,end) + + + + +--- + +**22. [O(1) Check Power of 2.java](https://github.com/awangdev/LintCode/blob/master/Java/O(1)%20Check%20Power%20of%202.java)** Level: Easy Tags: [Bit Manipulation] + + + + +--- + +**23. [Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)** Level: Medium Tags: [Backtracking, Permutation] + + +TODO: need to review permutation + +permutation的综合题: +1. validate Input 是不是可以做palindromic permutation. 这个就是(Palindrome Permutation I) +2. 顺便存一下permutation string的前半部分和中间的single character(if any) +3. DFS 做unique permutation: given input有duplicate characters。 + + + +--- + +**24. [Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)** Level: Easy Tags: [Array, Two Pointers] + + +- 更正常的start/end partition pointer类似: when condition meet, swap +- Clean up TODO + + + +--- + +**25. [Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Pascal's%20Triangle%20II.java)** Level: Easy Tags: [] + +简单处理array list. + + + +--- + +**26. [Permutation Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Index.java)** Level: Easy Tags: [] + +和Permutation Sequence相反的题目。思想类似。 + +题目为Easy,琢磨很久,分析: +每个数位的数字,都是跳过了小于这数字开头的多种可能。 + +举例【6,5,2】吧。我们找6,5,2是permudation里面的第几个。 +正常排序,也就是permutation的第一个,应该是【2,5,6】 +如果要从首位,2,变成6,要跨过多少可能性呢? +很简单,就是问:小于6的数字有多少个呢?(2,5).每个数字变成head,都有各自的一套变化,都有(n-1)!种可能。 + +本题做法:每个(n-1)!加起来。 Note:(n-1) means, 开头的数字(2,5)各带出多少种排列,也就是不就是(n-1)!嘛。 + 这一步,计算数量很简单: (有几个小于6的数字) ×(除去head剩下有多少个数字)! + +以上 ,都是为了把6推上皇位,而牺牲的条数。 + +那么把6推上去以后,还有接下去的呢。 + +接下去要看5,2. +6确定,后面permudation可变的情况有可能是【6,5,2】,那还可能是【6,2,5】呢。 + +Same process, 看given 数组的第二位5,算它接下去: +1. 有几个数字小于5呢? +2. 除去5,还有几个数字可以 factorial呢? +3. 一样的。第一步就结果乘以第二步。 + +最后接下去要看最后一个元素2了。 + + +6,5,2全看过了以后,加起来。 +就是【6,5,2】上位,所踏过的所有小命啊! + +我这解释太生动了。因为耗费了好长时间思考... + + + +--- + +**27. [Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)** Level: Medium Tags: [Backtracking, Math] + + +TODO: what about regular DFS/backtracking to compute the kth? dfs(rst, list, candiate list, k) + +k是permutation的一个数位。而permutation是有规律的。 + +也就是说,可以根据k的大小来判断每一个数位的字符(从最大数位开始,因为默认factorio从最大数位开始变化)。 + +于是先求出n!, 然后 k/n!就可以推算出当下这一个数位的字符。然后分别把factorio 和 k减小。 + +另外, 用一个boolean[] visited来确保每个数字只出现一次。 + +这个方法比计算出每个permutation要efficient许多。 + + + + +--- + +**28. [Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Prefix%20and%20Suffix%20Search.java)** Level: Hard Tags: [Trie] + + + + +--- + +**29. [Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array] + + + + + +--- + +**30. [Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array] + + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**31. [Remove Duplicates from Unsorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Unsorted%20List.java)** Level: Medium Tags: [Linked List] + + +基本方法: O(n) sapce, time +遍历。 +遇到duplicate(可能多个), while直到node.next不是duplicate. +接下去,既然不是duplicate,那就add 进 set + + +如果不用extra memory, do it in place: +那就要sort linked list. 用merge sort. + +复习merge sort: +1. find middle. +2. recursively: right = sort(mid.next); left = sort(head). +3. within sort(), at the end call merge(left, right) + + +--- + +**32. [Remove Node in Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Node%20in%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST] + + +方法1: Brutle一点。找到target和target的parent. +把target remove时,把target的children nodes 重新排列组成新的BST: inorder traversal, build tree based on inorder traversal list. + +方法2: 分析规律,先找到target和parent, 然后根据性质,把target remove时,移动children nodes, 保证还是BST。 + + + +--- + +**33. [Reshape the Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Reshape%20the%20Matrix.java)** Level: Easy Tags: [] + +读例子理解题意. +理清counter case. Basic implementation + + + +--- + +**34. [Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)** Level: Easy Tags: [] + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + + + +--- + +**35. [Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)** Level: Medium Tags: [Array, Enumeration] + + +#### 找公式规律 +- 找到个转角度的规律公式: r = c; c = (w - r) +- 用temp variable, swap in place. + + + +--- + +**36. [Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +Allow duplicates之后: +因为最终binary search的结果也是O(n) +所以这道题要记得: 既然是O(n), 那来个简单的for loop 也就好了。 + +当然,要跟面试官提起来原因。别一上来就只有for。。。 + + +--- + +**37. [Search Insert Position.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Insert%20Position.java)** Level: Easy Tags: [] + +一般的binary search. +在结尾判断该return 哪个position。 + + +--- + +**38. [Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Word%20Distance.java)** Level: Easy Tags: [] + +找short distance, wordB可以在wordA的前后;而同一时间,只需要计算一个最近的up to date的distance。 +greedy不断变更A/B index再做比较即可。 + + + +--- + +**39. [Single Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20II.java)** Level: Medium Tags: [Bit Manipulation] + + +一串数字里面, 所有数字都重复三次, 除了一个数字. 找到这个数字, linear time, without extrace space (constant space) + +TODO: bits + + + +--- + +**40. [Single Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20III.java)** Level: Medium Tags: [Bit Manipulation] + + +TODO: wut? + + +--- + +**41. [Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number.java)** Level: Easy Tags: [] + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + + + +--- + +**42. [Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)** Level: Medium Tags: [String] + + + + +--- + +**43. [Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)** Level: Medium Tags: [DP] + + +这个DP有点诡异. 需要斟酌。 +NOT DONE YET + + +--- + +**44. [String Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/String%20Permutation.java)** Level: Easy Tags: [] + +把#of occurrences 存进HashMap, 第一个string 做加法,第二个string做减法。最后看是否有不等于0的作判断。 + + + +--- + +**45. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + + +--- + +**46. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + + +--- + +**47. [Total Occurrence of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Occurrence%20of%20Target.java)** Level: Medium Tags: [] + +想法很简单。写起来有点长。 +找total number of occurance. 首先找first occurance, 再找last occurance. + + + +--- + +**48. [Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)** Level: Easy Tags: [Math] + + + + +--- + +**49. [Two Lists Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Lists%20Sum.java)** Level: Medium Tags: [Linked List] + + +给两个Linked list, sum up and 合成新的list + + + +--- + +**50. [Two Strings Are Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Strings%20Are%20Anagrams.java)** Level: Easy Tags: [] + +方法1:char ascii 用count[256] +坑:不要想象这个是个26letter lowercase. may not be true. + +方法2: 若是其他字符encoding, 而不只是utf16-encoding (java char)? +那么就继续用string去做 + + + +--- + +**51. [Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited value. +- 在nest for loop里面validate row,col,and block. +- validate block要利用i 和 j 增长的规律。 +- 说白了,i && j是按照0~n增长的index, 具体怎么用是可以flexible的。这个方法在同一个nest for loop解决所有运算。 +- `int c = 3 * (i % 3) + j % 3;` //make use of how i and j increases +- `int r = 3 * (i / 3) + j / 3;` + +#### A bit Slower approach +- 单独做block validation: validate block的时候虽然看到了4层for. 其实也就是n^2 +- 可能代码稍微复杂一点 + + + +--- + +**52. [Word Pattern.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Pattern.java)** Level: Easy Tags: [] + +每个char代表一个pattern。用HashMap. +但不够,如果a也match dog, b也match dog, 纠错了。比如pattern = "abba", str = "dog dog dog dog"。 +因此第二个HashMap 反过来。 +确保pattern和str一一对应。 + + + +--- + +**53. [Zigzag Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Zigzag%20Iterator.java)** Level: Medium Tags: [BST] + + +这个题目相对简单. 做的时候我先考虑起来k条怎么办. 那么用个map把index和每个listmark一下就好了。 +每次next(), 相应的list的头拿下来就好。 +然后就跑圈呗,每次刷一个list头。不难。只要把几个variable维护清楚就行。 + + +--- + +**54. [Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Anagram%20Mappings.java)** Level: Easy Tags: [Hash Table] + + +比较简单, 用HashMap 存index list. 最后再遍历一遍数组A, 列举出所有元素. +O(n) + + + +--- + +**55. [Judge Route Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/Judge%20Route%20Circle.java)** Level: Easy Tags: [String] + + +简单的character checking. 各个方向, 加加减减. + + + +--- + +**56. [Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/Island%20Perimeter.java)** Level: Easy Tags: [Hash Table] + + +#### Brutle +- 每个格子4个墙;每个shared的墙要-2 (墙是两面, -1 * 2) +- 最后合计结果就好. + +#### Hash Table +- 不必想太多用HashMap做.但是也可以思考一下: +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么久可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不打,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + + + +--- + +**57. [Power of Three.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Three.java)** Level: Easy Tags: [Math] + + +方法1: +Power of 3: 3 ^ x == n ? +意思是 n / 3 一直除, 最后是可以等于1的, 那么就有了 n/=3, check n%3, 最后看结果是不是整除到1的做法. 用while loop. + +方法2: +如果n是power of 3, 那么 3 ^ x的这个 x一定是个比n小的数字. 那么可以在 0 ~ n 之间做binary serach, 但是就比较慢. + +方法3: +巧妙的想法.最大的3^x integer是 3^19. 那么找到这个数, 一定可以被n整除. 一步到位. + + + +--- + +**58. [Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)** Level: Easy Tags: [Array, Math] + + +简单的实现, 加1, 进位. 唯一取巧的地方, 最后如果要多一位, 一定是10000...这个模式, 可以走捷径, 直接来个+1size的array, 然后第一位=1. +注意,转换成long也不合理,用太多memory. + + +--- + +**59. [Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)** Level: Easy Tags: [Bit Manipulation, Math] + + +跟powerOfThree一样: 可以loop, check mod; 也可以用binary search找合适的数字. + + + +--- + +**60. [Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i target,那么其中所有num[left++]的加上num[right]都>target. +也就是,num[right]不动,计算加入挪动left能有多少组,那就是: right-left这么多。 全部加到count上去。 +然后right--.换个right去和前面的left部分作比较。 + + + +--- + +**67. [Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + +#### DP +- 找对方程dp[x], 积累到amount x最少用多少个coin: #coin是value, index是 [0~x]. +- 子问题的关系是: 如果用了一个coin, 那么就应该是f[x - coinValue]那个位置的#coins + 1 + +##### initialization +- 处理边界, 一开始0index的时候, 用value0. +- 中间利用Integer.MAX_VALUE来作比较, initialize dp[x] +- 注意, 一旦 Integer.MAX_VALUE + 1 就会变成负数. 这种情况会在coin=0的时候发生. + +##### Optimization +- 方法1: 直接用Integer.MAX_VALUE +- 方法2: 用-1, 稍微简洁一点, 每次比较dp[i]和 dp[i - coin] + 1, 然后save. 不必要做多次min比较. + +#### Memoization +- dp[i] 依然表示: min # of coints to make amount i +- initialize dp[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录dp[amount] 如果已经给过value, 不要重复计算, 直接return. +- 但是这道题没必要强行做memoization, 普通DP的状态和方程相对来说很好找到 + + + +--- + +**68. [Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### DP +- 求最值, 想到DP. Time/Space O (n) +- 两个特别处: +- 1. 正负数情况, 需要用两个DP array. +- 2. continuous prodct 这个条件决定了在Math.min, Math.max的时候, +- 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. +- 这也就注定了需要一个global variable 来hold result. + +#### Space optimization, rolling array +- maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins +- Time: O(n), space: O(1) + + + +--- + +**69. [3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/3%20Sum%20Closest.java)** Level: Medium Tags: [Array, Two Pointers] + + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**70. [Triangle Count.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangle%20Count.java)** Level: Medium Tags: [Array] + + +其实也就是3sum的变形, 或者而说2sum的变形. 主要用2 pointers来做. +注意, 在选index时候每次定好一个 [0 ~ i], 在这里面找点start, end, 然后i 来组成triangle. +Note巧妙点: +在此之中, 如果一种start/end/i 符合, 那么从这个[start~end]中, 大于start的都可以, 所以我们count+= end-start. +反而言之, nums[i] + + + +--- + +**71. [3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum.java)** Level: Medium Tags: [Array, Two Pointers] + + + +#### sort array, for loop + two pointer. O(n^2) +- 处理duplicate wthin triplets: +- 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. +- 如果是triplet内有重复, 用完start point, 移动到结尾. + +Previous notes: +注意: + 1. 找 value triplets, 多个结果。注意,并非找index。 + 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 + +步骤: + 1. For loop 挑个数字A. + 2. 2Sum 出一堆2个数字的结果 + 3. Cross match 步骤1里面的A. + +时间 O(n^2), 两个nested loop + +另外, 还是可以用HashMap来做2Sum。稍微短点。还是要注意handle duplicates. + + + + +--- + +**72. [k Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/k%20Sum.java)** Level: Hard Tags: [DP] + + +DP. 公式如何想到, 还需要重新理解. + +dp[i][j][m]: # of possibilities such that from j elements, pick m elements and sum up to i. +i: [0~target] + +dp[i][j][m] = dp[i][j-1][m] + dp[i - A[j - 1]][j-1][m-1] + (i not included) (i included) + + + +--- + +**73. [Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DP, Tree] + + +Not quite clear. +根据左右分割而总结出了原理, 每次分割, 左右两边都会有一定数量的permutation, 总体上的情况数量当然是相乘. +然后每一个不同的分割点都加一遍: +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +然后把数学公式转换成DP的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**74. [Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, Tree] + + +方法1: +适合复习BST. 用DFS对待每个node. 注意BST的特征: 所有left nodes都小于当下node, 所有right nodes都大于当下node. + +根据题意用[L,R]切割.如果node.valtarget, 又因为j不能后退,只能k--,那么问题就被锁定了. 这样可以做到O(n2) + + + +--- + +**78. [Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/Array%20Partition%20I.java)** Level: Easy Tags: [Array] + + +给串数字, size=2n, 找pairs, 然后需要sum of min(pair) 最大. + +(a1, b1), (a2, b2), ..., (an, bn) which makes sum of min(ai, bi) for all i from 1 to n as large as possible. + +#### Sort, basics +- 从结果出发, 只需要找到加法的结果,而不强调具体配对. +- 写一写example就能做 +- 找到排列取单数位的规律,再考虑负数和正数的相同规律,即可找到排列求解的方法。 +- sort, O(nlogn) + + + + +--- + +**79. [1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/1-bit%20and%202-bit%20Characters.java)** Level: Easy Tags: [Array] + + +方法1: +Greedy. +从第一个bit开始数: 如果遇到1, 一定是跳两位;如果遇到0, 一定是跳一位. +loop to end, and see if index reaches the end. + +方法2: +用DP硬做了一下: +1. 如果i位是0, 那么前面dp[i-1]或者dp[i-2] true就够了. +2. 如果i位是1, 那么i-1位必须是1才满足规则, 并且 dp[i-2]需要true. + + + +--- + +**80. [Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Non-decreasing%20Array.java)** Level: Easy Tags: [Array] + + +比较升序的时候, 必须要估计到 i-1, i, i+1三个数位. +写出来i-1, i+1之间的关系, 然后做合理的fix. + +需要真的fix数组, 因为loop through做比较时会用到fix后的数字. + + + +--- + +**81. [Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Consecutive%20Ones.java)** Level: Easy Tags: [Array] + + +Basic. Math.max track结果。 +记得在有对外操作的loop后,一定要把result object清理干净。 + + + +--- + +**82. [Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)** Level: Easy Tags: [Array] + + +方法1: +换到正确的位置。 +需要小心handle i, 因为不知道换到nums[i]上的是什么,所以必须原地清理干净 方能move on. + +方法2: +做标记! +很巧妙地运用了标记的方法, 标记成负数,证明visit过。 +Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! + +方法3: +做标记! +跟方法2类似,也是做标记,这一次是加上一个大于n的数(因为题目给了n的border),最后check一下就好。为不超Integer.MAX_VALUE, 每次加n前,取余数。 + +做标记的方法固然快,但是相对来说比较hacky,在常规的代码中,估计不会用到. + + + + +--- + +**83. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + +time: O(n) +space: O(1) + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + + + +--- + +**84. [Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number%20At%20Least%20Twice%20of%20Others.java)** Level: Easy Tags: [Array] + + +找最大值, 和第二大的值, 看是否符合题意, 就行了. +分析题意, 最简单方法, 可以loop 两遍: 找最值; 作比较. +但其实, 举个反例: 有一个不满足, 就够反对这个'at least twice of alllll others'. + + + +--- + +**85. [Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Toeplitz%20Matrix.java)** Level: Easy Tags: [Array] + + +似乎没什么算法特点, 就是array基本运算, 然后分割成一个helper function去做重复计算, 剪短代码. +注意check MxN 的分界线. + + + +--- + +**86. [Sum of Two Integers.java](https://github.com/awangdev/LintCode/blob/master/Java/Sum%20of%20Two%20Integers.java)** Level: Easy Tags: [Bit Manipulation] + + +a^b 是: 不完全加法. +a&b 是: 所有可能的进位. a&b<<1是向左边进位的形态. + +Goal: 先a^b裸加, 算出进位; 再把结果和进位裸加, 再算出这一轮的进位; 再..裸价, 算进位....直到进位数==0. + +那么就,首先记录好进位的数字:carry. 然后 a^b 不完全加法一次。然后b用来放剩下的carry, 每次移动一位,继续加,知道b循环为0为止。 + +在第一回 a ^ b 之后, b 的本身意义就消失. 接下去应该给parameter重新命名. +sum = a ^ b; // sum without adding carries +nextCarry = (a & b) << 1; + +用其他variable name 取代 a, b 会更好理解一点. + +Bit Operation +Steps: + a & b: 每bit可能出现的进位数 + a ^ b: 每bit在此次操作可能留下的值,XOR 操作 + 每次左移余数1位,然后存到b, 再去跟a做第一步。loop until b == 0 + +(http://www.meetqun.com/thread-6580-1-1.html) + + + +--- + +**87. [Swap Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Bits.java)** Level: Easy Tags: [Bit Manipulation] + + +简单, 但是很多知识点: +1. Hex 0xaaaaaaaa 是1010101....1010; 0x55555555 是01010101....0101 +2. 可以用这两个hex取单数和负数. 如果需要取其他的pattern, 也可以做. +3. x很可能是negative number, 所以right-shift 要用logic shift, >>> 避免leading负数补位. + + + +--- + +**88. [Update Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Update%20Bits.java)** Level: Medium Tags: [Bit Manipulation] + + +熟悉bits的一些trick: +- ~0 = -1 = 111111...11111111 (32-bit) +- Create mask by shifting right >>>, and shifting left +- Reverse to get 0000...11110000 format mask +- & 0000 = clean up; | ABC = assign ABC + + + +--- + +**89. [Maximum XOR of Two Numbers in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20XOR%20of%20Two%20Numbers%20in%20an%20Array.java)** Level: Medium Tags: [Bit Manipulation, Trie] + + +比较难想到. 利用到XOR性质A^B=C, then A=B^C. +1. 枚举可能的A, 2. 然后一个个猜. + +1. 枚举A: 因为求MAX肯定是找leading-1最多的数字, 那么枚举A从(1000000...000)开始, +每次多一位取1或者0 +2. 因为枚举A的时候是按照每个bit来, 那么B和C也要以同样数位出现. +这里吧B和C变成了prefix的形式, 放在了set里面. +跟2sum用hashmap的思想类似, 每次用枚举的 A^B=C, 看看结果C是否已经在set里面. +如果在, 证明枚举的A可能被B^C得出, 那么就找到了一种情况. + +还用到一些技巧: +mask = (1 << i); // i位mask +mask = mask | (1 << i); // prefix mask + + + +--- + +**90. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**91. [Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)** Level: Medium Tags: [Backpack DP, DP] + + +给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法. + +nums 里的数字, 可以重复使用. 不同的order可以算作不同的拼法. + +#### Backpack DP +- dp[i] 表示: # of ways to fill weight i +- 1维: dp[w]: fill weigth w 有多少种方法. 前面有多少种可能性, 就sum多少个: +- dp[w] = sum{dp[w - nums[i]]}, i = 0~n + +##### 分析 +- 拼背包时, 可以有重复item, 所以考虑'最后被放入的哪个unique item' 就没有意义了. +- 背包问题, 永远和weight分不开关系. +- 这里很像coin chagne: 考虑最后被放入的东西的value/weigth, 而不考虑是哪个. + + + + + + +--- + +**92. [Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)** Level: Hard Tags: [Binary Search, DP, Partition DP] + + +给一串书pages[i], k个人, pages[i] 代表每本书的页数. k个人从不同的点同时开始抄书. + +问, 最快什么时候可以抄完? + +#### Partition DP +- 第一步, 理解题目要求的问题: 前k个人copy完n本书, 找到最少的用时; 也可以翻译成: `n本书, 让k个人来copy, 也就是分割成k段`. +- 最后需要求出 dp[n][k]. 开: int[n+1][k+1]. +- 原理: +- 1. 考虑最后一步: 在[0 ~ n - 1]本书里, 最后一个人可以选择copy 1 本, 2 本....n本, 每一种切割的方法的结果都不一样 +- 2. 讨论第k个人的情况, 在 j = [0 ~ i] 循环. 而循环j时候最慢的情况决定 第k个人的结果(木桶原理): `Math.max(dp[j][k - 1], sum)`. +- 3. 其中: `dp[j][k-1]` 是 [k-1]个人读完j本书的结果, 也就是著名的`上一步`. 这里循环考虑的是第k个人不同的j种上一步 : ) +- 4. 循环的结果, 是要存在 dp[i][k] = Math.min(Math.max(dp[j][k - 1], sum[j, i]), loop over i, k, j = [i ~ 0]) +- Time: O(kn^2), space O(nk) + +##### Init +- Init: dp[0][0] = 0, 0个人0本书 +- Integer.MAX_VALUE的运用: +- 当 i = 1, k = 1, 表达式: dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); +- 唯一可行的情况就只有一种: i=0, k=0, 刚好 0 个人 copy 0 本书, dp[0][0] = 0. +- 其他情况, i = 1, k = 0, 0 个人读 1本书, 不可能发生: 所以用Integer.MAX_VALUE来冲破 Math.max, 维持荒谬值. +- 当 i=0, k=0 的情况被讨论时候, 上面的方程式才会按照实际情况计算出 dp[i][k] +- 这道题的init是非常重要而tricky的 + +##### 计算顺序 +- k个人, 需要一个for loop; +- k个人, 从copy1本书开始, 2, 3, ... n-1,所以 i=[1, n], 需要第二个for loop +- 在每一个i上, 切割的方式可以有[0 ~ i] 中, 我们要计算每一种的worst time + +##### 滚动数组 +- [k] 只有和 [k - 1] 相关 +- Space: O(n) + +#### Binary Search +- 根据: 每个人花的多少时间(time)来做binary search: 每个人花多久时间, 可以在K个人之内, 用最少的时间完成? +- time variable的范围不是index, 也不是page大小. 而是[minPage, pageSum] +- validation 的时候注意3种情况: 人够用 k>=0, 人不够所以结尾减成k<0, 还有一种是time(每个人最多花的时间)小于当下的页面, return -1 +- O(nLogM). n = pages.length; m = sum of pages. + + + + +--- + +**93. [Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Perfect%20Square.java)** Level: Review Tags: [Binary Search, Math] + + +Binary找sqrt. 基本 mid+1, mid-1 template. +注意: define index as long. + + + +--- + +**94. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**95. [Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)** Level: Hard Tags: [DP, Interval DP, String] + + +- 给两个string S, T. 检验他们是不是scramble string. +- scramble string 定义: string可以被分拆成binary tree的形式, 也就是切割成substring; +- 旋转了不是leaf的node之后, 形成新的substring, 这就是原来string的 scramble. + + +#### Interval DP 区间型 +- 降维打击, 分割, 按照长度来dp. +- dp[i][j][k]: 数组S从index i 开始, T从index j 开始, 长度为k的子串, 是否为scramble string + +##### Break down +- 一切两半以后, 看两种情况: , 或者不rotate这两半. 对于这些substring, 各自验证他们是否scramble. +- 不rotate分割的两半: S[part1] 对应 T[part1] && S[part2] 对应 T[part2]. +- rotate分割的两半: S[part1] 对应 T[part2] && S[part2] 对应 T[part1]. + +##### Initialization +- len == 1的时候, 其实无法旋转, 也就是看S,T的相对应的index是否字符相等. +- initialization非常非常重要. 很神奇, 这个initailization 打好了DP的基础, 后面一蹴而就, 用数学表达式就算出了结果. +- input s1, s2 在整个题目的主要内容里面, 几乎没有用到, 只是用在initialization时候. +- More details, 看解答 + + + +--- + +**96. [Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)** Level: Medium Tags: [BST, Design, Stack, Tree] + + +画一下, BST in order traversal. 用stack记录最小值, 放在top. O(h) space. +每次消耗TreeNode, 都看看rightNode(其实就是下一个最小的candidate), 并且一条龙stack叠上rightNode所有的left子孙. + +Previous Notes: +用O(h)空间的做法: + +理解binary search tree inorder traversal的规律: + 先找left.left.left ....left 到底,这里是加进stack. + 然后考虑parent,然后再right. + +例如这题: + stack里面top,也就是tree最左下角的node先考虑,取名rst. + 其实这个rst拿出来以后, 它也同时是最底层left null的parent,算考虑过了最底层的parent。 + 最后就考虑最底层的parent.right, 也就是rst.right. + +注意: + next()其实有个while loop, 很可能是O(h).题目要求average O(1),所以也是okay的. + + +用O(1)空间的做法:不存stack, 时刻update current为最小值。 + +找下一个最小值,如果current有right child: + 和用stack时的iteration类似,那么再找一遍current.right的left-most child,就是最小值了。 + +如果current没有right child: + 那么就要找current node的右上parent, search in BinarySearchTree from root. + +注意: + 一定要确保找到的parent满足parent.left == current. + 反而言之,如果current是parent的 right child, 那么下一轮就会重新process parent。 + 但是有错:binary search tree里面parent是小于right child的,也就是在之前一步肯定visit过,如此便会死循环。 + + + + +--- + +**97. [Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, Stack] + + +方法1: 用queue, 把需要的item全部打出来 +方法2: 用stack, 把需要的item先存一行, 每次打开子序列时候, 全部加回stack. + + + +--- + +**98. [Best Time to Buy and Sell Stock with Cooldown.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Cooldown.java)** Level: Medium Tags: [DP] + + +Sequence DP +跟StockIII很像. 分析好HaveStock && NoStock的状态, 然后看最后一步. + + + +--- + +**99. [Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)** Level: Medium Tags: [Array, Binary Search] + + +binary search. +Goal: find peak, where both sides are descending +最左边, 最右边是Integer.MIN_VALUE时候, 也能构成中间数mid是peak的条件. + +Note: +没有必要特别check (mid-1)<0或者(mid+1)>=n. +证明: +1. 最左端: 当start=0, end = 2 => mid = 1, mid-1 = 0; +2. 最右端: 当end = n - 1, start = n - 3; mid = (start+end)/2 = n - 2; +那么mid + 1 = n - 2 + 1 = n - 1 < n 是理所当然的 + + + +--- + +**100. [Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP] + + +给两个string, A, B. 找这两个string里面的LCS: 最长公共字符长度 (不需要是continuous substring) + +#### Double Sequence DP +- 设定dp长度为(n+1), 因为dp[i]要用来表示前i个(ith)时候的状态, 所以长度需要时i+1才可以在i位置, hold住i. +- 双序列: 两个sequence之间的关系, 都是从末尾字符看起, 分析2种情况: +- 1. A最后字符不在common sequence 或者 B最后字符不在common sequence. +- 2. A/B最后字符都在common sequence. 总体count + 1. + + + +--- + +**101. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**102. [Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)** Level: Medium Tags: [Backtracking, String] + + +方法1: Iterative with BFS using queue. + +方法2: Recursively adding chars per digit + + + +--- + +**103. [Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + +time: O(MN) +Space: O(N) + +两个字符串, A要变成B, 可以 insert/delete/replace, 找最小变化operation count + +#### Double Sequence +- 考虑两个字符串的末尾index s[i], t[j]: 如果需要让这两个字符一样, 可能使用题目给出的三种operation: insert/delete/replace? +- 先calculate最坏的情况, 3种operation count + 1; 然后在比较match的情况. +- 注意, 在i或者j为0的时候, 变成另外一个数字的steps只能是全变. +- 第一步, 空间时间都是O(MN), O(MN) +- 滚动数组优化, 空间O(N) + +##### Detail analysis +- insert: assume insert on s, `#ofOperation = (s[0 ~ i] to t[0 ~ j-1]) + 1;` +- delete: assume delete on t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j]) + 1;` +- replace: replace both s and t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j - 1]) + 1;` +- dp[i][j]代表了两个 sequence 互相之间的性质: s[0 ~ i] 转换成 s[0~j] 所需要的最少 operation count +- init: 当i==0, dp[0][j] = j; 每次都要 + j 个character; 同理, 当j==0, dp[i][0] = i; +- 而dp[i][j]有两种情况处理: `s[i] == t[j]` or `s[i] != t[j]` + +##### 何时initialize +- 这种判断取决于经验: 如果知道initialization可以再 double for loop 里面一起做, 那么可以留着那么做 +- 这样属于 `需要什么, initialize什么` +- 事后在做space optimization的时候, 可以轻易在 1st dimension 上做rolling array + +#### Search +- 可以做, 但是不建议:这道题需要找 min count, 而不是search/find all solutions, 所以search会写的比较复杂, 牛刀杀鸡. + + + +--- + +**104. [Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)** Level: Hard Tags: [DP, String] + + +Double Sequence DP: +0. DP size (n+1): 找前nth的结果, 那么dp array就需要开n+1, 因为结尾要return dp[n][m] +1. 在for loop 里面initialize dp[0][j] dp[i][0] +2. Rolling array 优化成O(N): 如果dp[i][j]在for loop里面, 就很好替换 curr/prev + + + +--- + +**105. [Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer] + + +#### Vote 计数 +- vote++, vote--到最后剩下的就是winner. Time O(n), Space O(1) +- Majority Number是指超半数. 超半数的数字, 最后都会至少有vote>=1: match current majority number,vote++;if not, vote--. +- 注意:assume valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 + +#### HashMap count occurance +- Time, Space: O(n) + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**106. [Ones and Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Ones%20and%20Zeroes.java)** Level: Hard Tags: [DP] + + +还是Double Sequence, 但是考虑第三种状态: 给的string array的用量. +所以开了3维数组. + +如果用滚动数组优化空间, 需要把要滚动的那个for loop放在最外面, 而不是最里面. +当然, 这个第三位define在 dp[][][]的哪个位置, 问题都不大. + +另外, 注意在外面calcualte zeros and ones, 节约时间复杂度. + + + +--- + +**107. [Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)** Level: Medium Tags: [Binary Search, Math] + + +傻做就O(n), 要更好就考虑O(logN). +减少重复计算, 一切两半. + +注意: +- n/2的奇数偶数 +- n的正负 +- n == 0的情况, x == 0, x == 1 的情况. + + +--- + +**108. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**109. [Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS] + + +给一串integers, list里面可能有nest list. 算总的sum. 规则, 如果是nested list, 每深一个depth, sum要乘以depth. + +#### DFS +- New interface to understand: object contains integer or object +- Visit all && sum, consider dfs. +- bottom->up is easier: pick nested object and execute dfs, which returns sum of it, add with (level value * weight). +- 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) +- Note1: not multiplying on overall level sum. Only multiply level with single value at this level. +- Note2:top->bottom is not necessary: there is not need of passing added object into next level. + +#### BFS +- bfs, queue, 处理queue.size(). +- use a level variable to track levels + + + +--- + +**110. [Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Same%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### BFS +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**111. [Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +如题, build balanced BST from sorted array + +#### DFS +- Binary Search Tree特点: 左边的node都比右边的node小. +- height balance, subtree height 相差<1, 必须左右sub tree均分. 做DFS(num, start, end) +- 在每一个level, 找到中间点, 然后分割2半, 继续dfs +- Divide and Conquer +- time/space: O(n), visit all nodes, no redundant visits. + + + +--- + +**112. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**113. [Add Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Digits.java)** Level: Easy Tags: [Math] + + +方法1: 普通做法就是按照题意, double-while loop把数字加起来. 第一层循环是O(n), 然后第二层循环就少很多数位, overall O(n) + +方法2: 找数学规律, 每过9个数字, 取mod就会开始重复, 所以给所有数字取mod 就可以间接找到答案. O(1) + + + +--- + +**114. [Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. +遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + +跟Add Binary的理解方式一模一样. + +注意: +Linked List 没有天然size. +用DummyNode(-1).next来hold住结果. + + + + +--- + +**115. [Add Two Numbers II.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers%20II.java)** Level: Medium Tags: [Linked List] + + +Singly-linked list需要reverse, 用stack. +最终结果要恢复成input list 那样的sequence方向, 用stack一个个pop()刚好就可以做到. + +加法都一样: + 1. sum = carry + 2. carry = sum / 10 + 3. sum = sum % 10; + + + +--- + +**116. [Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Balanced%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个binary tree, 看是否是height-balanced + +#### DFS +- DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. +- 一旦有 <0 或者差值大于1, 就全部返回Integer.MIN_VALUE. Integer.MIN_VALUE比较极端, 确保结果的正确性。 +- 最后比较返回结果是不是<0. 是<0,那就false. +- Traverse 整个tree, O(n) + + +#### DFS, maxDepth function +- Same concept as in 1, but cost more traversal efforts. + + + +--- + +**117. [Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +HashMap + + + +--- + +**118. [Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +给一个特殊的binary tree, treeNode里面有一个 next pointer. + +写一个function, 把所有node都更同level的node 连在一起. 最右边的node.next = NULL + +#### DFS + Divide and Conquer +- 题目要求DFS. 想清楚了如何在DFS level把几种情况都考虑了, 写起来很简单. NOT BFS, because requires O(1) space +- 对于一个root来说, 只有几个点可以顾忌到: root.left, root.right, root.next. +- 想办法把这三个方向的点, 能连起来的都连起来: +- 1. `node.left.next = node.right` +- 2. If `node.next != null`, link `node.right.next = node.next.left`; +- 然后在dfs(root.left), dfs(root.right) +- Time: visit && connect all nodes, O(n) + +#### BFS +- 不和题意,用了queue space,与Input成正比。太大。 +- BFS over Tree。 用Queue 和 queue.size(),老规矩。 +- process每层queue时, 注意把next pointer加上去就好. + + + +--- + +**119. [Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +如题, 验证是否是BST. + +#### DFS +- 查看每个parent-child关系: leftchild < root < rightChild; +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest +- imagine we know the two extreme border: Integer.MIN_VALUE, Integer.MAX_VALUE; pass node around and compare node vs. node.parent. +- 方法: 把root.val 传下来作为 max 或者 min, 然后检查children +- + +##### Note: +- min/max需要时long type. +- 如果题目真的给node.val = Integer.MAX_VALUE, 我们需要能够与之比较, long就可以. + + + +--- + +**120. [Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List] + + +如题, 把一个sorted singly linked list 转换成一个 height balanced BST + +#### DFS +- Divide and Conquer +- 找到mid node +- 然后分割两半, 分别dfs做各自两个subtree: node.left,node.right +- 用长度来定位mid, 每次找中间点做root, 然后前半段, 后半段分别dfs with length. +- 用快慢pointer 找到mid. Better: 不用traverse entire linked list + +#### Details +- slowPointer = node; +- fastPointer = node.next; +- 然后把root = mid.next +- 然后开始sortedListToBST(mid.next.next); //后半段 +- mid.next = null;//非常重要,要把后面拍过序的断掉 +- sortedListToBST(head); //从头开始的前半段 +- 最后root.left, root.right merge一下。 + + + +--- + +**121. [Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### DFS +- 分析题意后, 按照题意: Flatten the tree, no extra space. +- 1. reserve right child: `reservedRightNode` +- 2. Connect `root.right = root.left`, DFS flatten(root.right) +- 3. 移花接木, coneect end of list -> reservedRightNode +- 4. flatten 剩下的. root.right... + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**122. [Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)** Level: Easy Tags: [Backtracking, Binary Tree, DFS] + + +给一个binary tree, 返回所有root-to-leaf path + +#### DFS, backtracking +- Find all paths, bfs/dfs all works. dfs will be simplier to write +- Recursive:分叉. dfs. +- top->bottom: enumerate current node into the list, carry to next level, and backtrack +- top->bottom is trivial to consider: path flows from top->bottom + +#### DFS, bottom->up +- We can also take current node.left or node.right to generate list of results from the subproblem +- let dfs return list of string candidates, and we can run pair the list with currenet node, once they come back. +- TODO: can write code to practice + +#### Iterative +- Iterative, 非递归练习了一下 +- 因为要每次切短list, 所以再加了一个Stack 来存level +- 单这道题用dfs更简单, 因为找的就是从头到尾的path, 是dfs的pattern + + + + +--- + +**123. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**124. [Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)** Level: Medium Tags: [Hash Table, String, Two Pointers] + + +方法1: +Two Pointers +双指针: 从start开始遍历, 但是第一步是while loop来推进end; 直到推不动end, 然后start++ +巧妙点: 因为end是外围variable, 在start的loop上, end不会重置;[star ~ end] 中间不需要重复计算. +最终可以O(n); + +Previous verison of two pointers: +用两个pointer, head和i. +注意:head很可能被退回到很早的地方,比如abbbbbba,当遇到第二个a,head竟然变成了 head = 0+1 = 1. +当然这是不对的,所以head要确保一直增长,不回溯 + +方法2: + HashMap: + 一旦有重复, rest map. + 没有重复时候, 不断map.put(), 然后求max值 + +问题: 每次reset map之后就开始从新从一个最早的index计算, 最坏情况是O(n^2): +'abcdef....xyza' + + + + +--- + +**125. [Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, String, Two Pointers] + + +基本思想: 用个char[]存string的frequency. 然后2pointer, end走到底, 不断validate. +符合的就process as result candidate. + +HashMap的做法比char[]写起来要复杂一点, 但是更generic + + + +--- + +**126. [Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Two Pointer: Slow Fast Pointer +- O(1) sapce: 用快慢指针。一个跑.next, 一个跑.next.next。 总有一次,fast会因为cycle而追上slow。 +- 那个时候其实slow.val = fast.val. + +#### Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**127. [Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +O(n), one pace, no extra space +找到窗口, 然后平移, 最后pre 和 head之间 skip一个node就好. + + + +--- + +**128. [Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String] + + +大清洗 O(nk) +map.size一旦>k,要把longest string最开头(marked by pointer:start)的那个char抹掉 +一旦某一个char要被清除,所以在这个char 的1st and last appearance之间的char都要被清洗from map + + + + +--- + +**129. [Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Linked List, Math, Two Pointers] + + +LinkedList 里面有 cycle, 找到cycle的起始点(第一个重复出现的element). + +#### Slow, fast Pointer +- 快慢指针, O(1)space. +- 1. 确认有cycle后 2. 数学问题:找到开头. +- 当head == slow.next时候, head就是cycle starting point. +- 也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +#### 证明 +- 1. 假设慢指针走t步, 快指针走快一倍, 也就是2t. +- 2. 我们假设cycle的长度是Y, 而进入cycle之前的长度为X. +- 3. 假设慢指针走了m圈cycle, 而快指针走了n圈cycle之后, 两个pointer相遇. +- 4. 最终在Y cycle里面的K点相遇, 也就是两个指针都在这最后一圈里面走了K 步. +- 那么: +- t = X + mY + K +- 2t = X + nY + K +- 整合公式: X + K = (n - 2m)Y +- 这里的m和n不过是整数的跑圈数, 也就是说X和K加在一起, 总归是结束cycle. X 和 K 互补 +- 结论: 当slow/fast 指针在K点相遇后, 再走X步, 就到了cycle的起点, 也就是题目要求的起点. + +#### Hash Table, O(n) space + + + + +--- + +**130. [Kth Smallest Element in a Sorted Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20Sorted%20Matrix.java)** Level: Medium Tags: [Binary Search, Heap] + +time: O(n + klogn) +space: O(n) + +给一个sorted matrix, 找kth smallest number (not distinct) + +Related: `Kth Largest Element in an Array` + +#### PriorityQueue +- 和Merge K sorted Array/ List 类似:使用PriorityQueue。 +- 因为Array的element无法直接找到next,所以用一个class Node 存value, x,y positions. +- Initial O(n) time, also find k O(k), sort O(logn) => O(n + klogn) +- 变型: Kth Largest in N Arrays + +#### Binary Search +- we know where the boundary is start/end are the min/max value. +- locate the kth smallest item (x, y) by cutt off partition in binary fasion: +- find mid-value, and count # of items < mid-value based on the ascending matrix +- O(nlogn) + + + + +--- + +**131. [Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +画图, 最小值被rotate之后, 变成array中间的最低谷. +并且, 左边山坡的最小值, 大于右边山坡的最大值. +以此来给binary search做判断. + +O(nlogn) + + + +--- + +**132. [Find Minimum in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Hard Tags: [Array, Binary Search] + + +一个需要严谨思考的题目. 因为有duplicate, 会导致不断平移, 所以最终time complexity是O(n) +所以不如直接扫一遍, 给出答案. + +但是还是写一个Binary Search的样子, 只不过worst结果是O(n) + + + +--- + +**133. [Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)** Level: Medium Tags: [Union Find] + + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + + + +--- + +**134. [Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)** Level: Medium Tags: [Union Find] + + +Lint还不能跑, 全部按照题意和答案document的. + + + +--- + +**135. [Connecting Graph III.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20III.java)** Level: Medium Tags: [Union Find] + + +还是UnionFind的变形, 这次是算有剩下多少个union. 其实非常简单, 维持一个全局变量count: +一开始count=n, 因为全是散装elements; 每次union, count--. + + + +--- + +**136. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**137. [Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands%20II.java)** Level: Hard Tags: [Union Find] + + +给一个island grid[][], and list of operations to fill a particualr (x,y) position. + +count # of remaining island after each operation. + +#### Union Find, model with int[] +- 把board转换成1D array, 就可以用union-find来判断了. +- 用int[] father 的unionFind, 需要转换2D position into 1D index. 这样比较clean +- 判断时,是在四个方向各走一步,判断是否是同一个Land. +- 每走一次operator,都会count++. 若发现是同一个island, count-- +- count的加减, 都放在了UnionFind自己的function里面, 方便tracking, 给几个helper function就对了. +- Time: O(k * log(mn)) + +#### Union Find, model with Hashmap +- 用HashMap的Union-find. + + +#### Note: +- Proof of UnionFind log(n) time: https://en.wikipedia.org/wiki/Proof_of_O(log*n)_time_complexity_of_union%E2%80%93find + + + +--- + +**138. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + +**139. [Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- HashMap构建Trie. Trie三个Method: +- 1. Inset: 加 word +- 2. Search: 找word +- 3. StartWith: 找prefix + +##### 特点 +- 只有两条children的是binary tree. 那么多个children就是Trie +- 那么没有left/right pointer怎么找孩子? +- 用HashMap,以child的label为Key,value就是child node。 HashMap走位 + +##### 其他 +- node里的char在这是optional. 只要在每个TrieNode里面用map存储向下分布的children就好了. +- 另外有种题目,比如是跟其他种类的search相关,在结尾要return whole string,就可以在node里存一个up-to-this-point的String。 + +##### Previous Note +- 如果是遇到一个一个字查询的题,可以考虑一下。 +- 构建TrieNode的时候要注意:如何找孩子?如果是个map的话,其实就挺好走位的。 +- 而且,每个node里面的 char 或者string有时候用处不大, +- 可以为空。但是有些题目,比如在结尾要return一些什么String,就可以在end string那边存一个真的String。 + + + + + +--- + +**140. [Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +Trie结构, prefix tree的变形: '.'可以代替任何字符,那么就要iterate这个node所有的children. + +节点里面有char, isEnd, HashMap +Build trie = Insert word:没node就加,有node就移动。 +Search word:没有node就报错. 到结尾return true + +这题因为'.'可以代替任何possible的字符,没一种都是一个新的path,所以recursive做比较好些。 +(iterative就要queue了,麻烦点) + + + +--- + +**141. [Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)** Level: Hard Tags: [Backtracking, DFS, Trie] + + +给一串words, 还有一个2D character matrix. 找到所有可以形成的words. 条件: 2D matrix 只可以相邻走位. + +#### Trie, DFS +- 相比之前的implementation, 有一些地方可以优化: +- 1. Backtracking时候, 在board[][] 上面mark就可以, 不需要开一个visited[][] +- 2. 不需要implement trie的所有方程, 用不到: 这里只需要insert. +- 普通的trie题目会让你search a word, 但是这里是用一个board, 看board的每一个字母能不能走出个Word. +- 也就是: 这里的search是自己手动写, 不是传统的trie search() funcombination +- 3. TrieNode里面存在 end的时候存string word, 表示到底. 用完了 word = null, 刚好截断重复查找的问题. + +##### 关于Trie +- Build Trie with target words: insert, search, startWith. Sometimes, just: `buildTree(words)` and return root. +- 依然要对board matrix做DFS。 +- no for loop on words. 直接对board DFS: +- 每一层,都会有个up-to-this-point的string. 在Trie里面check它是不是存在。以此判断。 +- 若不存在,就不必继续DFS下去了。 +- Trie solution time complexity, much better: +- build Trie: n * wordMaxLength +- search: boardWidth * boardHeight * (4^wordMaxLength + wordMaxLength[Trie Search]) + + +#### Regular DFS +- for loop on words: inside, do board DFS based on each word. +- Time cpmplexity: word[].length * boardWidth * boardHeight * (4^wordMaxLength) + +#### Previous Notes +- Big improvement: use boolean visited on TrieNode! +- 不要用rst.contains(...), 因为这个是O(n) 在leetcode还是会timeout(lintcode竟然可以pass)! +- 在Trie search() method 里面,凡是visit过的,mark一下。 + + + + +--- + +**142. [Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)** Level: Medium Tags: [Array, Backtracking, DFS] + + +#### DFS, Backtracking: +- 找到开头的字母, 然后recursively DFS 去把word串到底: +- 每到一个字母, 朝四个方向走, 之中一个true就可以. +- Note:每次到一个字母,mark一下'#'. 4个path recurse回来后,mark it back. + +#### Note: other ways of marking visited: +- 用一个boolean visited[][] +- Use hash map, key = x@y + + + + +--- + +**143. [Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)** Level: Hard Tags: [Backtracking, Trie] + + +可以开Trie class, 里面用到TrieNode. 开Trie(words) 可以直接initalize with for loop +TrieNode 里面可以有一个 List startWith: 记录可以到达这个点的所有string: 有点像树形, ancestor形状的存储. + +神操作: +根据square的性质, 如果选中了list of words, 设定 int prefixIndex = list.size(). +取出list里面的所有word[prefixedIndex], 并且加在一起, 就是下一个word candidate的 prefix. + +形象一点: +list = ["ball", "area"]; +prefixIndex = list.size(); ball[prefixIndex] = 'l'; area[prefixIndex] = 'e'; +//then +candidatePrefix = ball[prefixIndex] + area[prefixIndex] = "le"; +这里就可以用到Trie的那个 findByPrefix function, 在每个点, 都存有所有这个点能产生的candidate. +这时, 试一试所有candidate: dfs + +能想到这种倒转的结构来存prefix candidates 在 Trie里面, 这个想法非常值得思考. + + + +--- + +**144. [Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. +#### 方法1 +Array, 维持一个左手最高墙array, 右手最高强array. +对于每个index而言, vertically 能存放的最大水柱, 就是靠左右最高墙决定的: +min(leftHighestWall, rightHighestWall) - currHeight. + +#### 方法2 +方法1上面的优化, two pointer, 还是找左边最高和右边最高. O(1) space. +利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +左边按照maxLeft来计算, 右边按照maxRight来计算. + +#### 方法3 +2 Pointers, 双面夹击: +1. 找中间最高bar的index +2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条。 +3. 每次还要减去block自身的height + +#### 方法4 +主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. + + + + +--- + +**145. [Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)** Level: Easy Tags: [Design, Stack] + + +双Stack:一个正常stack,另一个minStack存当下level最小值. 注意维护minStack的变化 + +另外. 如果要maxStack,也是类似做法 + + + +--- + +**146. [Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)** Level: Easy Tags: [Design, Stack] + + +#### 双Stack +画图, 知道最后maintain的stack是那个 reverseStack: pop(), peek(), empty() 都在这个stack上, 无需变换. +push()里面做stack和reverseStack的来回倾倒. +相比老的code, 在PUSH里面做倾倒, 更容易读. + +#### Previous notes +双Stack. 一个是等于是queue,一个是backfillStack. +Tricky: 是在pop()和peek()的时候backfill, 并且要等到stack用完再backfill. +写一下例子就知道,如果提早backfill,stack.peek()就不是queue的head了. + + + + +--- + +**147. [Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)** Level: Medium Tags: [DFS, Divide and Conquer, Stack] + + +给一个expression string. 里面包括数字, 字母, 括号. 其中数字代表括号里面的内容重复几次. + +括号里面可以是String, 也可能是expression. + +目的: 把expression展开成一个正常的String. + + +#### Stack, Iteratively +- Process inner item first: last come, first serve, use stack. +- Record number globally and only use it when '[' is met. +- Stack存 [ ] 里面的内容, detect 括号开头结尾: 结尾时process inner string +- 有很多需要注意的细节才能做对: +- Stack 也可以用, 每个地方要注意 cast. 存进去的需要是Object: String, Integer +- 几个 type check: instanceof String, Character.isDigit(x), Integer.valueOf(int num) +- 出结果时候: `sb.insert(0, stack.pop())` + + +#### DFS +- Bottom->up: find deepest inner string first and expand from inside of `[ ]` +- 与Stack时需要考虑的一些function类似. 特别之处: **检查`[ ]`的结尾** +- 因为DFS时候, 括号里的substring会被保留着进入下一个level, 所以我们在base level要keep track of substring. +- 用int paren 来track 括号的开合, 当paren再次==0的时候 找到closure ']' +- 其他时候, 都要继续 append to substring + + + + +--- + +**148. [Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)** Level: Hard Tags: [Array, Monotonous Stack, Stack] + + +给n个bar,组成柱状图histogram. 求在这一排柱状图里面可以找到的面积最大的长方形. + +思考: 找长方形面积, 无非是找两个index, 然后底边长度 * height. + +#### Monotonous Stack +- 重点是根据找Histogram里面rectangle的性质, 维持一个单调递增的Stack +- 在loop over indexes的时候: +- 如果高度>= previous peek(), 那么对于那个peek, 就意味着, 往下走, 一直走高嘛, 之前的peek总可以继续抄底 +- 什么时候不能抄底了呢? 就是有一个下降趋势的时候 +- 这时候并不是calculate所有前面的peek, 而是考虑 大于 current height的之前所有的peek. +- 把这些peek到 current height 前一格的rectangle全部找出来: stack.pop() +- 这个stack.pop()的过程里面, 其实没有算上 current height, 因为需要留到下一轮, 把current index加进stack 再说 +- 为什么用stack? 因为需要知道连续递增的peek, stack.peek() O(1), 好用 + 而其实不用stack, 也可以用其他方式记录所有height, 只不过要 O(n)去找peek不方便 + +#### 知识点 +- 理解monotonous stack 是如何被维护的 +- 维护monotonous stack 是题目需要, 而不是stack本身性质, 是一种借助 stack.peek() O(1)的巧妙用法. + + + + +--- + +**149. [Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)** Level: Medium Tags: [Stack, Tree] + + +给一串数字, 做一个 maximum binary tree: 最顶上的root最大; 左child也是一个max tree, 右child也必须是max tree. + +#### Monotonous Stack +- 用到bottom->top递减的stack: 最底下的root维持成最大的element. +- 过程当中, 一旦遇到currNode.val > stack.peek(), 就意味着需要把这个currNode放在 stack的底层位置. +- 也就是说, 遇到这个条件, process, pop()所有 currNode.val > stack.peek(), 最后把currNode加进去. + +- maxTree题目本身的要求是: 大的在最中间, 左右两边的subTree也要是maxTree: +- Monotonous Stack在这里帮助 keep/track of max value, 但是left/right tree的logic是MaxTree独有的. +- left/right node的assignment是根据题目要求: 中间最大值分开后, 左边的是左边subTree, 右边的作为右边subTree. + +#### Previous notes +- Should memorize MaxTree. 依次类推,会做Min-Tree, Expression Tree +- Stack里,最大的值在下面。利用此性质,有这样几个step: + +##### Step1 +- 把所有小于curr node的,全Pop出来, while loop, keep it going. +- 最后pop出的这个小于Curr的node:它同时也是stack里面pop出来小于curr的最大的一个,最接近curr大小。(因为这个stack最大值靠下面) +- 把这个最大的小于curr的node放在curr.left. + +##### Step2 +- 那么,接下去stack里面的一定是大于curr: +- 那就变成curr的left parent. set stack.peek().right = curr. + +##### Step3 +- 结尾:stack底部一定是最大的那个,也就是max tree的头。 + + + + + +--- + +**150. [Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Integer.java)** Level: Easy Tags: [Math] + + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + + + +--- + +**151. [Swap Nodes in Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Nodes%20in%20Pairs.java)** Level: Medium Tags: [Linked List] + + +#### enumurate +基本原理, 写出来, 然后连线: +pre -> A -> B -> C -> ... +需要一个虚拟 preNode做起始node, 不然无法把后面的node换到开头. + +#### 注意 +用dummy = pre作为head前一格. +用 `pre.next == null && pre.next.next` 来check是否为NULL. +pre.next.next 保证了至少有一次swap. + + + +--- + +**152. [Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)** Level: Hard Tags: [Binary Search, DFS, Divide and Conquer] + + +2Dmatrix, 里面的value有一些递增, 递减的特点(细节比较长, 看原题). 目标是找到peak element + +peak: 比周围4个方向的点value大 + +#### DFS + +##### 基本原理 +- 我们不可能一口气准确定位(x,y), 但是我们可以再一个row/col里面, 找到1D array的 peak. +- 根据这个点, 再往剩下两个方向移动 +- 1. 在中间的一行i=midX, 找到peak所在的y. +- 2. 在中间的一列j=midY, 找到peak所在的x. (有可能强势override之前找到的y, 也就是放弃那一行的peak, 在midY上找peak) +- 3. 根据 (x,y) 的4个neighbor check (x,y)是不是 peak, 如果不是, 像更高的位置移动一格 +- 4. 根据之前算的 midX, midY 把board分成4个象限, 在每一份里面再继续找 +- 这个题目LintCode不给做了, 所以思路对的, 但是解答还没有再次验证. + +##### 剪枝/切分象限 +- 每次只是找到一个row/col里面的peak而已! +- 找到这个点, 就等于把board切成了两半. +- 然后, 再跟剩下的相邻的两个位置比较, 就知道了哪里更大, 就去哪里找peak, 也就是又切了第二刀. +- 切第二刀的时候, 也要把(x, y) 移到需要取的象限. 进行DFS +- 根据mid row 切割: +- http://www.jiuzhang.com/solution/find-peak-element-ii/#tag-highlight-lang-java +- http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf + +##### 时间复杂度 +- 每一个level都减一半 +- T(n) = n + T(n/2) = n + n/2 + n/4 + ... + 1 = n(1 + 1/2 + .... + 1/n) = 2n = O(n) + +#### Binary Search +- TODO +- O(nLogN) + + + +--- + +**153. [Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + + +#### s- qrt(int x) +- 理解题意, 从[0, x]找一个可以m*m=x的值. +- 注意, 如果找不到, 最后问考官该return一个什么值:按道理,因为return int, 会取整,那么return一个平方最close to x就可以. +- 注意 mid 用 long, 因为很可能超过最大int. + +#### sqrt(double x) +- 二分float number, 应该用精度来定义结尾. +- 还是二分, 但是判断条件变成: while ( end - start > eps) +- eps = 1e-12,也就是精度到1e-12 + + + +--- + +**154. [First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Bad%20Version.java)** Level: Easy Tags: [Binary Search] + + +Binary Search + +根据isBadVersion的性质,判断还如何end=mid or start=mid. +isBadVersion 是有方向的嘛,一个点错了,后面全错。 + + + +--- + +**155. [Wood Cut.java](https://github.com/awangdev/LintCode/blob/master/Java/Wood%20Cut.java)** Level: Medium Tags: [Binary Search] + + +二分的思想: 判断的是一个 validate() function, 而不是简单的'==' + +不需要sort! 为什么呢? 因为我们不是在given array上面二分, 我们是根据最大值在[0, max]上二分. + +Overall time: O(nLogM), where M = largest wood length + + + +--- + +**156. [Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +- 注意不要思维定式: 以为mid是index +- 这里mid其实是binary search on value [1, n] 的一个value. +- 再次用到validate() function + +Time: O(nLogN) + + + +--- + +**157. [Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)** Level: Hard Tags: [Hash Table, String, Trie] + + +Obvious的做法是全部试一遍, 判断, 变成 O(n^2) * O(m) = O(mn^2). O(m): isPalindrome() time. + +当然不行了, 那就看是O(nlogN), 还是O(n)? + +#### 方法1: Hash Table + Palindrome的性质. 复合型. +O(mn) + +##### 思路 +- 每一个word, 都可以拆分成 front + mid + end. 如果这个word + 其他word可以组成palindrome,那就是说 +- 砍掉 (mid+end), front.reverse() 应该存在 words[] 里面. +- 砍掉 (front+mid), end.reverse() 应该存在 words[] 里面. +- 我们用HashMap存所有的, 然后reverse, 找配对就好. + +##### Corner case +- 如果有 empty string "", 那么它跟任何一个palindrome word, 都可以配对, 并且根据位置前后变换, 凑成2份 distinct indexes. +- 这样就有了那个 `if (reverseEnd.equals("")) {...}` 的logic. +- 注意: 虽然在处理砍头/砍尾的两个 for loop里面都在根据 empty string 重复记录, + 但因为 "" 自己本身不能作为起点, 所以overall只会在被其他palindrome配对时记录一次. + + +#### 方法2: Trie +还要做一下那. + + + +--- + +**158. [Game of Life.java](https://github.com/awangdev/LintCode/blob/master/Java/Game%20of%20Life.java)** Level: Medium Tags: [Array] + + +#### basic +- 简单的implementation, 把count function写清楚就好. +- time: O(mn), extra space: O(mn) +- 注意结尾要一个个board[i][j]copy + +#### follow up +unlimited border? 可能需要分割board. 用大框分割, 每次换行的时候, 重复计算2行就好了. see details below. + +#### improvement: do it in place! +- time: O(mn), extra space: O(1) +- bit manipulation: 用第二个bit来存previous value. +- 因为我们只考虑 0 和 1 而已, 所以可以这样取巧. 但是并不scalable. +- 如果需要存multiple state, 可能需要移动更多位, 或者用一个 state map +- 注意 bit manipulation 的细节: <<1, >>1, 还有 mast的用法: |, & + + + + +--- + +**159. [Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)** Level: Review Tags: [Array, Binary Search, PreSum] + + +给int[] nums 和 window min size k. window size可以大于K. 找最大的连续数列average value. + +- Binary Search的思想, 用在所要找的这个 average sum 上面. 大小是在[min, max]之中 +- 找k的时候, 是>=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + + + +--- + +**160. [Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### 方法1: +找是否有overlap. priorityQueue 按照start time排序好以后, 比较current和peek: current.end > peek.start? + +#### 方法2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 + + + +--- + +**161. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**162. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**163. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**164. [Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +##### DP +- 计数DP.注意方程式前两位置加在一起: 前两种情况没有overlap, 也不会缺情况. +- 注意initialization, 归1. +- 需要initialize的原因是,也是一个reminder: 在方程中会出现-1index +- Of course, row i = 0, or col j = 0, there is only 1 way to access +- time O(mn), space O(mn) + +##### 滚动数组 +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + + + + +--- + +**165. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**166. [Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Square.java)** Level: Medium Tags: [Coordinate DP, DP] + + +只能往右边,下面走, 找面积最大的 square. 也就是找到变最长的 square. + +#### DP +- 正方形, 需要每条边都是一样长度. +- 以右下角为考虑点, 必须满足条件: left/up/diagonal的点都是1 +- 并且, 如果三个点分别都衍生向三个方向, 那么最长的 square 边就是他们之中的最短边 (受最短边限制) +- dp[i][j]: max square length when reached at (i, j), from the 3 possible directions +- dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; +- Space, time O(mn) + +##### init +每个点都可能是边长1, 如果 matrix[i][j] == '1' + +##### 滚动数组 +[i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + +--- + +**167. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**168. [Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)** Level: Medium Tags: [DP, Game Theory, Greedy] + + +拿棋子游戏, 每个人可以拿1个或者2个, 拿走最后一个子儿的输. 问: 根据给的棋子输, 是否能确定先手的输赢? + +Game Theory: 如果我要赢, 后手得到的局面一定要'有输的可能'. + +#### DP, Game Theory +- 要赢, 必须保证对手拿到棋盘时, 在所有他可走的情况中, '有可能败', 那就足够. +- 设计dp[i]:表示我面对i个coins的局面时是否能赢, 取决于我拿掉1个,或者2个时, 对手是不是会可能输? +- dp[i] = !dp[i - 1] || !dp[i-2] +- 时间: O(n), 空间O(n) +- 博弈问题, 常从'我的第一步'角度分析, 因为此时局面最简单. + +#### Rolling Array +空间优化O(1). Rolling array, %2 + + + +--- + +**169. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**170. [Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Recursive +- 在自己的基础上recursive, 不用helper function +- Divide and Conquer, with helper(dfs) method +- O(n) time, no extra space + +#### Iterative: Stack +- Add left nodes all the way +- Print curr +- Move to right, add right if possible +- O(n) time, O(h) space + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. + +若不右移, 很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + +#### HashMap +? How? + + + +--- + +**171. [Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Recursive +trivial, 先加left recursively, 再加right recursively, 然后组成头部. + +#### Stack +- 双stack的思想, 需要在图纸上画一画 +- 原本需要的顺序是: 先leftChild, rightChild, currNode. +- 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild +- 这样出来的结果是reverse的, 那么翻转一下就可以了. +- v1做的时候用了stack1, stack2, 因为根据这个双stack的思想而来 +- v2简化, 可以放在一个stack里面, 每次record result 的时候: rst.add(0, item); + +##### 利用stack的特点 +- 每次加element进stack的时候, 想要在 bottom/后process的, 先加 +- 想要下一轮立刻process的, 最后push进stack. + +##### 注意 +这些binary tree traversal的题目.常常有多个做法:recursive or iterative + + + +--- + +**172. [Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Change%20to%20Anagram.java)** Level: Easy Tags: [String] + + +HackerRank里面的random 题目: 给一个string, 一切成两半, 看两半要变化多少个字符, 能变成anagram. + +- 切两半成两个String A,B. 分别在int count[26]里面++, --. +- 记录 26个小写字母的频率. 如果全部抵消, 就是anagram. +- 注意: 最后count出来要除以2:字母不同,会在不同的字母位上加减count,那么就是刚好重复计算了一遍。所以除以二 + +- Note: HackerRank里要注意自己写: Scanner, import java.util, non-static method ...etc. + + + +--- + +**173. [Classical Binary Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Classical%20Binary%20Search.java)** Level: Easy Tags: [Binary Search] + + +#### Binary Search Template +- while: start + 1 < end +- mid = start + (end - start) / 2; +- 根据mid作比较 +- 末尾double check start, end. + + + + +--- + +**174. [Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)** Level: Easy Tags: [DP, Memoization, Sequence DP] + + +每一步可以走1步或者2步, 求总共多少种方法爬完梯子. + +#### Recursive + Memoization +- 递归很好写, 但是重复计算, timeout. time: O(2^n) +- O(2^n): each n can spawn 2 dfs child, at next level, it will keep spawn. Total 2^n nodes will spawn. +- 用全局变量int[] memo 帮助减少重复计算 +- O(n) time, space + +#### DP +- 加法原理, 最后一步被前两种走法决定: dp[i] = dp[i - 1] + dp[i - 2] +- 基础sequence DP, int[] dp = int[n + 1]; +- DP[]存的是以 1-based index的状态 +- dp[i]: count # of ways to finish 前i个 台阶 +- 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1] +- dp[0]往往是有特殊状态的 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**175. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**176. [Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +#### Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, +- 找到 node.val == target, 或者走位走完, return closest + + + +--- + +**177. [Compare Version Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Version%20Numbers.java)** Level: Medium Tags: [String] + + +给两串version number, 由数字和'.' 组成. 比较先后顺序. + +If version1 > version2 return 1, if version1 < version2 return -1, otherwise return 0. + +#### divide and conquer +- 用 str.split("\\.") 分割string +- Convert成integer, 逐个击破 + +#### 注意 +- '1.0' 和 '0' 是相等的 +- 如果可以假设version integer都是valid, 直接Integer.parseInt()就可以了 +- 不然的话, 可以compare string + + + +--- + +**178. [Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样, 如果一样, 直接 2 ^ h - 1就好 +- 不一样的话, 再DFS + +##### Trick +- 直接DFS会timeout, O(n), 其实可以optimize +- to pass the test with O(h^2), 位运算: Math.pow(2, h) = 2 << (h - 1). 神奇! +- 2 << 1就是把所有bits往左移动一位, 也就是 * 2 + +#### Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + + + +--- + +**179. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**180. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**181. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**182. [Closest Number in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Number%20in%20Sorted%20Array.java)** Level: Easy Tags: [Binary Search] + + +- Binary Search 的一种变型, LintCode无法再跑一边. +- 考虑mid-1, mid+1. +- 一旦没有mid = target.index。 那么target最终就narrow down在(mid-1,mid) 或者(mid,mid+1) + + + +--- + +**183. [Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +A complete binary tree is a binary tree in which every level, except possibly the last, + +is completely filled, and all nodes are as far left as possible + +#### BFS +- 当出现了第一次有 null children的node的时候, 说明到了leaf level, mark flag = true; +- 自此以后,queue再不该有node再有child; queue后面出现的node的left/right child应该都是null +- 否则就是有问题, return false; + + + + +--- + +**184. [Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Strings.java)** Level: Easy Tags: [String] + + +看StringA是不是包括所有 StringB的字符. + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + + + +--- + +**185. [Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate.java)** Level: Easy Tags: [Array, Hash Table] + + +无序数组, 找是否有重复element, return true/false. + +#### HashSet +- No brain: HashSet. +- Time O(n), Space O(n) + +#### Sort, Binary Search +- Arrays.sort(x): Time O(nLogN), Space O(1) +- 排序后, 重复数会排在一起, 然后 binary search + + + +--- + +**186. [Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20II.java)** Level: Easy Tags: [Array, Hash Table] + + +Unsorted array, 找出是否有duplicate elemenets: 必要条件是, 这两个element的index i,j 的大小最多相差k. + +#### HashSet +- 很巧妙地根据k range地条件, 把HashSet里面的值控制在[i - k, i] +- 每次不断地往set里面加新元素, 从set里减去末尾index的元素 +- 而set.add(x)如果遇到重复, 会return false. +- 一旦在这个length k 的 range里面, 有重复, 就符合条件. +- Time O(n) + +#### HashTable +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k +- Time O(nm), m = # of duplicates + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**187. [Contains Duplicate III.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20III.java)** Level: Medium Tags: [BST] + + +给一个unsorted array, 问, 是否有两个element, value相差最大为t, 而两个element的index 相差最大为k. + +Note: 虽然题目名字是Contains Duplicate, 但其实要找的两个element不是duplicate, 而是Math.abs(value1 - value2) <= t. + +#### TreeSet +- TreeSet还是一个set, 我们用来装已经visit过得item +- 如果window大小超过K, 那么把nums[i - k - 1] 去掉, 并且加上新的element +- 这里有个公式推算: (Math.abs(A-B) <= t) =>>>>> (-t <= A - B <= t) =>>>>>> A >= B - t, A <= B + t +- 也就是说, 如果对于 B = nums[i], 来说, 能找到一个target A, 满足上面的公式, 那么就可以 return true. +- Time O(nLogk), treeSet的大小不会超过k, 而 treeSet.ceiling(), treeSet.add(), treeSet.remove() 都是 O(logK) +- Space O(k) + +#### Note +- 与Contains Duplicate II 类似概念. TreeSet有BST 因此可以直接用, 而不用自己构建BST +- 简化题目里面的重要条件 Math.abs(A-B) <= t 而推断出 A >= B - t, A <= B + t +- 并且需要需要用 TreeSet.ceiling(x): return number greater or equal to x. 这个用法要记住吧, 没别的捷径. + + + +--- + +**188. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**189. [Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)** Level: Easy Tags: [Brainteaser, DP, Game Theory] + + +#### Brainteaser +- 著名Nim游戏 +- 写一些,发现n=4,5,6,7,8...etc之后的情况有规律性: 谁先手拿到4就输了. +- 最终很简单n%4!=0就可以了, time, space O(1) + +#### DP +- 正规地找规律做, 就跟 coins in a line 一样, 按照先手后手来做 +- 可以rolling array 优化空间 +- Time O(n), 当然啦, 这个题目这样会timeout, 可以使用brainteaser的做法写出结果. + + + +--- + +**190. [Convert Integer A to Integer B.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Integer%20A%20to%20Integer%20B.java)** Level: Easy Tags: [Bit Manipulation] + + +把Integer A 转换成 Integer B 需要改变多少bits? + +#### Bit Manipulation +- a^b 显示出bit format里面有不同binary code的数位. +- 每次 (a^b)>>i 移动i位之后, 再 & 1时其实是指留下这一位的数字. +- count +- 其实用到了 ^ 找不同的bit, >> 移位, &1 mask + + + +--- + +**191. [Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)** Level: Easy Tags: [Basic Implementation] + + +根据 Cosine Similarity 的公式, basic implementation + + + +--- + +**192. [Count 1 in Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%201%20in%20Binary.java)** Level: Easy Tags: [Bit Manipulation] + + +count 一个 32-bit number binary format 里面有多少1 + +#### Bit Manipulation +- shift >> i +- apply mask & 1 + +#### Convert to string O(n) space +可以把integer -> string -> char array. + + + +--- + +**193. [Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)** Level: Easy Tags: [Basic Implementation, String] + + +介绍一种count数字的方法, 然后每一行读出上一行的结果, 一行一行推算. 问nth行是啥样? + +#### Basic Implementation +- 主要是题意很难理解, 非常misleading, 等到看明白题目, 其实没有什么算法要求. +- Count duplicates and print + + + +--- + +**194. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**195. [Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)** Level: Medium Tags: [Array, DP, Greedy] + + +给出步数,看能不能jump to end. + +#### Greedy - start from index = 0 +- Keep track of farest can go +- 一旦 farest >= nums.length - 1, 也就是到了头, 就可以停止, return true. +- 一旦 farest <= i, 也就是说, 在i点上, 已经走过了步数, 不能再往前跳, 于是 return false +- This can be done using DP. However, greedy algorithm is fast in this particular problem. + +#### Greedy - start from index = n - 1 +- greedy: start from end, and mark last index +- loop from i = [n - 2 -> 0], where i + nums[i] should always >= last index +- check if last == 0 when returning. It means: can we jump from index=0 to the end? +- Time: O(n), beat 100% + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (A[j] >= i - j), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- Time: O(n^2) + + + + +--- + +**196. [Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change%202.java)** Level: Medium Tags: [Backpack DP, DP] + + +给串数字, target amount, 求总共多少种方式可以reach the amount. + +#### DP +- O(MN): M, total target amount; N: size of coins +- 类似于: 网格dp, unique path 里面的2种走法: 从上到下, 从左到右 +- 状态: dp[i]: sum of ways that coins can add up to i. +- Function: dp[j] += dp[j - coins[i]]; +- Init: dp[0] = 1 for ease of calculation; other dp[i] = 0 by default +- note: 避免重复count, 所以 j = coins[i] as start +- 注意 coins 需要放在for loop 外面, 主导换coin的流程, 每个coin可以用无数次, 所以在每一个sum value上都尝试用一次每个coin + +#### knapsack problem: backpack problem + + + +--- + +**197. [Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + +time: O(nm), m = # of colors +space: O(nm) + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, 但是不知道最后一个房子选什么颜色, 那么就遍历最后一个房子(i - 1)的颜色 +- 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- 考虑DP最后一个位置的情况(颜色选择):需要附带颜色status在DP[i]上: 定义二维数组, 其中一位是status +- dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. +- dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- 然后选ith 房子的 color, 再选(i-1)th 房子的color. Double for loop, skip same color + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 + + + +--- + +**198. [Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Partition DP +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- 确定末尾的2种状态: single letter or combos. 然后计算出单个letter的情况, 和双数的情况 +- 定义`dp[i] = 前i个digits最多有多少种decode的方法`. new dp[n + 1]. +- 加法原理: 把不同的情况, single-digit, double-digit 的情况加起来 +- dp[i] += dp[i - x], where x = 1, 2 +- note: calculate number from characters, need to - '0' to get the correct integer mapping. +- 注意: check value != '0', 因为'0' 不在条件之中(A-Z) +- Space, Time O(n) + +#### 引申 +- 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + + + +--- + +**199. [Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +找连续的持续上升子序列的长度. + +#### Coordinate DP +- 1D coordinate, dp 的角标, 就是代表 index i 的状态 +- 求最值, dp[i] = 在index i位置的最长子序列 +- 如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1 +- 如果没有持续上升, 那么dp[i] = 1, 重头来过 +- maintain max + +#### Basic +- 用一个数存current count, maintain max + + + +--- + +**200. [Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +#### DP +- Time, Space O(MN) +- 往右下角走, 计算最短的 path sum. 典型的坐标型. +- 注意: init 第一行的时候, 要accumulate dp[0][j - 1] + grid[i][j], 而不是单纯assign grid[i][j] + +#### Rolling Array +- Time O(MN), Space O(1) +- 需要在同一个for loop里面完成initialization, 和使用dp[i][j] +- 原因: dp[i % 2][j] 在被计算出来的时候, 是几乎马上在下一轮是要被用的; 被覆盖前不备用,就白算 +- 如果按照第一种方法, 在开始initialize dp, 看起来固然简单, 但是不方便空间优化 + + + +--- + +**201. [Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)** Level: Medium Tags: [Bit Manipulation, Bitwise DP, DP] + + +给一个数组, 算里面有多少bit 1. + +#### Bitwise DP +- 对于每一个数字, 其实很简单就能算出来: 每次 >>1, 然后 & 1 就可以count 1s. Time: 一个数字可以 >>1 O(logN) 次 +- 现在要对[0 ~ num] 都计算, 也就是N个数字, 时间复杂度: O(nLogN). +- 用DP来优化, 查找过的number的1s count, 存下来在 dp[number]里面. +- 计算你顺序从 0 -> num, count过的数字就可以重复利用. +- Bit题目 用num的数值本身表示DP的状态. +- 这里, dp[i] 并不是和 dp[i-1]有逻辑关系; 而是dp[i] 和dp[i>>1], 从binary representation看出有直接关系. + + + +--- + +**202. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**203. [House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) or rolling array O(1) + +搜刮房子, 相邻的不能碰. 每个房子里有value, 求max. + +#### Sequence DP +- dp[i]: 前i个房子拿到的max gain +- 看最后结尾状态的前一个或前两个的情况,再综合考虑当下的 +- 搞清楚当下[i]的和之前[i-x]的情况的关系: 不可以连着house, 那么就直接考虑 dp[i-2]的情况 +- Sequence DP, new dp[n + 1]; + +#### Rolling Array +- [i]'只和前两个位子 [i-1], [i - 2]'相关 +- 用%2来标记 [i], [i - 1], [i - 2]三个位置. +- 其他滚动时惯用curr/prev来表示坐标, 这里%2虽然抽象, 但是更加实用. + + + + +--- + +**204. [House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)** Level: Medium Tags: [DP, Sequence DP, Status DP] + + +和House Robber I 类似, 搜刮房子, 相邻不能动. 特点是: 现在nums排成了圈, 首尾相连. + +#### Sequence DP +- dp[i][status]: 在 status=[0,1] 情况下, 前i个 房子拿到的 max rob gain. status=0, 1st house robbed; status=1, 1st house skipped +- 根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]); +- 特别的是,末尾的last house 和 first house相连. 这里就需要分别讨论两种情况: 第一个房子被搜刮, 或者第一个房子没被搜刮 +- be careful with edge case nums = [0], only with 1 element. +- Time,space: O(n) + +#### 两个状态 +- 是否搜刮了第一个房子, 分出两个branch, 可以看做两种状态. +- 可以考虑用两个DP array; 也可以加一dp维度, 补充这个状态. +- 连个维度表示的是2种状态(1st house being robbed or not); 这两种状态是平行世界的两种状态, 互不相关. + +#### Rolling array +- 与House Robber I一样, 可以用%2 来操作rolling array, space reduced to O(1) + + + +--- + +**205. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**206. [Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + +time: O(NK^2): +space: (NK) + +一排n个房子, 每个房子可涂成k种颜色, 涂每个房子的价钱不一样, 用costs[][]表示. + +costs[0][1]表示涂了index是0的房子, 用了color 1. + +规则: 相邻的两个房子不能使同一种颜色 + +求: 最少的cost + +#### DP +- 跟Paint House I 几乎一模一样, 只不过paint color更多了: k colors. +- 先考虑单纯地用dp[i]表示涂前 i 个房子的最小cost +- 但是 dp[i] 和 dp[i-1] 两个index选什么颜色会互相影响, 难讨论, 于是加状态: 序列DP被加了状态变成2D. +- 考虑最后位, 而前一位i-1又被i位的颜色限制, 于是在考虑 min dp[i] 时候, 又多了一层iteration. +- 做dp[i][j]: # cost for 前 i 个房子, 所以要先pick (i-1) 房子的cost, 然后在找出 (i-2)房子的cost +- K种颜色 => O(NK^2) +- 如果不优化, 跟Paint House I 几乎是一模一样的代码 +- Time O(NK^2), space(NK) +- Rolling array: reduce space to O(K) + +#### 注意 +- 序列型dp[i]表示'前i-1个'的结果. 所以dp最好设定为 int[n + 1] size. +- 然而, 颜色在这里是状态, 所以保留在 j: [ 0~k) +- [[8]] 这样的edge case. 跑不进for loop, 所以特殊handle. + +#### Optimization Solution +- Time: O(NK) +- 如果已知每次都要从cost里面选两个不同的最小cost,那么先把最小两个挑出来, 就不必有第三个for loop 找 min +- 每次在数列里面找: 除去自己之外的最小值, 利用最小值/次小值的思想 +- 维持2个最值: 最小值/次小值. +- 计算的时候, 如果除掉的不是最小值的index, 就给出最小值; 如果除掉的是最小值的index, 就给出次小值. +- Every loop: 1. calculate the two min vlaues for each i; 2. calcualte dp[i][j] +- 如何想到优化: 把表达式写出来, 然后看哪里可以优化 +- 另外, 还是可以rolling array, reduce space complexity to O(K) + + + +--- + +**207. [Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)** Level: Hard Tags: [Array, DP, Sequence DP] + + +比stock II 多了一个限制:只有2次卖出机会. + +#### DP加状态 +- 只卖2次, 把买卖分割成5个状态模块. +- 在状态index 0, 2, 4: 没有持有股票. 1. 一直在此状态, max profit不变; 2. 刚卖掉, dp[i][前状态] + profit +- 在状态index 1, 3: 持有股票. 1. 一直在此状态, daily profit. 2. 刚刚买进, 状态改变, 但是没有profit yet: dp[i][前状态] + +##### Partial profit +- 把每天的partial profit (diff)加在一起, 最终的overall profit是一样的. 唯一更好的是, 不需要记录中间买入的时间点. +- 什么时候会积累profit呢? +- 1. 原本就持有股票的, 如果毫无动作, 那么状态不变, 积累profit diff. +- 2. 卖出了股票, 状态改变, 积累profit diff. +- 注意: 只有在状态index: 0, 2, 4, 也就是卖掉股票的时候, 才可以积累profit + +##### Rolling Array +- [i] 只有和 [i-1] 打交道, reduce space +- O(1) space, O(n) time + +#### 找峰头 +- 找峰头;然后往下再找一个峰头。 +- 怎么样在才能Optimize两次巅峰呢?从两边同时开始找Max!(棒棒的想法) +- leftProfit是从左往右,每个i点上的最大Profit。 +- rightProfit是从i点开始到结尾,每个点上的最大profit. +- 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 +- 三个O(n),还是O(n) + + + +--- + +**208. [Best Time to Buy and Sell Stock IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20IV.java)** Level: Hard Tags: [DP, Sequence DP] + + +有int[] price of stock, 最多做 k transactions. 求最大profit. + +#### DP +- 根据StockIII, 不难发现StockIV就是把状态划分为2k+1份. 那么同样的代码, 移植. + +##### 注意1: +- 如果k很大, k>n/2, 那么长度为n的数组里面, 最多也只能n/2个transaction +- 那么题目简化为stockII, 给n数组, 无限次transaction. +- 注意, status的数量是 2k+1 +- Time O(NK), Space O(2k+1) to store the status + +##### 注意2: +- 最后状态是'没有stock'的都该考虑, 做一个 for 循环比较max. +- 当然, 来一个profit variable, 不断比较, 也是可以的. + +#### 方法2 +- (previous notes, 熟练第一种方法的思考就可以) +- 记得要理解:为什么 i-1天的卖了又买,可以和第 i 天的卖合成一次交易? +- 因为每天交易的price是定的。所以卖了又买,等于没卖!这就是可以合并的原因。要对价格敏感啊少年。 +- Inspired from here: http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html + +##### 局部最优解 vs. 全局最优解: +- local[i][j] = max(global[i – 1][j – 1] + diff, local[i – 1][j] + diff) +- global[i][j] = max(global[i – 1][j], local[i][j]) +- local[i][j]: 第i天,当天一定进行第j次交易的profit +- global[i][j]: 第i天,总共进行了j次交易的profit. + +- local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生。 +- 当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff; +- 当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。 +- (Note:在我下面这个solution里面没有省去 +diff) + +- global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况: +- 如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j]; +- 如果第i天有交易(卖出),那么global[i][j]=local[i][j]。 + + + + + +--- + +**209. [Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)** Level: Hard Tags: [Binary Search, Coordinate DP, DP] + + +俄罗斯套娃, 这里用envelope来表现. 给一串array, 每一个[x, y] 是envelope 长宽. [[5,4],[6,4],[6,7],[2,3]]. + +看用这些套娃, 可以最多套几个. + +#### DP: 1D Coordinate +- envelopes没有顺序, 先排序 (主要根据第一个index排序) +- 然后观察: 排序过后, 就变成了1D的坐标动态规划. +- max number 取决于上一个成功Russian doll的 max value + 1 +- 上一个index不知道, 所以遍历找上一个index. +- 当下index i 的状态, 取决于前面index j 的状态, 所以遍历两个index. +- O(n^2)的DP, n = envelopes.length; + +#### DP: 2D Coordinate +- 这个方法是自己想出来的, 但是时间复杂度太大, timeout +- 把envelop标记在2D grid上面, 然后像走机器人一样, 求到最右下角的最大 count max. +- count 当下能存在多少Russian doll +- 两种情况: 当下coordinate 没有target, 当下coordinate有target +- 当下coordinate 没有target: 如同机器人走法, Math.max(dp[i - 1][j], dp[i][j - 1]) +- 当下coordinate 有target: dp[i - 1][j - 1] + dp[i][j] +- timeout: O(n^2), n = largest coordinate. + + + + +--- + +**210. [Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20in%20String.java)** Level: Medium Tags: [Two Pointers] + + +#### Two Pointer +- 如果做s1的permudation, 时间复杂度是O(n!) 肯定不可以 +- 这里用HashTable的做法 (因为26字母, 所以用int[26]简化) 来记录window内的 character count +- 如果window内的character count 相等, 那么就是permudation +- 更进一步优化: 找两个map相互对应, 不如用一个 int[26]: s1对遇到的character做加法, s2对遇到的character做减法 +- two pointer 运用在 n1, n2 的把控; 以及 s2.charAt(i - n1) 这一步 + + + +--- + +**211. [Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations%20II.java)** Level: Medium Tags: [Backtracking] + + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +#### Backtracking +- 排序, +- Mark visited. 通过permutation规律查看是否排出了重复结果 +- 并且要检查上一层recursive时有没有略过重复element +- time O(n!) + +##### 背景1 +- 在recursive call里面有for loop, 每次从i=0开始, 试着在当下list上加上nums里面的每一个。 +- 从i=0开始,所以会依次recursive每一个nums: +- 因此,例如i=2,肯定比i=3先被访问。也就是:取i=2的那个list permutation肯定先排出来。 + +##### 背景2 +- 重复的例子:给出Input[x, y1, y2], 假设y的值是一样的。那么,{x,y1,y2}和{x,y2,y1}是相同结果。 + +##### Note +- 综上,y1肯定比y2先被访问,{x,y1,y2}先出。 紧随其后,在另一个recursive循环里,{x,y2...}y2被先访问,跳过了y1。 +- 重点:规律在此,如果跳过y1,也就是visited[y1] == false, 而num[y2] == num[y1],那么这就是一个重复的结果,没必要做,越过。 +- 结果:那么,我们需要input像{x,y1,y2}这样数值放一起,那么必须排序。 + +#### Non-recursive, manuall swap +- Idea from: https://www.sigmainfy.com/blog/leetcode-permutations-i-and-ii.html +- 用到 sublist sort +- 用 swap function, 在原数组上调节出来新的permutation +- 注意: 每次拿到新的candidate, 都要把没有permutate的数位sort, 然后再开始swap. +- 这是为了确保, [j]和[j-1]在重复时候, 不用重新记录. + +#### Queue +- 给一个visited queue +- 和queue在所有的地方一同populate. +- 然后visited里面存得时visited indexes。 (Not efficient code. check again) + + + +--- + +**212. [Shuffle an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Shuffle%20an%20Array.java)** Level: Medium Tags: [Permutation] + + +像shuffle music 一样, 做一套shuffle array的functions: + +shuffle() 给出random的permutation + +reset() 给出最初的nums + +#### Permutation +- Permutation 实际上就是在list/array/... 上面给元素换位置 +- 硬换位置, 每次换的位置不同, 用random来找到要换的index +- 维持同一个random seed +- O(n) + +##### Note +- compute all permutations 太慢, 不可行. + + + +--- + +**213. [Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Anagrams%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, Sliding Window] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### HashTable +- count character apperance 就想到hashtable +- 注意countS, countP 的技巧: 作比较只需要O(26) +- Overall timeO(n) +- 小心不要用一个int[] count 来技术 查0, 复杂度是O(n) + + + +--- + +**214. [Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Anagrams.java)** Level: Medium Tags: [Hash Table, String] + + +给一串string, return list of list, 把anagram 放在一起. + +#### Hash Table, key 是 character frequency +- 存anagram +- 用 character frequency 来做unique key +- 用固定长度的char[26] arr 存每个字母的frequency; 然后再 new string(arr). +- 因为每个位子上的frequency的变化,就能构建一个unique的string +- O(nk), k = max word length + + +#### Hash Table, key 是 sorted string +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**215. [Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 背包有自己的大小M, 看最多能放多少重量的书? + +#### Backpack DP 1 +- 简单直白的思考 dp[i][m]: 前i本书, 背包大小为M的时候, 最多能装多种的书? +- **注意**: 背包问题, 重量weight一定要是一维. +- dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + A[i - 1]); +- 每一步都track 最大值 +- 最后return dp[n][m] +- 时间空间 O(mn) +- Rolling array, 空间O(m) + +#### Backpack DP 2 +- true/false求解, 稍微曲线救国: 重点是, 最后, 按照weight从大到小遍历, 第一个遇到true的, index就是最大值. +- 考虑: 用i个item (可跳过地取), 是否能装到weight w? +- 需要从'可能性'的角度考虑, 不要搞成单一的最大值问题. +- 1. 背包可装的物品大小和总承重有关. +- 2. 不要去找dp[i]前i个物品的最大总重, 找的不是这个. + dp[i]及时找到可放的最大sum, 但是i+1可能有更好的值, 把dp[i+1]变得更大更合适. + +##### 做法 +- boolean[][] dp[i][j]表示: 有前i个item, 用他们可否组成size为j的背包? true/false. +- (反过来考虑了,不是想是否超过size j, 而是考虑是否能拼出exact size == j) +- **注意**: 虽然dp里面一直存在i的位置, 实际上考虑的是在i位置的时候, 看前i-1个item. + +##### 多项式规律 +- 1. picked A[i-1]: 就是A[i-1]被用过, weight j 应该减去A[i-1]. 那么dp[i][j]就取决于dp[i-1][j-A[i-1]]的结果. +- 2. did not pick A[i-1]: 那就是说, 没用过A[i-1], 那么dp[i][j]就取决于上一行d[i-1][j] +- dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]] + +##### 结尾 +- 跑一遍dp 最下面一个row. 从末尾开始找, 最末尾的一个j (能让dp[i][j] == true)的, 就是最多能装的大小 :) +- 时间,空间都是:O(mn) + + + + +--- + +**216. [Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 每本书有value int[] V + +背包有自己的大小M, 看最多能放多少value的书? + +#### Backpack DP +- 做了Backpack I, 这个就如出一辙, 只不过: dp存的不是max weight, 而是 value的最大值. +- 想法还是,选了A[i - 1] 或者没选A[i - 1]时候不同的value值. +- 时间空间O(mn) +- Rolling Array, 空间O(m) + +#### Previous DP Solution +- 如果无法达到的w, 应该mark as impossible. 一种简单做法是mark as -1 in dp. +- 如果有负数value, 就不能这样, 而是要开一个can[i][w]数组, 也就是backpack I 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**217. [Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)** Level: Medium Tags: [Backpack DP, DP] + + +#### Backpack DP +- 与背包1不同: 这里不是check可能性(OR)或者最多能装的size是多少; 而是计算有多少种正好fill的可能性. +- dp[i][w]: 用前i本书, 正好fill到 w weight的可能性. +- 对于末尾, 还是两种情况: +- 1. i-1位置没有加bag +- 2. i-1位置加了bag +- 两种情况可以fill满w的情况加起来, 就是我们要的结果. +- 如常: dp[n + 1][w + 1] +- 重点: dp[0][0] 表示0本书装满weight=0的包, 这里我们必须 dp[0][0] = 1, 给后面的 dp function 做base +- Space, time: O(MN) +- Rolling array, 空间优化, 滚动数组. Space: O(M) + +#### 降维打击, 终极优化 +- 分析row(i-1)的规律, 发现所有row(i)的值, 都跟row(i-1)的左边element相关, 而右边element是没用的. +- 所以可以被override. +- Space: O(M), 真*一维啊! +- Time: O(MN) + + + +--- + +**218. [Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Primes.java)** Level: Easy Tags: [Hash Table, Math] + + +计数: 所有小于n的prime number. + +#### prime number定义 +- >=2的没有除自己和1以外公约数的数。 +- 还有另外一个定义方法: 这个n,有没有小于n的一个i, 而达到: i * i + # of i = n. 如果有,那就不是 prime + +#### Steps +- 一个boolean长条,存isPrime[]。 然后从i=2, 全部变true. +- hash key: the number itself +- 然后利用这个因子的性质,非prime满足条件: self*self, self * self + self ... etc. +- 所以就check每一个j, j+i, j+i+i, 然后把所有non-prime全部mark成false. +- 最后,数一遍还剩下的true个数就好了 + + + +--- + +**219. [Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Node%20in%20a%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +Given Singlely linked list, 删除一个任意node (不能是head node) + +#### Basic +- update node.val +- Link curr.next to curr.next.next + + + +--- + +**220. [Excel Sheet Column Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Number.java)** Level: Easy Tags: [Math] + + +#### Math +- 26位的运算, 根据10位运算去思考 +- 'A' - 'A' = 0. 所以 char - 'A' + 1 = 题目里的对应数位 +- 或者: 26位运算和10位一样:num += 每位的digit * Math.pow(26, 数位号) + + + + +--- + +**221. [Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Title.java)** Level: Easy Tags: [Math] + + +#### 基本换算 +- 26位 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + + + +--- + +**222. [Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game.java)** Level: Easy Tags: [String] + + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + + + +--- + +**223. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**224. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**225. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**226. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**227. [Evaluate Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Reverse%20Polish%20Notation.java)** Level: Medium Tags: [Stack] + + +给一个 RPN string list, 根据这个list, 计算结果. + +#### Stack +- stack 里面 存数字 +- 每次遇到operator, 都拿前2个数字计算 +- 计算结果存回到stack里面, 方便下一轮使用. +- Time,Space O(n) + + + + +--- + +**228. [Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)** Level: Hard Tags: [DP, Enumeration, Partition DP] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +其中字符可能是 "*", 可以代表 [1 - 9] + +#### DP +- 乘法原理 +- 跟decode way I 一样, 加法原理, 切割点时: 当下是取了 1 digit 还是 2 digits 来decode +- 定义dp[i] = 前i个digits最多有多少种decode的方法. new dp[n + 1]. +- 不同的情况是: 每一个partition里面, 如果有"*", 就会在自身延伸出很多不同的可能 +- 那么: dp[i] = dp[i - 1] * (#variations of ss[i]) + dp[i - 2] * (#variations of ss[i,i+1]) + +##### 特点 +- 枚举的能力: 具体分析 '*' 出现的位置, 枚举出数字, 基本功. +- 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) +- 理解取MOD的原因: 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. +- 枚举好以后, 其实这个题目的写法和思考过程都不难 + + + + +--- + +**229. [Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)** Level: Hard Tags: [DP, Partition DP] + + +给一个String s, 找出最少用多少cut, 使致 切割出的每一个substring, 都是palindrome + +#### Partition DP +- Find minimum cut: 分割型DP +- dp[i]: 最少cut多少刀, 使得前 i 长度的string, 割出来都是palindrome +- 最终要得到 dp[n], 所以 int[n + 1] +- 移动切刀, 看在哪里切, index j in [0 ~ i] +- 考虑[j, i - 1] 是否是回文串, 如果是, 那么: dp[i]= min(dp[i], d[j] + 1). +- note: 估计遍历 j 的时候, 反过来遍历也可以. + +#### 计算Palindrome的优化 +- 利用palindrome的性质, 可以算出 boolean palindrome[i, j]的情况. +- 找一个任意mid point: +- 1. 假设palindrome是奇数长度, 那么 mid 是单独的字符, 而两边的字符 [mid-1], [mid+1] 应该完全相等. +- 2. 假设palindrome是偶数长度, 那么 [mid] 和 [mid + 1] 这样位置的字符应该相等. +- 这样做出来 palindrome[i, j]: 从字符 i 到 字符 j 的 substring 是否是 palindrome +- 这样就给我们的问题合理降维, 目前是time: O(n^2). +- 不然求一次palindrome, 就是n, 会变成O(n^3) + +#### Previous Notes +- Double for loop 检查每种substring string (i~j). 若i,j相邻或者同点,那么肯定isPal;否则,i,j之间的(i+1, j-1)一定得isPal。 +- 看上去,在检查i,j的时候,中间按的(i+1, j-1)怎么可能先知道? 其实不然..在j慢慢长大的时候,所有的0~j的substring都检查过。所以isPal[i+1][j-1]一定是已经知道结果的。 +- okay.那么假如以上任意一种情况成立,也就是说isPal[i][j] == true。那就要判断,切到第一层循环参数j的末尾点时,有多少种切法? +- 想法很顺:我们naturally会想到,把i之前的cut加上i~j之间发生的不就好了。 +- 反正现在j不变,现在就看吧i定在哪里,cut[i - 1]是否更小/最小; 再在cut[i-1]基础上+1就完了。 +- 当然,如果i==0, 而 i~j又是isPal,那没啥好谈的,不必切,0刀。 +- 最终,刷到cut[s.length() - 1] 也就是最后一点。 return的理所应当。 + + + + +--- + +**230. [Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)** Level: Hard Tags: [Backpack DP, DP] + + +给n种不同的物品, int[] A weight, int[] V value, 每种物品可以用无限次 + +问最大多少value可以装进size是 m 的包? + +#### DP +- 可以无限使用物品, 就失去了last i, last unique item的意义: 因为可以重复使用. +- 所以可以转换一个角度: +- 1. 用i **种** 物品, 拼出w, 并且满足题目条件(max value). 这里因为item i可以无限次使用, 所以考虑使用了多少次K. +- 2. K虽然可以无限, 但是也被 k*A[i]所限制: 最大不能超过背包大小. +- dp[i][w]: 前i种物品, fill weight w 的背包, 最大价值是多少. +- dp[i][w] = max {dp[i - 1][w - k*A[i-1]] + kV[i-1]}, k >= 0 +- Time O(nmk) +- 如果k = 0 或者 1, 其实就是 Backpack II: 拿或者不拿 + +#### 优化 +- 优化时间复杂度, 画图发现: +- 所计算的 (dp[i - 1][j - k*A[i - 1]] + k * V[i - 1]) +- 其实跟同一行的 dp[i][j-A[i-1]] 那个格子, 就多出了 V[i-1] +- 所以没必要每次都 loop over k times +- 简化: dp[i][j] 其中一个可能就是: dp[i][j - A[i - 1]] + V[i - 1] +- Time O(mn) + +#### 空间优化到1维数组 +- 根据上一个优化的情况, 画出 2 rows 网格 +- 发现 dp[i][j] 取决于: 1. dp[i - 1][j], 2. dp[i][j - A[i - 1]] +- 其中: dp[i - 1][j] 是上一轮 (i-1) 的结算结果, 一定是已经算好, ready to be used 的 +- 然而, 当我们 i++,j++ 之后, 在之前 row = i - 1, col < j的格子, 全部不需要. +- 降维简化: 只需要留着 weigth 这个 dimension, 而i这个dimension 可以省略: +- (i - 1) row 不过是需要用到之前算出的旧value: 每一轮, j = [0 ~ m], 那么dp[j]本身就有记录旧值的功能. +- 变成1个一位数组 +- 降维优化的重点: 看双行的左右计算方向 +- Time(mn). Space(m) + + + +--- + +**231. [First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Missing%20Positive.java)** Level: Hard Tags: [Array] + + +给一串无序数字, 有负数: 找这个array里面第一个 missing的 positive integer + +missing positive integer 其实是以 [1, n] 来做比较的. + +#### Array分析, index 技巧 +- 用while loop, 不断地尝试把 number 送到该放的地方 +- 如果 index = nums[i] 超过了nums.length, 当然就不移动了 +- 注意: 检查 val != nums[val], avoid infinitely loop +- 检验: nums[i] 是否等于 i, 如果不对, 就找到了结果 + +#### Edge Case +- 如果nums==null, 其实missing positive integer 自然而然是 1 +- validation时, 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) +- 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +- 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + +**232. [Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20strStr().java)** Level: Easy Tags: [String, Two Pointers] + + +给两个string A, B, 找一个 B 在 A 种的起始位置. + +#### Two Pointer +- 找到B在A中的起始位置, 然后看一下从这个点开始的substring是否等于B就可以了 +- 还挺多坑的, 这些可以帮助优化: +- 1. 当B是“”的时候,也就是能在A的其实位置找到B....index = 0. +- 2. edge condition: 如果 haystack.length() < needle.length() 的话, 必须错, return -1 +- 3. 如果在某个index, A后面剩下的长度, 比B的长度短, 也是误解, return -1 + + + +--- + +**233. [Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)** Level: Medium Tags: [Linked List, Sort] + + +input一串数字, 需要出sorted output. 每次insert一个数字时, 都要放到正确的sorted的位置 + +每次insertion的时候, 都从input里面减掉这个数字 + +#### Linked List +- 把list里面每个元素都拿出来,scan and insert一遍 +- Time O(n^2), worst case, 每次放入n个数字里面的element, 刚好都是最大的 +- 所以每次要traverse n nodes, 然后走n次 + +##### 思考方法 +- 如果已经有个sorted list, insert一个element进去。怎么做? +- while 里面每个元素都小于 curr, keep going +- 一旦curr在某个点小了,加进去当下这个空隙。 + + + +--- + +**234. [Interleaving Positive and Negative Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20Positive%20and%20Negative%20Numbers.java)** Level: Medium Tags: [Two Pointers] + + +给一串数组 有正负数. 重新排列, 让数组里面 正数 和 负数 相隔开. 原来的order无所谓 + +#### Two pointer +- 需要知道正负的位置, 所以排序 O(nlogN) +- 考虑: 正数多还是负数多的问题, 举栗子就看出来端倪了 +- 然后Two Pointer, swap +- Time O(nlogn), space O(n) + +#### extra space +- 用extra O(n) space, 把正负分成两个list +- 然后分别按照index填回去 +- time O(n). space O(n) +- 但是就么有用到Two pointer了 + + + +--- + +**235. [Largest Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number.java)** Level: Medium Tags: [Sort] + + +给一串数字, 非负数, 把所有数字串联起来, 组成最大数字. + +因为结果很大, 所以用string表示 + +#### Sort, Comparator +- 考虑 more significant spot 应该拿到更大的值 +- 如果sort number, comparator 会比较难写: 每个digit的weight不同, 要分别讨论个位数和多位数. +- goal: 让较大的组合数排在前面, 让较小的组合数排在后面 +- 不如: 组合两种情况, 用String比较一下大小 (也可以用 integer来比较组合数, 但是为保险不超Integer.MAX_VALUE, 这里比较String) +- String.compareTo() 是按照 lexicographically, 字典顺序排列的 +- 利用compareTo, 来倒序排列 string, 刚好就得到我们要的结果. +- O(nlogn), 排序 + + + +--- + +**236. [Last Position of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Last%20Position%20of%20Target.java)** Level: Easy Tags: [Binary Search] + + +给一个sorted integer array, 找target出现的最后的index. array 里有重复数字 + +有重复,不是末尾点,继续binary search + + + +--- + +**237. [Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/Length%20of%20Last%20Word.java)** Level: Easy Tags: [String] + + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + + + +--- + +**238. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**239. [Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + +O(n)跑2遍for. +O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. +特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 + + + +--- + +**240. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**241. [N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens.java)** Level: Hard Tags: [Backtracking] + + +N-Queen 问题, 给数字n, 和 nxn board, 找到所有N-queens的答案. + +#### Backtracking +- 用dfs找所有情况, 每一个iteration, 从找一行里挑合适的点, dfs +- 选中的点加进candidate list 里面, 记得要backtracking. +- 每一个candidate都需要validation, 检查 row, col, 2 diagnal 有没有queen + +#### validate n queue at certain (x, y) +- 1. array 里面不能有 target row# +- 2. diagnal. 记得公式: +- row1 - row2 == col1 - col2. Diagnal elelment.fail +- row1 - row2 == - (col1 - col2). Diagnal element. fail +- Draw a 3x3 board to test the 2 scanarios: +- (0,0) and (3,3) are diagnal +- (0,2) and (2,0) are diagnal + + + + +--- + +**242. [N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens%20II.java)** Level: Hard Tags: [Backtracking] + + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + + + +--- + +**243. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**244. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**245. [Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)** Level: Easy Tags: [Array, Quick Select, Quick Sort] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- quickSelect 可以找到 kth 最小的元素 +- 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- quick select 的template要熟悉一下, 一下子可能想得到, 但写不出来 +- 主要步骤: partition, dfs, only recur on one part of the array + + + + +--- + +**246. [Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Middle%20of%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +找Linked List的中间node + +- 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + + + +--- + +**247. [Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)** Level: Easy Tags: [Design] + + +让一个class 是 singleton + + + +--- + +**248. [Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Linked%20List%20Elements.java)** Level: Easy Tags: [Linked List] + + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + + + +--- + +**249. [Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)** Level: Easy Tags: [DP, Math, Memoization] + + +#### Memoization +- fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + +#### DP array. +- 滚动数组, 简化DP + +#### recursively calculate +- recursively calculate fib(n - 1) + fib(n - 2). 公式没问题, 但是时间太长, timeout. + + + + +--- + +**250. [Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单, 但是要在Linkde List random access坐标, 是很难得: 所以需要把一半 ListNode 翻转 +- reverse linked list: 遍历接开头 +- 用快慢指正找到mid point +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + +#### Previous Note +- Palindrome都是要两边回溯相等 +- linkedlist不能reverse iterating, 那么就reverse the list, 从中间开花作比较。 + + + +--- + +**251. [Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +#### Reverse List +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + + + +--- + +**252. [Reverse Linked List II .java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List%20II%20.java)** Level: Medium Tags: [Linked List] + + +reverse 一个 linked list 中 [m ~ n] 的一部分. + +#### Reverse linked list +- 在基本的reverse linked list 上面 多了一层: 找到front node, 接下来的 [m ~ n] node 需要被reverse +- 只需要reverse中间的部分. +- Reverse的时候: 用一个dummyNode, 这道题里面, 其实就用 nodeFront, 那么 dummy.next 就是整个reversed list. + +##### 注意 +- 一定要Mark开头的那个mth node, 最后用它接上 剩下node tail. 不然后面的node会断掉 + +#### Previous notes +- 遍历到M前, +- 存一下那个点, +- 从M开始, for loop, reverse [m~n]。 然后把三段链接在一起。 + + + + +--- + +**253. [Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Linked%20Lists.java)** Level: Easy Tags: [Linked List] + + +给两个 linked list, 问从哪个node开始, 两个 linked list 开始有重复? + +#### Basics +- 长短list,找重合点 +- 长度不同的话,切掉长的list那个的extra length +- 那么起点一样后,重合点就会同时到达 +- Time O(n) * 2, constant space + + + +--- + +**254. [Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation.java)** Level: Easy Tags: [Hash Table] + + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count occurrance +- 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assum lower case letter. 应该至少是所有ASCII code + + + +--- + +**255. [Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Check Palindrome +- 前后两个指针, 往中间移动, 查看是否字母重合 + +#### 过滤 alphanumeric +- 可以用 ASCII code 来手动过滤, 只要 '0' ~ '9', 'a' ~ 'z', 'A' - 'Z' 之间的 +- 也可以用 regular expression: match 所有这些字母, 是 [a-zA-Z0-9] +- 那凡是不是这些字母的 match, 就是取反: "[^a-zA-Z0-9]". 测试: https://regex101.com/ + + + +--- + +**256. [Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)** Level: Easy Tags: [Design, Stack] + + +如题. + +#### Queue, 倒水 +- 两个Queue,交互倒水 +- 用一个Temp做swap + +##### 做法1 +- 逻辑在push里面: +- 1. x 放q2。 +- 2. q1全部offer/append到q2. +- 3. 用一个Temp做swap q1, q2. +- q1的头,就一直是最后加进去的值. + + +##### 做法2 +- 逻辑在top()/pop()里, 每次换水,查看末尾项. + + + + +--- + +**257. [Implement Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack.java)** Level: Easy Tags: [Stack] + + +随便用一个data structure, implement stack. + +#### Stack, 先入, 后出 +- ArrayList: return/remove ArrayList的末尾项。 +- 2 Queues + + + +--- + +**258. [Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### DFS +- 简单处理swap +- recursively swap children + +#### BFS +- BFS with Queue +- 每次process一个node, swap children; 然后把child加进queue里面 +- 直到queue process完 + + + +--- + +**259. [Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree, 找最深depth + +#### DFS +- 这里要走过所有的node, 所以dfs非常合适 +- Divide and conquer. +- 维持一个最大值: Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +- 注意check root == null + +#### Note +- BFS is doable as well, but a bit more code to write: tracks largest level we reach + + + +--- + +**260. [Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### BFS +- Shortest path; minimum depth: 想到BFS, check level by level, BFS更能确保更快找到结果 +- depth definition: reach to a leaf node, where node.left == null && node.right == null +- BFS using queue, track level. + + +#### DFS +- Divide and Conquery一个最小值. +- 注意处理Leaf的null: null leaf 出现的时候, 就忽略这个leaf, 直接return算有leaf +- 另一种count的方法: 用Integer.MAX_VALUE代替 null leaf,这样可以避免错误counting. (不能直接recursive) +- 这个无论如何都要走所有node, 所以dfs应该比较适合. + + + + +--- + +**261. [Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**262. [Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +检查binary tree是否 identical. + +特点: subtree如果是有旋转的, 只要tree node value相等, 就可以算是identical + +#### DFS +- 在DFS的基础上, 比对左左,左右,右左,右右 + + + +--- + +**263. [Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)** Level: Easy Tags: [DFS, Tree] + + +#### DFS +- 基础binary tree traversal. 注意对null child的判断 + + + +--- + +**264. [Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree s, 和一个binary tree t, 检查t是不是s的subtree. + +#### DFS +- 跟 identical binary tree的写法很像 +- 只有 current s.val = t.val 的时候才需要compare same tree. +- 其他情况, 继续recursively isSubtree +- 注意:即使找到T1 == T2, 但很可能只是数字相同(这里不是binary search tree!!), 而children不同 +- 所以同时要继续recursively isSubtree(T1.left, T2) ...etc. + + + +--- + +**265. [Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个Binary Tree root, 以及两个node p, q. 找 p 和 q 的 lowest common ancestor + +#### DFS +- 因为是 binary tree, 所以直接盲目搜索搜索path不efficient, use extra space and waste time +- 巧用DFS来找每一个node的common ancestor. +- Need the assumption: 1. unique nodes across tree; 2. must have a solution +- 当root == null或者 p q 任何一个在findLCA底部被找到了(root== A || root == B),那么就return 这个root. +- 三种情况: +- 1. A,B都找到,那么这个level的node就是其中一层的ancestor: 其实,最先recursively return到的那个,就是最底的LCA parent. +- 2. A 或者 B 找到,那就还没有公共parent, return 非null得那个。 +- 3. A B 都null, 那就找错了没有呗, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time/space O(n) + + + +--- + +**266. [Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Tree] + + +给一个Binary Tree root, 以及两个node A, B. 特点: node里面存了parent pointer. 找 lowest common ancestor + + +#### Hash Set +- 这个题有个奇葩的地方, 每个node还有一个parent, 所以可以自底向上. +- save visited in hashset. 第一个duplicate, 就是A B 的 lowest common ancestor + +#### Save in lists +- 自底向上。利用parent往root方向返回 +- 把所有parent存下来, 然后在两个list里面找最后一个 common node + +#### 注意 +- 无法从root去直接搜target node 而做成两个list. 因为根本不是Binary Search Tree! + + + + +--- + +**267. [Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的list +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Brutly寻找p和q的common ancestor, 然后recursively drive left/right +- 非常巧妙, 但是也比较局限; 稍微变条件, 就很难recursive. +- 几种情况: +- 1. one of p, q 在leaf, 那么此时的root其实就是lowest common ancestor +- 2. 如果p, q 在root的左右两边, 这就是分叉口, 那么root就是lowest common ancestor +- 3. 如果p,q 在root的同一边 (左,右), 那么继续dfs +- O(1) extra space, O(logn) time + + + +--- + +**268. [Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/Hash%20Function.java)** Level: Easy Tags: [Hash Table] + + +#### Hash Function +- 解释Hash怎么做. +- Hash function例子: +- hashcode("abcd") = (ascii(a) * 33^3 + ascii(b) * 33^2 + ascii(c) *33^1 + ascii(d)*33^0) % HASH_SIZE +- 用到的参数比如: magic number 33, HASH_SIZE. + +- Hash的意义是:给一个string key, 转换成数字,从而把size变得更小。 +- 真实的implementation还要处理collision, 可能需要design hash function 等等。 + + +##### 每一步都%HASH_SIZE的原因 +- hashRst = hashRst * 33 + (int)(key[i]); +- hashRst = hashRst % HASH_SIZE; +- 原因是,hashRst会变得太大,所以不能算完和 再 %... + + + +--- + +**269. [Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Sorted%20Lists.java)** Level: Easy Tags: [Linked List] + + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + + + +--- + +**270. [Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)** Level: Easy Tags: [Array, Bit Manipulation, Math] + + +给一串unique数字, 数字取自 [0 ~ n], 无序, 找第一个skipped的数字. + +#### Swap +- 跟First Missing Positive 非常像, 只有一行代码的区别. +- swap 所有的数字, 到自己的correct position +- 最后一个for loop找到错位的index, 也就是缺的数字. + +#### Bit Manipulation +- XOR will only retain bits that are different 1 ^ 0 = 1, but 0^0, 1^1 == 0 +- Use that feature, 把所有value都和index XOR了 +- 剩下的多余的数字, 其实是那个index无法被XOR消掉, 也就是那个缺的number value. +- 注意: 题目告诉数字是 [0 ~ n], 然而缺一个数字, 那么在[0 ~ n - 1] 里面, 最大的数字(不管缺没缺), 一定是 n = nums.length. + +#### HastSet +- 全存, 找missing +- O(n) space, 不合题意 + +#### sorting +- sort, 找1st missing +- O(n log n) 太慢, 不合题意 + + + +--- + +**271. [LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)** Level: Hard Tags: [Design, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度。 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) + +##### 巧妙点 +- 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +- 2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +- 直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法: +- moveToHead() +- insertHead() +- remove() + +#### O(n) 检查重复 +- timeout method, 天真的来了一个O(n) 的解法,结果果然timeout. +- 一个map存数值。一个queue来存排位。 +- 每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + + + + +--- + +**272. [Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- Two pointers 其实也可以是一个 for loop pointer, 另一个 dynamic variable. +- track unique index +- skip duplicated items +- O(n) + +#### 思考模式: +- Remove Duplicate from Array 不同于remove from linked list. +- LinkedList里面我们是最好不要动node.val的,直接把node去掉。 +- 而array我们很难直接把node去掉,又不能用新array,那么就要: +- 把不重复的element一个个放到最前面。 +- 这个思想跟merge two sorted array (其中一个后续非常长的array可以放下arr1,arr2) 类似。 +- 就是找个不会事后mess up,不会去动得index,把满足条件的element 填进去。这样保证了in place. +- *反向思维*:remove duplicate, 实际上也是找unique elements, and insert into original array + + + +--- + +**273. [Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- 跟 `Remove Duplicates from Sorted Array` 几乎一模一样, 只不过unique index现在可以 validate 2 位 +- 其余一模一样, use index to track unique item; skip if duplicated for more than 2 times +- O(n) time, O(1) space +- 这里也可以真的用2个pointers 写while loop, 但是没有必要, 只是单纯地走一个for loop其实就足够. + + + +--- + +**274. [Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List.java)** Level: Easy Tags: [Linked List] + + +从Linked list 里面摘掉重复元素, 只留下unique元素. + +#### Linked List +- sorted list, 重复元素都在一起 +- 知道如何构建Linked List. +- 一点遇到重复元素: node.val == node.next.val, 就去掉. +- 用一个dummy node 来跑路 +- 注意: +- 只有当没有重复的时候, 才node = node.next; +- 有重复的时候, 当后面第三个元素被提上来之后, 还是可能跟当下元素重复, 所以不能前移node. +- ex: A -> A -> A +- while loop 里面check node 和 node.next 比较好, 这样ending position会非常清晰 + + + +--- + +**275. [Remove Duplicates from Sorted List II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List%20II.java)** Level: Medium Tags: [Linked List] + + +从Linked list 里面摘掉重复元素: 只要重复过, 全部都删掉; 重复出现过得元素一个不留. + +#### Linked List +- sorted list, 重复元素都在一起 +- 运用 dummyHead: 如果要去掉所有重复元素, 就要有个dummyHead作为局外人在开头牵线 +- 只要发现一个 node.val == node.next.val, 就记下这个duplicated val, move forward, 过掉所有重复过的元素 +- 思想: +- 用第二个 inner while loop, 把所有的重复元素都处理干净, 然后再move forward +- 优点: outter while loop 不需要考虑太多case, 在inner loop 都把主要的business logic 解决了. + +##### 注意DummyHead 的使用 +- 当我们有了DummyHead 作为Linked List 的局外线头, 其实可以选择每次遇到duplicate, 就把更加后面的元素 强行assign 给 dummyHead.next +- 下面还尝试过一种做法: 但是需要考虑的edge case 太多了: 不断移动node, 知道不重复, assign prev.next = node. +- 这样的做法比较直白, 但是需要考虑很多edge case, 而且并没有很好利用到 dummy head, 注意规避. + +##### Previous Note +- 斩草除根。 +- 多个node,check node.next ?= node.next.next + + + + +--- + +**276. [QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)** Level: Medium Tags: [Quick Sort, Sort] + + +implement quick sort. + +#### Quick Sort +- 首先partition. 返还一个partition的那个中间点的位置: 这个时候, 所有小于nums[partitionIndex] 都应该在 partitionIndex左边 +- 然后劈开两半 +- 前后各自 quick sort, recursively +- 注意:在partition里面, 比较的时候nums[start] < pivot, nums[end]>pivot, 如果写成了 <= 会 stack overflow. +- Time O(nlogn), Space: O(1) + + + +--- + +**277. [MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/MergeSort.java)** Level: Medium Tags: [Merge Sort, Sort] + + +#### Merge Sort +- Divide and conquer, recursively +- 先从中间分段, merge sort 左边 (dfs), merge sort 右边 +- 最后merge起来 +- merge的时候因为是做int[], 所以没办法必须要O(n) space +- Time O(nlogn), Space O(n) + + + +--- + +**278. [Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序, 排序以后才能逐个看是否partial string已经存在 +- 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 +- 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: 1. 长 string 排在前; 2. 相等length, 按照dictionary order 排序 +- 全部放入Trie. Trie.insert() +- 针对sorted words array, 从最长的开始找 Trie.startWith. +- 一旦找到, 就是符合题意的, 直接return. +- 注意: startWith 必须每一个node都是 isEnd, 才满足'逐个字母拼出' 的条件. +- Time: build Trie O(mn) + sort:O(nlogn) => O(nlogn) +- Space: O(mn) + +#### +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**279. [Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**280. [Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)** Level: Medium Tags: [BFS, Tree] + + +如题, 但是output要倒序. + +#### BFS +- 跟Binary Tree Level Order Traversal一样,只不过存result一直存在存在0位. + + +#### DFS +- 根据level来append每个list +- rst里面add(0,...)每次都add在list开头 + + + +--- + +**281. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**282. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + +**283. [Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum.java)** Level: Easy Tags: [DFS, Tree] + + +给一个inputSum, 然后dfs, 找到是否有一条path, 得出的path sum 跟 inputSum 一样. + +#### DFS +- 确定好结尾的条件: is leaf && val == sum +- 每一层减掉node.val, 然后dfs. +- 写一写: root == null => false 对parent nodes的影响. 这里发现没影响, 所以可以简化成用1个functionDFS. + + + + +--- + +**284. [Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)** Level: Easy Tags: [Backtracking, DFS, Tree] + + +给一个inputSum, 然后dfs, 找到所有path, 满足: path sum 跟 inputSum 一样. + +#### DFS, Backtracking +- 用remaining sum 来检测是否满足 input path sum 条件 +- 满足的时候add to result list +- 两种backtracking: +- 1. backtrack 当下node, 加进list, 然后dfs. dfs结束后删掉之前加进去的元素. 非常clean. +- 2. backtrack 下一个dfs level增加的value. dfs return 之后, 删掉list里面的末尾元素: 但是删掉的dfs余下的value. +- 第一种backtrack更加好掌握. + +#### Previous Notes: +- Binary Tree的一个基本题: 找到所有满足条件的path +- 遍历到底,比较sum vs. target +- 注意divide的情况。要把遍历的例子写写 + + + +--- + +**285. [Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)** Level: Easy Tags: [DFS, Double Recursive, Tree] + + +count所有存在的 path sum == target sum. 可以从任意点开始. 但是只能parent -> child . + +#### DFS +- 对所给的input sum 做减法, 知道 sum 达到一个目标值截止 +- 因为可以从任意点开始, 所以当sum达标时候, 需要继续recursive, 从而找到所有情况 (有正负数, sum可能继续增加/减少) +- 经典的 helper dfs recursive + self recursive +- 1. helper dfs recursive 处理包括root的情况 +- 2. self recursive 来引领 skip root的情况. + +#### 特点 +- 与 `Binary Tree Longest Consecutive Sequence II` 在recursive的做法上很相似: +- 利用dfs做包括root的recursive computation +- 利用这个function自己, 做`不包括root的recursive computation` + + + +--- + +**286. [Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20String.java)** Level: Easy Tags: [String] + + +给两个String, 看A rotate之后 能不能变成B + +#### LeetCode +- Basics +- StringBuffer.deleteCharAt(xx), StringBuffer.append(xx) +- O(n) + + +#### LintCode +- Different problem: 给一个char[], 要rotate offset times. +- *三步rotate* +- 有个坑:offset可能很长,那么要%length,才能得到真正需要rotate的部分。 +- Note: rotate 一个 full length之后,是string 不变 + + + +--- + +**287. [Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)** Level: Medium Tags: [Backtracking, Combination, DFS] + + +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +#### DFS, Backtracking +- for loop, recursive (dfs). +- 每个item用一次, 下一个level dfs(index + 1) +- Combination DFS. 画个图想想. 每次从1~n里面pick一个数字i +- 因为下一层不能重新回去 [0~i]选,所以下一层recursive要从i+1开始选。 + + + +--- + +**288. [Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)** Level: Medium Tags: [Array, Backpack DP, DP] + + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + +#### Backpack DP +- 计数问题, 可以想到DP. 其实就是Backpack VI. +- 从x个数字里面找candidate(可以重复用同一个数字), 来sum up to target. 找: # of ways to form the sequence. +- Backpack VI: 给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法 +- dp[i]: # of ways to build up to target i +- consider last step: 如果上一步取的是 candidate A, 那么就该加到dp[i]: +- dp[i] += dp[i - A] +- 要找overall dp[i], 就做一个for loop: dp[i] = sum{dp[i - num]}, where for (num: nums) +- Time: O(mn). m = size of nums, n = target +- If we optimize dp for loop, 需要Sort nums. O(mlogm). will efficient 如果m是constant或者relatively small. Overall: O(n) + +#### DFS, backtracking +- 尽管思考方式是对的, 但是 times out +- 可以重复使用数字的时候, 比如用1 来拼出 999, 这里用1就可以走999 dfs level, 不efficient + + + +--- + +**289. [Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右:即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list + +#### DFS +- Use Map 来存每一个level的结果 +- dfs function 里, 如果 input depth 不存在, 就add to map. +- dfs function 里面先: dfs(node.right), 然后 dfs(node.left) +- 由于always depth search on right side, 所以map会被right branch populate; 然后才是 leftChild.right + + + + +--- + +**290. [Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)** Level: Medium Tags: [DFS, Tree] + + +找到从max path sum from root. 条件: 至少有一个node. + +#### DFS +- 比Binary Tree Maximum Path Sum I 简单许多. 因为条件给的更多:at least 1 node + have to start from root +- root一定用到 +- 3种情况: curr node, curr+left, curr+right +- 因为一定包括root, 说以从 `dfs(root, sum=0)` 开始, 每个level先加root, sum += root.val + + + +--- + +**291. [Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +给一个single linked list, 右移k steps. k non-negative. + +#### Linked List basics +- 记得用dummy.next来存head. +- 特殊: 这里k可能大于list总长. 写一写linked node 移动的步数, 然后 k = k % n. +- 找到newTail, newHead, 然后利用dummy, 换位子 + + + +--- + +**292. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + +**293. [Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)** Level: Hard Tags: [Array, Hash Table, Union Find] + + +给一串数字, unsorted, 找这串数字里面的连续元素序列长度 (consecutive序列, 是数字连续, 并不是说要按照原order) + +#### HashSet +- 要想看连续元素, 必须要num++, num--这样搜索 +- 1. 需要O(1)找到元素 +- 2. 需要简单快速找到 num - 1, num + 1. +- 如果用min,max开array, 耗费空间 +- 用HashSet来存, 用set.contains() 来查找 num - 1, num + 1 存在与否 +- for loop. O(n) +- 里面的while loop 一般不会有O(n); 一旦O(n), 也意味着set 清零, for loop也不会有更多 inner while 的衍生. +- overall O(n) 时间复杂度 + + +#### Union Find +- 最终是要把相连的元素算一下总长, 其实也就是把元素group起来, 相连的group在一起, 于是想到UnionFind +- 这里用到了一个`int[] size` 来帮助处理 `合并的时候parent是哪个`的问题: 永远往group大的union里去 +- main function 里面, 有一个map来track, 每个元素, 只处理1遍. +- union的内容: current number - 1, current number + 1 +- https://www.jianshu.com/p/e6b955ca208f + +##### 特点 +- Union Find 在index上做好像更加容易 +- 其他union find function: `boolean connected(a,b){return find(a) == find(b)}` + + + +--- + +**294. [Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +找到binary tree 里的最长 consecutive sequence. + +#### DFS +- Divide and Conquer. dfs +- 分开 看左边/右边 +- 如果左边满足连续递增的规则, dfs (depth + 1), 如果不满足规则, dfs(depth = 1) +- 右边也是一样 +- 对结果跟max作比较, return + + + +--- + +**295. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**296. [Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)** Level: Medium Tags: [Basic Implementation, Enumeration, String] + + +给一个时间string"12:09", 用里面的4个integer组合成其他时间string, 目标找最小的next time. + +如果组合出的time string 在input time之前, 默认 + 24 hours. + +#### String +- enumerate all candidates and filter to keep the correct ones +- String.compareTo(string) -> gives lexicographical comparision + + + +--- + +**297. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**298. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**299. [Word Ladder.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder.java)** Level: Medium Tags: [BFS] + + +给一串string[], 需要找shortest distance to change from wordA -> wordB. (限制条件细节见原题) + +#### BFS +- 通常, 给一个graph(这道题可以把beginWord看成一个graph的起始node), 找shortest path用BFS +- 在start string基础上,string的每个字母都遍历所有26个字母 +- visited 过的 从wordList里去掉 +- time: word length m, there can be n candidates => O(mn) +- 但是总是exceed time limit on LeetCode. However, it passes LintCode: +- 原因是 LeetCode给的是list, list.contains(), list.remove() 都是 O(logn) time!!! +- convert to set first. + +#### Trie +- timeout, overkill + + + +--- + +**300. [Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)** Level: Medium Tags: [Design, Hash Table] + + + +给一个string[] dict, 和一个word. + +每个word都可以缩写成固定的abbreviation ``(详细看原题) + +检查input word是否满足unique + +#### HashMap +- 简单算出abbreviatioin +- 检查abbr是否存在; 如果存在, 是不是input word本身. + + + +--- + +**301. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**302. [Ugly Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number.java)** Level: Medium Tags: [Math] + + +LeetCode: 判断数字是否是ugly number. (definition: factor only have 2, 3, 5) + +#### Math +- 看是否可以整除. +- 看整除最终结果是否== 1 + +LintCode: 找kth ugly number, 应该与 Ugly Number II是一样的 + +- 方法1: PriorityQueue排序。用ArrayList check 新的ugly Number是否出现过。 +- 方法1-1:(解释不通,不可取)用PriorityQueue排序。神奇的3,5,7走位:按照题目答案的出发,定了3,5,7以什么规律出现。但是题目并没有特殊表明。 +- 方法2: DP . Not Done yet. + + + + +--- + +**303. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**304. [Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**305. [Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 + + + +--- + +**306. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**307. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**308. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**309. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**310. [ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)** Level: Medium Tags: [Design, Hash Table] + + +#### basic implementation +- 用HashMap, 理解题目规律,因为重复的计算可以被覆盖,所以是个优化题。没有什么太大的悬念和意义. +- 消灭重合点: +- 如果process当下col, 其实要减去过去所有加过的row的交接点。。。 +- 再分析,就是每次碰到row 取一个单点, sumRow += xxx。 +- 然后process当下col时候, sum += colValue * N - sumRow. 就等于把交叉所有row(曾经Process过的row)的点减去了。很方便。 +- 最后read in 是O(P), process也是O(P). + + + + +--- + +**311. [Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Two Pointers +- 木桶理论。盛水的最高取决于最低的那面墙。 +- 左右两墙,往中间跑动。 +- 另:若一面墙已经小于另外一面,就要移动,换掉矮墙(可能下一面更高,或更低) +- 但决不能换掉当下的高墙,因为低墙已经limit的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**312. [Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + +time: O(n) +space: O(1) + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List +- Basic Implementation of copy linked list: +- use node and dummy to hold new list, 遍历head.next .... null. +- Map 在这里用来: 1. avoid creating same node; 2. return the item if existing +- map 的 key全部是old object, 新的key全部是 newly created object +- 每一步都check map里面有没有head. 没有? 加上 +- 每一步都check map里面有没有head.random. 没有? 加上 + + + +--- + +**313. [Encode and Decode Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20Strings.java)** Level: Medium Tags: [String] + + +如题. + +#### String +- 'word.length()#word' 这样encode, 可以避免遇到# +- 基于我们自己定的规律, 在decode的里面不需要过多地去check error input, assume所有input都是规范的. +- decode就是找"#",然后用"#"前的数字截取后面的string. + + + + +--- + +**314. [Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)** Level: Medium Tags: [DFS, Divide and Conquer] + + +如题: Calculate the a^n % b where a, b and n are all 32bit integers. + +#### Divide and Conquer +- a^n可以被拆解成(a*a*a*a....*a), 是乘机形式,而%是可以把每一项都mod一下的。所以就拆开来take mod. +- 这里用个二分的方法,recursively二分下去,直到n/2为0或者1,然后分别对待. +- 注意1: 二分后要conquer,乘积可能大于Integer.MAX_VALUE, 所以用个long. +- 注意2: 要处理n%2==1的情况,二分时候自动省掉了一份 a,要乘一下。 + + + + +--- + +**315. [Find the Connected Component in the Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Connected%20Component%20in%20the%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS] + + +给一个undirected graph, return 所有的component. (这道题找不到了) + +#### BFS +- BFS遍历,把每个node的neighbor都加进来. +- 一定注意要把visit过的node Mark一下。因为curr node也会是别人的neighbor,会无限循环。 +- Component的定义:所有Component内的node必须被串联起来via path (反正这里是undirected, 只要链接上就好) +- 这道题:其实component在input里面都已经给好了,所有能一口气visit到的,全部加进queue里面,他们就是一个component里面的了。 +- 而我们这里不需要判断他们是不是Component + +#### DFS +- DFS 应该也可以 visit all nodes, mark visited. + + + +--- + +**316. [HashWithCustomizedClass(LinkedList).java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithCustomizedClass(LinkedList).java)** Level: Medium Tags: [Hash Table] + + +练习HashMap with customized class. functions: get(), put(), getRandom() + +#### Hash Table +- store map as array: `Entry[] table;` +- store entry as linked list: `public Entry(K key, V value, Entry next)` +- compute hashKey: `Math.abs(key.hashCode()) % this.capacity` +- Handle collision: +- 1. Check if duplicate (matching key), if so, replace and return +- 2. Check through the linked list, find find duplicate (matching key), replace and return. +- 3. If no duplicate, add the entry to the tail +- Find item: compute hashKey -> find linked list -> iterate over list to find a matching key. + + + +--- + +**317. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**318. [Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的sum. + +#### Segment Tree + Binary Search +- 其实是segment tree 每个node上面加个sum +- 记得Segment Tree methods: Build, Query +- Note: 存在SegmentTreeNode里面的是sum. 其他题目可能是min,max,count ... or something else. + + + +--- + +**319. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**320. [Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Prefix.java)** Level: Easy Tags: [String] + + +找一串String里面最长的公共prefix. + +#### Sort, compare string +- Sort O(nlogn) +- first and last string should share common prefix +- 这里假设题目要求的是所有string的公共 prefix, 而不是部分strings + +#### Brutle +- Nested loop, 每一次比较所有string 同位是否相等 +- 相等,append string. 不等,return. +- O(mn) + + + +--- + +**321. [Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element%20II.java)** Level: Medium Tags: [Array] + + +#### Sort + count +- O(nlogN) + +#### Two counters +- O(n), count and track valueA, valueB +- count overall apperance at the end for the two items +- save to result +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +--- + +**322. [Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Linked List +- linked list 不能像partitioin array一样从两边遍历 +- 把小于value的加在前半段, 把 >= value的加在后半段 +- 做法很普通: 建造两个list, midTail pointer, post pointer +- 把满足条件(=x)的数字分别放到两个list里面 +- 记得用dummyNode track head. +- 最终midTail.next = post链接起来。 + + + +--- + +**323. [Peeking Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Peeking%20Iterator.java)** Level: Medium Tags: [Design] + + +#### Use concept pre cache +- 找一个cache来存next()的值, 也就是: next value的值提前存在cache里面 +- 因此peek()的时候, 就可以直接return cache, 而不用做 itt.next() +- 然后每次真的next()的时候, 里取下一个itt.next()维护这个cache + +#### Previous notes +- 再一次理解错题意. peek() 就是头顶,但是不一定是最大值啊。 +- 总是把PEEK想成了最大值,然后用2 STACK做了最大值的cache,练的一手好双stack,可惜错了。 + + + + +--- + +**324. [Rehashing.java](https://github.com/awangdev/LintCode/blob/master/Java/Rehashing.java)** Level: Medium Tags: [Hash Table] + + +给一个Hash Table, 是用 linked list 做的. 问题是: capacity太小, collision太多的情况下, 需要double capacity 然后rehash. + +#### Hash Table +- 明白hashCode() function的意义: 拿到hashKey的时候, 用hashKey%capacity 来做hash code +- hashcode就是hash map里面的index +- 明白collision handling 的方式, 和如何double capacity而rehashing +- 都是基本操作, 概念实现 + + + +--- + +**325. [Reorder List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reorder%20List.java)** Level: Medium Tags: [Linked List] + + +给一个Linked list, reorder: 从head/tail 两个方向 向中间进发, re-order like: one node at a time, + +#### Linked List 功能大集合 +- reverse list, find mid of list, merge two list +- 先find mid, 然后把 mid.next reverse了, 最后merge 两段. +- 注意, 用完mid.next之后, 一定要 mid.next = null, 不然merge会出问题 + + + +--- + +**326. [Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)** Level: Medium Tags: [Backtracking, DFS, String] + + +给一串数字, 检查是否是valid IP, 如果合理, 给出所有valid 的IP组合方式. + +#### Backtracking +- 递归的终点:list.zie() == 3, 解决最后一段 +- 递归在一个index上面, pass s.toCharArray() +- validate string要注意leading '0' +- 注意: 递归的时候可以用一个start/level/index来跑路 +- 但是尽量不要去改变Input source, 会变得非常confusing. +- note: code有点messy, 因为要考虑IP的valid情况 +- 那个'remainValid', 其实是一个对于remain substring的判断优化, 不成立的就不dfs了 + + + +--- + +**327. [Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String.java)** Level: Medium Tags: [String] + + +#### Break by space, then flip +- 结尾不能有空格 +- trim() output +- 如果Input是 ""的话,split以后就啥也没有了 +- 另个题目Reverse Words in String (char[]) 可以in-place, 条件是char[]里面是没有首尾空格. +- Time, Space: O(n) + +#### Other methods +- flip entire string, then flip each individual string (代码有点多, 这道题犯不着) + + + +--- + +**328. [Reverse Words in a String II.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20II.java)** Level: Medium Tags: [String] + + +#### In-place reverse +- reverse用两回. 全局reverse。局部:遇到空格reverse +- 注意ending index: `i == str.length - 1`, 结尾点即使没有' '也要给reverse一下最后一个词 + + + + +--- + +**329. [Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20III.java)** Level: Easy Tags: [String] + + +给一个String, 里面的Word被single space split开来, 目的是reverse里面所有的Word, 但preserve Word 和 space order. + +#### Reverse function +- Reverse Words in a String II 的降级版, 去掉第一个overall reverse就好了 + + + +--- + +**330. [Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)** Level: Medium Tags: [Array, Binary Search] + + +给2D matrix, 每行sorted, 每行的首位都大于上一行的末尾. goal: find target from matrix + +#### 2D matrix 转1D array +- 一行一行是从小到大, sorted, 连续的, 可以看做1D sorted array +- Binary Search + + + +--- + +**331. [Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)** Level: Medium Tags: [Binary Search, Divide and Conquer] + + +给matrix, 每一行sorted, 每一列从上往下sorted, 找target是否存在 + +#### Binary Search +- 根据给定的性质, 其实点选的极端一点: x = 最下面的row, y = 当下一行里面最小的left position. +- (x,y)在左下角 +- 在此情况下, 只能往一个方向运行: 如果小于target, y++; 如果大于target, 那么只能x-- +- 每次操作, 都是删掉一行, 或者一列, 再也不需要回头看 +- `while (x >= 0 && y < col) {}` 确保不会跑脱 +- 同样的方式: 可以从右上角(0, col - 1) 开始, 代码稍微改一改 + +#### Divide and Conquer? +- TODO + + + +--- + +**332. [Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)** Level: Medium Tags: [Array, Binary Search] + + +给sorted array, 有重复数字, 找跟target重合所在的range. + +#### Binary Search +- 2个while loop +- 找first/last occurance +- TODO: Can the code be simplified? + + + + +--- + +**333. [Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)** Level: Medium Tags: [BST, Binary Tree] + + +给一个BST, integer range (k1, k2), 找range 里面所有的integer. + +#### BST +- 等于dfs遍历了所有k1<= x <= k2的x node。 +- dfs left, process root, then dfs right +- 这里, 把 left/right/match的情况全部cover了,然后把k1,k2的边框限制好,中间就全部遍历了。 + + + +--- + +**334. [Merge Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array%20II.java)** Level: Easy Tags: [Array] + + +如题, merge two sorted array into 新的 sorted array + +- 长度已经固定. Basic Implementation +- 如果一个array足够大, merge into this array, 那么就是从末尾merge. + + + +--- + +**335. [Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/Nth%20to%20Last%20Node%20in%20List.java)** Level: Easy Tags: [Linked List] + + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth + + + +--- + +**336. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**337. [Summary Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Summary%20Ranges.java)** Level: Medium Tags: [Array] + + +给一串sorted list, 中间有缺数字, return 所有数字的range string (example 看题目) + +#### Basic implementation +- 用一个list as the buffer to store candidates +- when: 1. end of nums; 2. not continuous integer => convert list to result + + + +--- + +**338. [Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Topological Sort] + + +#### Topological Sort BFS +- indegree tracking: Track all neighbors/childrens. 把所有的children都存在 inDegree里面 +- Process with a queue: 先把所有的root加一遍(indegree == 0),可能多个root。并且全部加到queue里面。 +- BFS with Queue: +- Only when map.get(label) == 0, add into queue && rst. (indegree剪完了, 就是root啦) +- inDegree在这里就 count down indegree, 确保在后面出现的node, 一定最后process. + + +#### Basics about graph +- 几个graph的condition: +- 1. 可能有多个root +- 2. directed node, 可以direct backwards. + +TODO: +- build`Map inDegree = new HashMap<>();` and include the root itself +- that is more traditional indegree building + + + +--- + +**339. [Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)** Level: Hard Tags: [Greedy, Hash Table, Stack] + + +#### Hash Table, Greedy +- count[] = int[256], 不需要 `c-'a'` +- boolean visited[]: 一旦一个字母固定了位置后, 再次遇到时候, 直接跳过用过的character +- 如果tail字母可以变小, 那就delete掉tail, 重新接上新字母 (前提条件: 去掉的字母后面还会再出现, set visited[tail] = false) +- Space: O(1) count[], visited[]. +- Time: Go through all letters O(n) + +#### Stack +- Use stack instead of stringBuffer: keep append/remove last added item +- However, stringBuffer appears to be faster than stack. + + + +--- + +**340. [Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)** Level: Medium Tags: [Array, Enumeration] + + +从(0,0)坐标, 走完spiral matrix, 把结果存在list里. + +#### DX, DY +- Basic implementation, array, enumeration +- 写一下position前进的方向: RIGHT->DOWN->LEFT->UP +- 用一个direction status 确定方向 +- 写一个compute direction function 改变方向 `(direction + 1) % 4` +- `boolean[][] visited` 来track走过的地方 + + + +--- + +**341. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**342. [Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort] + + +#### Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn) + + +#### Basic Implementation +- 这里已经给了 **sorted** intervals by start point. +- 直接找到可以insert newInterval的位子. Insert +- 然后loop to merge entire interval array +- 因为给的是个list, 所以方便`intervals.remove(i)` +- remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture +- O(n) + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**343. [Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)** Level: Hard Tags: [KMP, String] + + +#### Divide by mid point, Brutle +- check (mid, mid+1), or (mid-1, mid+1). +- If the two position matches, that is a palindrome candidate +- 比较front string 是否是 end string 的substring +- O(n^2) +- timeout on last case: ["aaaaaa....aaaacdaaa...aaaaaa"] + +#### KMP +- TODO + + + +--- + +**344. [Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum.java)** Level: Easy Tags: [Array, Hash Table] + + +#### HashMap +- 相对暴力简洁: 找到一个value, 存一个index +- 若在HashMap里面 match 到结果, 就return HashMap里存的index. +- O(n) space && time. + +#### Sort array, two pointer +- 前后++, --搜索. Sort 用时O(nlogn). +- 1. 第一步 two pointer 找 value. +- 2. 注意,要利用额外的空间保留original array, 用来时候找index. (此处不能用HashMap,因为以value 为key,但value可能重复) +- O(n) space, O(nlogn) time. + + + + +--- + +**345. [K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)** Level: Hard Tags: [Array, BST, TreeSet] + + +题目解析后: find 2 number, that: 1. k slots between the 2 number, 2. no slots taken between the two number. + +#### BST +- BST structure not given, use TreeSet to build BST with each node +- Every time find last/next inorder element +- `treeSet.lower(x)`, `treeSet.higher(x)` +- 一旦位置相隔(k + 1), 就满足题目条件 +- O(nlogn), good enough + +#### Track slots of days +- Reverse the array, save days index into days[], where the new index is slot. +- days[i]: at slot i, which day a flower will be planted +- O(n) +- Needs to understand: http://www.cnblogs.com/grandyang/p/8415880.html + + + +--- + +**346. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**347. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**348. [Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)** Level: Hard Tags: [Design, Geometry, Hash Table] + + +看的list of coordinates 是否能组成perfect rectangle, 并且不允许overlap area. + +#### 画图发现特点 +- 特点1: 所有给出的点(再找出没有specify的对角线点), 如果最后组成perfect rectangle, 都应该互相消除, 最后剩下4个corner +- 特点2: 找到所有点里面的min/max (x,y), 最后组成的 maxArea, 应该跟过程中accumulate的area相等 +- 特点1确保中间没有空心的部分, 保证所有的重合点都会互相消除, 最后剩下4个顶点 +- 特点2确保没有重合: 重合的area会最终超出maxArea + + + +--- + +**349. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**350. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**351. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + +**352. [Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)** Level: Medium Tags: [Backtracking, DFS, DP] + + +String 只包含 + , - 两个符号. 两个人轮流把consecutive连续的`++`, 翻转成 `--`. + +如果其中一个人再无法翻转了, 另一个人就赢. 求: 给出string, 先手是否能赢. + +#### Backtracking +- curr player 每走一步, 就生成一种新的局面, dfs on this +- 等到dfs结束, 不论成功与否, 都要backtracking +- curr level: 把"++" 改成 "--"; backtrack的时候, 改回 '--' +- 换成boolean[] 比 string/stringBuilder要快很多, 因为不需要重新生成string. +- ++ 可以走 (n - 1)个位置: +- T(N) = (N - 2) * T(N - 2) = (N - 4) * (N - 2) * T(N - 4) ... = O(N!) + +##### iterate based on "++" +- 做一个String s的 replica: string or stringBuilder +- 每次dfs后, 然后更替里面的字符 "+" => "-" +- 目的只是Mark已经用过的index +- 真正的dfs 还是在 original input string s 身上展开 +- 每次都重新生成substring, 并不是很efficient + +##### Game theory +- 保证p1能胜利,就必须保持所有p2的move都不能赢 +- 或者说, 在知道棋的所有情况时, 只要p2有一种路子会输, p1就一定能走对路能赢. +- 同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +- p1: player1, p2: player2 + +#### O(N^2) 的 DP +- 需要Game Theory的功底, Nim game. https://www.jiuzhang.com/qa/941/ +- http://www.1point3acres.com/bbs/thread-137953-1-1.html +- TODO: https://leetcode.com/problems/flip-game-ii/discuss/73954/Theory-matters-from-Backtracking(128ms)-to-DP-(0ms) + + + +--- + +**353. [Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)** Level: Easy Tags: [Array, DFS] + + +#### DFS +- 虽然Easy, 但用到DFS最基本的想法. +- 1. dive deep +- 2. mark VISITED +- 3. sum it up +- Time: worst O(mn), traverse all possible nodes + +- 更要注意, 要从符合条件value==1的地方开始dfs. +- 对于什么island都没有的情况,area应该位0, 而不是Integer.MIN_VALUE, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**354. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + +**355. [Number of Digit One.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Digit%20One.java)** Level: Hard Tags: [Math] + + +Pure math problem, not quite representative + +Explanation +https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython + + + +--- + +**356. [Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)** Level: Hard Tags: [Bit Manipulation, String] + + +#### String +- 首先要分两半解决,断点是'.': str.split("\\."); +- Integer那一半好弄,whie loop里: num%2, num/2. 做一个 `parseInteger()` function +- Decimal那边复杂点. 做一个 `parseDecimal()` function: +- bit == 1的数学条件: 当下num * 2 >= 1。 更新: num = num * 2 - 1; +- bit == 0的数学条件: num * 2 < 1. 更新: num = num * 2 + +#### 注意 +- num是 double, 小数在 `num = num * 2 - 1` 的公式下可能无限循环 +- 因此check: num重复性,以及binary code < 32 bit. +- 所以题目也才有了32BIT的要求! + + + +--- + +**357. [Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning.java)** Level: Medium Tags: [Backtracking, DFS] + + +给个string s, partition(分段)后, 要确保每个partition都是palindrome. + +求所有partition palindrome组合. `list>` + +#### DFS +- 可以top->bottom: 遍历str, validate substring(start, i); if valid, add as candidate, and dfs; backtrack by remove candidate. +- 也可以bottom->up: 遍历str, validate substring(start, i); if valid, dfs(remaining str), return list of suffix; cross match with curr candidate. + +#### DFS Top->Bottom +- 在遍历str的时候,考虑从每个curr spot 到 str 结尾,是能有多少种palindorme? +- 那就从curr spot当个字符开始算,开始backtracking. +- 如果所选不是palindrome, 那move on. +- 若所选的确是palindrome, 加到path里面,DFS去下个level,等遍历到了结尾,这就产生了一种分割成palindrome的串。 +- 每次DFS结尾,要把这一层加的所选palindrome删掉,backtracking嘛 + +#### Optimization +- 可以再每一个dfs level 算 isPalindrome(S), 但是可以先把 boolean[][] isPalin 算出来, 每次O(1) 来用 +- 注意: isPalin[i][j] 是 inclusive的, 所以用的时候要认准坐标 +- Calculate isPalin[i][j]: pick mid point [0 ~ n] +- expand and validate palindrome at these indexes: `[mid, mid+1]` or `[mid-1][mid+1]` + +#### Complexity +- Overall Space O(n^2): 存 isPlain[][] +- Time O(2^n), 每一层都在做 pick/not pick index i 的选择, 所以worst case 2^n. +- 因为我们的isPalin[][]优化了palindrome的判断O(1), 所以overall Time: O(2^n) + + + +--- + +**358. [Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST, DFS, Tree] + + +BST里面有2个node misplace, 要归为. 要求: O(1) extra space + +#### Observation +- BST inorder traversal should give small -> large sequence +- misplaced means: a **large**->small item would occur, and later a large>**small** would occur. +- The first large && second small item are the 2 candidates. Example +- [1, 5, 7, 10, 12, 15, 18] +- [1, 5, `15, 10`, `12, 7`, 18] + +#### dfs, O(1) extra space +- traverse, and take note of the candidate +- at the end, swap value of the 2 candidates + +#### O(n) space +- inorder traversal the nodes and save in array, find the 2 items misplanced and swap them +- But O(n) space should not be allowed + + + + +--- + +**359. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**360. [Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)** Level: Medium Tags: [Array, Hash Table, PreSum] + + +给一个int[][] matrix, 找一个sub matrix, where the sum == 0. + +#### PreSum的思想 +- 算出一个右下角点(i,j)到(0,0)的大小: 上一块 + 左一块 + curr node - overlap area +- preSum[i][j]: sum from (0,0) to (i-1,j-1) +- same approach as `subarray sum`: use hashmap to store diff->index; if diff re-appears, that means sum of 0 has occurred +- sequence of calculation: 1. iterate over start row. 2. iterate over end row. 3. iterate over col number (this is where hashmap is stored based on) +- the iteration over col is like a screening: find previous sum and determine result +- Note: 其实并没有真的去找 `== 0` 的解答,而是根据特性来判断 `剩下的/后来加上的一定是0` + + + +--- + +**361. [Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Substring.java)** Level: Medium Tags: [DP, String] + + +给一个string, 找到最长的palindrome substring. + +Related: Longest Palindromic Subsequence, Palindrome Partioning II + +O(n^2) is not too hard to think of. How about O(n)? + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + +#### DP: isPalin[][] +- 穷举double for loop. O(n^2) +- boolean isPalin[i][j], 每次确认有palindrome就记录下来true / false +- 穷举的for loop计算顺序: end point j, and stat point i = [0, j] +- 在计算 isPalin[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- double for loop: O(n^2). slower, because it guarantees O(n^2) due to the for loop + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**362. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + +**363. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**364. [Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/Gas%20Station.java)** Level: Medium Tags: [Greedy] + + +给一串gas station array, 每个index里面有一定数量gas. + +给一串cost array, 每个index有一个值, 是reach下一个gas station的油耗. + +array的结尾地方, 再下一个点是开头, 形成一个circle route. + +找一个index, 作为starting point: 让车子从这个点, 拿上油, 开出去, 还能开回到这个starting point + +#### Greedy +- 不论从哪一个点开始, 都可以记录总油耗, `total = {gas[i] - cost[i]}`. 最后如果total < 0, 无论从哪开始, 必然都不能走回来 +- 可以记录每一步的油耗积累, `remain += gas[i] - cost[i]` +- 一旦 remain < 0, 说明之前的starting point 不合适, 也就是说, 初始点肯定在后面的index. 重设: start = i + 1 +- single for loop. Time: O(n) + +#### NOT DP +- 看似有点像 House Robber II, 但是问题要求的是: 一个起始点的index +- 而不是求: 最后点可否走完/最值/计数 + + + +--- + +**365. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**366. [Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- 就是pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**367. [Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)** Level: Hard Tags: [Coordinate DP, Stack, String] + + +给一串string, 里面只有`(`, `)`. 找最长valid parentheses 的长度. + +#### 1D Coordinate DP +- use dp[i] track local max, maintain global max +- int[] dp. dp[i]: longest valid string that ends on i. +- 结尾是 ')', 2种情况: 1. 刚好s[i-1]是'('; 2. s[i]的')'更前面的一个起始'(' 对应 +- 注意, 结尾如果是'('属于不合理情况, 忽略. +- init: dp[0] = 0, 单个char不可能成型. +- 计算顺序: 从左到右, 找local max, maintain global max +- O(n) space, O(n) runtime + +#### Stack +- Stack 里面存所有的open/close parentheses. +- 如果遇到stack.top()刚好开合结掉, 就stack.pop(). +- 剩下的都是不合理的elements. +- 有点像negatively找 solution: `endIndex - 最后一个failedIndex(stack.pop()) - 1`, 应该就是最后一个succeeded string的长度 +- 每次更新 endIndex 为stack.top(), 然后从stack继续找下一个failedIndex +- 所有的length作比较, 就可以找出最长length +- O(n) stack space, O(n) runtime. 应该比dp慢一点, 因为做了2遍O(n) + + + + +--- + +**368. [Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)** Level: Review Tags: [BFS, DFS, DP] + + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- in dfs: remove the incorrect parentheses one at a time +- detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` +- once detected, remove the char from middle of s, and dfs on the rest of the s that has not been tested yet. + +##### Core concept: reverse test +- `if a parenthese string is valid, the reverse of it should also be valid` +- Test s with open='(', close=')' first; **reverse s**, and test it with open=')', close='(' + +##### Minor details +- only procceed to remove invalid parenthese when `count<0`, and also break && return dfs after the recursive calls. +- The above 2 facts eliminates all the redundant results. +- Reverse string before alternating open and close parentheses, so when returning final result, it will return the correct order. +- Open questions: how does it guarantee minimum removals? + +##### Backtracking +- 如果用stringbuffer, 那么久不会每次create new string, 但是需要maintain这个string buffer, 就会backtracking + +##### Complexity +- Seems to be O(n), but need to derive + +#### BFS +TODO + +#### DP + + + +--- + +**369. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + +**370. [H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)** Level: Medium Tags: [Bucket Sort, Hash Table, Sort] + + +找到h-index, 给的citation int[] 并不是sorted. h-index 的definition 具体看题目. + +#### Sort, find h from end +- 例子写出来,发现可以sort以后按照定义搜索一遍。 nlogn. +- 搜索一遍时候可以优化,用binary search. 但是没意义,因为array.sort已经用了nlogn +- 题目给的规则, 从小到大排序后: 剩下的 paper `n-h`, 全部要 <= h 个 citation. +- time O(nlogn), search O(n) + +##### 正向思考 +- 从i = 0 开始找第一个 `citations[i] >= h`, 就是第一个符合 h-index 规则的paper, return h + +##### 反向思考 +- 如果从 h = n, 每次h--; 那么 `x = n - h` 就是从 `[0 ~ n)` 开始找第一个 `dictations[x] >= h`, 就是结果 +- 同时, `dictations[x-1]` 就是最后一个(dictation最多的)其余的paper. + +#### Bucket count / Bucket Sort +- O(n) +- Bucket sort的思想(更像是counting sort?): 过一遍 input, 把dictation value 作为 index, 分布在bucket[index]上++ +- bucket[x] 是 count when # of citation == x. +- 如果 x 大于 n的时候, 就超出了index范围, 但是刚好这个问题可以包容, 把这样的情况记位在bucket[n]就可以 +- 巧妙: `sum += bucket[h]` where `h = [n ~ 0]` 利用了h-index的definition: +- #of papers (sum of bucket[n]...bucket[0]) has more than h cidations +- 这里运用到了bucket sort的思想, 但是并不是sorting, 而h-index的定义运用的很巧妙. +- Read more about actual bucket sort: https://en.wikipedia.org/wiki/Bucket_sort + + + +--- + +**371. [H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index%20II.java)** Level: Medium Tags: [Binary Search] + + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + + + +--- + +**372. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**373. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**374. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + +**375. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**376. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**377. [Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)** Level: Hard Tags: [Greedy, Hash Table, Heap] + + +给一个string, 全是lowercase letter, 要求重新排列: 然后每个unique的character要有k distance apart. + +跟Task Scheduler有点像, 只不过Task那道题里面还可以用其他方法求count, 这道题要求出排列结果 + +#### PriorityQueue + HashTable +- PriorityQueue排序+分布排列的一个经典用法. +- Count frequency and store in pq. +- Consume element of pq for k rounds, each time pick one element from queue. +- Exception: if k still has content but queue is consumed: cannot complete valid string, return ""; +- space, O(n) extra space in sb, O(26) constant space with pq. +- time: O(n) to add all items + + + +--- + +**378. [Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort] + + +#### PriorityQueue +- Use priority queue to sort by customized class interval{int dist; int x, y;}. +- Sort by larger distance and then sort by start index +- seat(): pq.poll() to find interval of largest distance. Split and add new intervals back to queue. +- leave(x): one seat will be in 2 intervals: remove both from pq, and merge to a new interval. +- 主方程写出来其实很好写, 就是 split + add interval, 然后 find + delete interval 而已. 最难的是构建data structure +- seat(): O(logn), leave(): O(n) + +##### Trick: 构建虚拟 boundary +- 如果是开头的seat, 或者是结尾的seat, 比较难handle: 一开始坐在seat=0的时候, 没有interval啊! +- Trick就是, 我们自己定义个虚拟的座位 `seat=-1`, `seat=N` +- 一开始有一个 interval[-1, N] 然后就建立了boundary. +- 从此以后, 每次split成小interval的时候: +- 遇到 `interval[-1, y]`, distance就是 `(y - 0)` +- 遇到 `interval[x, N]`, distance就是 `(N - 1 - x)` +- 当然正常的interval dist 就是 `(y - x) / 2` + +##### distance 中间点 +- Interval.dist 我们其实做的是 distance的中间点 `(y - x) / 2` +- 这里的dist是 `距离两边的距离` 而不是 x, y 之间的距离. 这里要特别注意. + +#### TreeSet +- https://leetcode.com/problems/exam-room/discuss/139885/Java-Solution-based-on-treeset/153588 + +#### Map +- how? +- TODO, not sure. + + + +--- + +**379. [Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Anagrams.java)** Level: Medium Tags: [Array, Hash Table] + + +把anagram找到并output + +#### HashMap +- 存在int[26], Arrays.toString(arr) 就是 string key: character frequency map +- anagram都有一样的key, 存进hashmap +- output anagrams + +#### HashMap + Sort +- HashMap 的做法. sort每个string, 存进HashMap, 重复的就是anagrams,最后输出。 +- toCharArray +- Arrays.sort +- Stirng.valueOf(char[]) +- 时间n*L*O(logL),L是最长string的长度。 + +#### Previous Notes +- Arrays.toString(arr)的做法。arr是int[26], assuming only have 26 lowercase letters. +- Count occurrance, 然后convert to String,作为map的key. +- Time complexity: nO(L) +- 另一种做法:http://www.jiuzhang.com/solutions/anagrams/ +- 1. take each string, count the occurrance of the 26 letters. save in int[]count. +- 2. hash the int[] count and output a unique hash value; hash = hash * a + num; a = a * b. +- 3. save to hashmap in the same way as we do. +- 这一步把for s: strs 里面的时间复杂度降到了O(L). L = s.length(). +- Need to work on the getHash() function. +- 时间变成n*O(L). Better. + + + + +--- + +**380. [Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)** Level: Medium Tags: [DFS, Hash Table, Tree] + + +给一串3-digit 的数组. 每个数字的表达一个TreeNode, 3 digit分别代表: depth.position.value + +这串数字已经从小到大排列. 求: 所有可能的 root->leaf path 的所有可能的 path sum 总和. + +#### DFS, Hash Table +- 因为`前两个digit可以uniquely identify`一个node, 所以可以把前两个digit作为key, 定位node. +- 特点: 比如考虑root, 有 n 个leaf, 就会加 n 遍root, 因为有 n 个 unique path嘛. +- 实现: 每个node, 上来先把curr value加进sum; 只要有child, 到这个node位置的以上path sum 就要被重加一次. +- format: depth.position.value. (on same level, position may not be continuous) +- approach: map each number into: , and dfs. +- Start from dfs(map, rootKey, sum): +- 1. add node value to sum +- 2. compute potential child. +- 3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +- 4. also, if child exist, dfs into next level +- Space, time O(n) + + + +--- + +**381. [Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Words.java)** Level: Easy Tags: [Hash Table, String] + + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**382. [Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Characters.java)** Level: Easy Tags: [Array, String] + + +determine if characters are unique in string + +#### HashSet +- space O(n), time O(n) + +#### char[] +- space O(n), time O(nlogn) + +#### no additional data structure +- double for loop: O(n^2) + + + + +--- + +**383. [Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)** Level: Medium Tags: [DP, Math] + + +具体看题目: count # of valid rectangles (four corner are 1) in a grid[][]. + +#### basic thinking + Math +- Fix two rows and count matching columns +- Calculate number rectangles with `combination` concept: +- total number of combinations of pick 2 points randomly: count * (count - 1) / 2 + +#### DP +- TODO. HOW? + +#### Brutle +- O(m^2 * n^2), times out + + + +--- + +**384. [Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)** Level: Medium Tags: [DP, String] + + +根据题意, count # of palindromic substring. (不同index截取出来的substring算不同的情况) + +#### isPalin[][] +- build boolean[][] to check isPalin[i][j] with DP concept +- check all candidates isPalin[][] +- O(n^2) + +#### odd/even split check +https://leetcode.com/problems/palindromic-substrings/discuss/105689/Java-solution-8-lines-extendPalindrome + + + +--- + +**385. [Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Multiply%20Strings.java)** Level: Medium Tags: [Math, String] + + +给两个integer String, 求乘积 + +#### String calculation, basic implementation +- let num1 = multipier, num2 = base. +- mutiply and save into int[m + n], without carry. Loop over num1, each row num1[x] * num2 +- move carry to the correct index and direclty save result +- calculate carry on rst[]: sb.insert(0, c) such that no need to reverse() later +- remove leading '0', but do not delete string "0" +- time,space O(mn) + +#### Previous notes. +- Bad solution: reversing makes it complicated, no need to reverse. +- 1. 数字‘123’, 在数组里面, index == 0 是 ‘1’。 但是我们平时习惯从最小位数开始乘积,就是末尾的'3'开始。 +- 所以!翻转两个数字先!我去。这个是个大坑。 +- 2. 乘积product,和移动Carrier都很普通。 +- 3. !!最后不能忘了再翻转。 +- 4. 最后一个看坑。要是乘积是0,就返回‘0’。 但是这个其实可以在开头catch到没必要做到结尾catch。 +- 用到几个StringBuffer的好东西: reverse(), sb.deleteCharAt(i) +- 找数字,或者26个字母,都可以: s.charAt(i) - '0'; s.charAt(i) - 'a'; + + + +--- + +**386. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**387. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**388. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**389. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**390. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + +**391. [Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + +time: O(n) +space: O(1) + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 看第二个解答, 进一步简化了代码 +- PreProduct, 跟preSum的感觉有点像, 就是差一位. + + + +--- + +**392. [Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)** Level: Medium Tags: [Bit Manipulation] + +time: O(n) +space: O(1), 32-bit array + +给出Hamming Distance定义(bit format时候有多少binary diff), 求一串数字的hamming distance总和. + +#### Bit Manipulation +- Bit题: 考验 bit >>, mask & 1, 还有对题目的理解能力 +- Put integers in binary, and compare each column: +- for each `1`, ask: how many are different from me? all the `0` +- `# of diffs at each bit-column = #ofZero * #ofOne ` +- 1. countZero[], countOne[]; 2. loop over nums and populate the two array + +##### 注意雷点 +- 问清楚: 10^9 < 2^31, we are okay with 32 bits +- `最终的hamming distance 要从 [1 ~ 32] 哪个bit开始算起`? 取决于 `最长`的那个binary format: 但不用先去找bit length +- 在做countZero, countOne时候, 都做32-bit; 最终做乘积的时候, 如果 `1` 或者 `0` 个数为零, 乘积自然为0. + + + + +--- + +**393. [Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + +time: O(n) +space: O(n) + +给一个tree, 按照题意找最一个node满足: +1. 这个node的subtree涵盖最深level的所有leaves. +2. 这个node必须是能找到的最deep那个 + +条件2的需求是因为: root本身就是满足条件1的node, 还有很多Higher-level node也是如此, 所以要找那个deepest. + + +#### DFS on tree +- 分析题目, 思想是: 看到tree里面所有的leaves, 找到他们最deep的 common ancestor +- Maintain a map +- Recursively dfs: return deepest node that has all leaves by these comparisons: +- 1. If left,right child same depth, return root: they need common ancestor +- 2. If not same depth, return the one with larger depth +- 被传送去上一个level的, 永远都是subtree里面符合题意的: the node containing all leaf nodes +- Visit all nodes once O(n), space O(n) + +#### BFS +- Find all leaves at deepest level +- Use map to track each node-parent +- Backtrack all nodes to find common ancestor + + + +--- + +**394. [Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)** Level: Easy Tags: [Bit Manipulation] + +time: O(n), n = # of bits +space: O(1) + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + + + +--- + +**395. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**396. [Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array] + +time: O(n) +space: O(1) + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, track start/end +- start/end point, 然后比较大小记录dist +- 注意1: 如果第一个座位没有人, 特殊处理, dist = [0 ~ end] +- 注意2: 如果最后一个座位没有人, 特殊处理: dist = [n - 1 - start]; +- 其余: `dist = Math.max(dist, (end - start) / 2)` +- 相关题目: 几乎同样概念 `Binary Gap`, 升级复杂版`Exam Room` + + + + +--- + +**397. [Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + +time: O(n) +space: O(n) + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path 的情况, 不懂得要问: +- 1. `.` 代表current directory, 可以忽略. +- 2. `../` 表示previous level. +- 3. double slash 可以忽略. +- 4. empty string 要output `/` +- 最终就是用stack (`上一个加进去的item, 用来备选pop() out`), 遇到 `../` pop()掉上一个加上去的item, 其余加进stack +- 最终用 '/' 把所有item连接起来. + + + +--- + +**398. [Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)** Level: Medium Tags: [Linked List, Stack, Tree] + +time: O(n) +space: O(n) + +给一个BST, convert成 sorted doubly DoublyListNode. + +#### Inorder Traversal, Linked List +- 会iterative traverse Binary Search Tree(Stack && handle left-dig-down) +- create Doubly-ListNode, 注意用一个dNode作为tail node of the list + +##### Iterative inorder traversal +- 在check right node的事后, +- 不论right == null or != null, 每次都要强行move to right. +- 如果不node = node.right, +- 很可能发生窘境: +- node always = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 + + + +--- + +**399. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) + +#### DP +- 最多2个fence 颜色相同 +- 假设i是和 i-1不同,那么结果就是 (k-1)*dp[i - 1] +- 假设i是何 i-1相同,那么根据条件,i-1和i-2肯定不同。那么所有的结果就是(k-1)*dp[i-2] +- dp[i]: count # of ways to paint 前i个 fence +- 加法原理 +- time, space: O(n) +- rolling array: space O(1) + +#### Previous Notes +- 这题目很有意思. 一开始分析的太复杂, 最后按照这个哥们的想法(http://yuanhsh.iteye.com/blog/2219891) 的来做,反而简单了许多。 +- 设定T(n)的做法,最后题目化简以后就跟Fibonacci number一样一样的。详细分析如下。 +- 做完,还是觉得如有神。本来是个Easy题,想不到,就是搞不出。 + + + + +--- + +**400. [Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + +time: O(n) +space: O(n) + +#### Queue +- 简单的level traversal.根据level奇数偶数而add到不同位子. +- Option1: based on level % 2, insert to front/end of list +- Option2: based on level, insert right/left of node into queue + + + +--- + +**401. [Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- true/false problem, think about dp +- 子问题: 前i个字母, 是否可以有valid break +- 检查dp[j] && `if substring(j, i) valid`, for all j = [0 ~ i] +- dp = new boolean[n + 1]; dp[0] = true; +- goal: if there is a j, `dp[j] == true && word[j, n] in dict`. Need iterate over i = [0 ~ n], also j = [0, i] +- 注意, 用set代替list, 因为要用 contains(). + +#### Previous notes +##### 方法2(attempt4 code) +- 与Word BreakII用同样的DP。 +- valid[i]: 记录从i到valid array末尾是否valid. + +##### 方法1:(attempt3 code) +- state,rst[i]: 从[0~i] inclusive的string是否可以在dict中break开来找到? +- function: rst[i] = true if (rst[i - j] && set.contains(s.substring(i - j, i))); j in[0~i] +- 1. rst[i - j] 记录的是[0, i-j]这一段是否可以break后在dict找到。 +- 2. 若true,再加上剩下所有[i-j, i]都能在dict找到,那么rst[i] = rst[0, i - j] && rst[i-j, i] == true +- 优化:找dict里面最长string, 限制j的增大。 + + + + +--- + +**402. [Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### 理解意思是关键 +- 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出 +- 记录每天最小值Min是多少. O(n) +- 每天都算和当下的Min买卖,profit最大多少. + +#### DP +- Find min value for first i items, new dp[n + 1]. +- dp[i]: 前i天, prices最小的price是多少: min cost of first i days +- 然后用当天的price做减法dp[i]算max profit. +- Time, Space: O(n) +- 更进一步, 用一个min来表示min[i], 因为计算中只需要当下的min. + +#### Rolling array +- index i only depend on [i - 2] +- Space O(n) + +#### Brutle Failed +- 每天都试着买进,然后之后的每一天尝试卖出. double for loop, O(n^2). timeout. +- 其中很多都是没必要的计算:[7, 1, 5, 3, 6, 4]。 if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. + + + +--- + +**403. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**404. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**405. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**406. [Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Random%20Pick%20Index.java)** Level: Medium Tags: [Reservior Sampling] + +time: O(n) +space: O(n) for input int[], O(1) extra space used + +#### Reservior sampling +- Random choose: think about reservoir sampling. https://www.youtube.com/watch?v=A1iwzSew5QY +- Use random generator rd.nextInt(x) pick integer between [0, x) +- try all numbers, when target is met, we want to model reservoir sampling: +- item was chosen out of i samples, and all other samples are failed. +- where we can use 'count' to represent the denominator/base to choose. +- **HAVE TO finish all samples** to make sure equal opportunity +- we can pick that last matched item as result +- `rd.nextInt(count++) == 0` make sure we are always picking num == 0 to meet definition of reservoir sampling. + +#### Knowledge +- If multiply these probablities together to get the probability of one item being chosen with reservior sampling: +- probability = 1/i * (1 - 1/i+1) * (1 - 1/i+2) ....(1 - 1/n) = 1/n + + + + +--- + +**407. [Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Celebrity.java)** Level: Medium Tags: [Array, Greedy] + +time: O(n) +space: O(1) + +有n个人, 其中有个人是celebrity, 满足条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +#### Understand the property +- If brutly find celeb by comparing all possible pair: take complete O(n^2) handshakes. +- Instead, we can perform pruning, or like survival mode: +- 1. Assume a celeb = 0, and compare with all i = [1~ n-1] +- 2. If `celeb candidate know i, set celeb = i` as the next candidate (ex: prev canddiate invalid when he knows i) +- 3. For last standing celeb candidate: compare with all for validation +- Why performing the last run of validation? There could be someone dropped out before we execute `know(celeb, i)`. + +##### 思考逻辑 +- 先写出来[0 ~ n - 1], 最简单的方式 O(n^2) 检查, 记录每个人的状态. +- 逐渐发现, 因为 celeb 谁都不会认识, 那么当任何candidate knows anyone, 他自身就不是celeb. +- 我们可以greedy地, 一旦fail一个, 就立刻假设下一个是celeb candidate +- 最终还是要检查一遍, 避免错漏. +- 想一下happy case: 如果 celeb=0, 那么 know(celeb, i) 永远都是false, 然后 celeb一直保持0, 坚持到verify所有人. + + + +--- + +**408. [Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/Sparse%20Matrix%20Multiplication.java)** Level: Medium Tags: [Hash Table] + +time: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` +space: O(1) extra + +给两个matrics, 做乘积. 注意, 是sparse matrix (特点: 很多0). + +#### Hash Table +- Recall matric multiplication rules: result[i][j] = sum(A-row[i] * B-col[j]) +- `sparse matric: lots positions are zero` +- 平白地写matric multiplication 没有意义, 重点就是optimization: +- `optimization`: for A-zero-row, and B-zero-col, there is no need to calculate, just return 0. +- 1. Find A-zero-rows and store in setA, same for setB +- 2. during multiplication, reduce time complexity. +- Base: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` + +#### Matrices +- 乘法规则: result[i][j] = sum(A-row[i] * B-col[j]) +- A column size == B row size. 并且: 计算顺序是iterate over A column size + + + +--- + +**409. [Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)** Level: Medium Tags: [Hash Table] + +time: O(mn) +space: O(X), X = max wall width + +给一面墙, 每一行是一行bricks. 用一条vertical line 扫描, 会vertically割开brink. 找到割开最少brick的那条线的x index. + +#### Hash Table +- Find the vertical line (x-coordinate of the grid), where most gaps are found. +- Each gap has (x,y) coordinate +- Create `map`, and maintain a max occurance. +- 计算: x-coordinate: `x = 0; x += brick[i] width` +- Eventually: min-crossed bricks = wall.lenght - maxOccurrance + +##### 思想 +- 分析题意, 找到题目的目标 +- 虽然Map自己不能有sort的规律, 但是可以maintain global variable + + + +--- + +**410. [Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/Exclusive%20Time%20of%20Functions.java)** Level: Medium Tags: [Stack] + + +#### Stack +- 1. later function always appears after prior fn: 1 is called by 0 +- 2. `Not mentione in the question`: a function can be started multiple times +- 3. `Not mentione in the question`: a fn cannot start if children fn starts +- 4. Use stack to keep id +- TODO: what leads to the choice of stack? stacking fn id + + + +--- + +**411. [Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)** Level: Medium Tags: [Array, Math] + + +#### Array, Math +- 这个问题更在于问题本身的分析 (而且还有多余条件); 最终的for loop 也比较不standard. +- People younger than 15 cannot make requests due to the first rule. +- From the age of 15, people can make requests to the same age: a[i] * (a[i] - 1) requests. +- People can make requests to younger people older than 0.5 * i + 7: a[j] * a[i] requests. +- The third rule is redundant as the condition is already covered by the second rule. +- TODO: the approach. + + + +--- + +**412. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + + +// 如何想到从中间initialize + + + +--- + +**413. [Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)** Level: Medium Tags: [Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +#### Map +- use `Map` to store inline preSum and its index. +- 1. Build presum incline +- 2. Use map to cache current preSum value and its index: `Map` +- 3. Each iteration: calculate possible preSum candidate that prior target sequence. ex: `(preSum - k)` +- 4. Use the calculated preSum candidate to find index +- 5. Use found index to calculate for result. ex: calculate range. + + + +--- + +**414. [Contiguous Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Contiguous%20Array.java)** Level: Medium Tags: [Hash Table] + + +TODO: how aout without chaning the input nums? + + + +--- + +**415. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + +time: O(n) +space: O(n) + +给一串点, 找是否有一个所有点中间的, 跟y-axis平行的中线. + +#### Hash Table +- 1. store in `Map>`, 2. iterate over map, check head,tail against the mid point +- 很好的细节题目: +- 1. 除以2, 需要存double +- 2. (问面试官)可以有重复的点! 所以track `set` +- 3. 处理 left==right时候, 就当做两个点来处理. +- 4. 存进set里面没有sort, 但是最后做check的时候, 需要sort list +- 时间: visit all nodes 两遍, O(n) + + + +--- + +**416. [Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + +time: O(1) avg +space: O(n) + +#### Hash Table +- 用`map 来track value->index`, 用`list track index->value` +- map查看value是否存在 +- list maintain 用来 insert/remove/random operations. +- 特点: 一旦remove, 换到list结尾然后 `list.remove(list.size() - 1)`, 这样remove的cost更低. +- list.remove(object) 应该是要O(logn) 做一个search的. + + + +--- + +**417. [Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Coordinate DP, DP] + +time: O(n^2) +time: O(n) + +给一串 unsorted sequence, 找到长 increasing subsequence 的个数! + +#### Coordinate DP +- 需要能够判断综合题, 分清楚情况和套路: combination of `longest subsequence` and `ways to do`, as well as global variable. +- len[i] (我们平时的dp[i]): 在前i个元素中, 最长的 increasing subsequence length; +- count[i]: 在前i个元素中, 并且以 len[i]这个长度为准的 subsequence的 count. 或者: 在前i个元素中, ways to reach longest increasing subsequence. +- `len[i] == len[j] + 1`: same length, but different sequence, so add all `count[i] += count[j]` +- `len[i] < len[j] + 1`: 这就是更长的情况找到了, 那么有多少次 count[j] 有多少, count[i] 就有多少. 仔细想sequence: 长度增长了, 但是ways to reach i 没有增长. +- 同样的判断需要用在 maxLen 和 maxFreq上: +- 如果没有增长 maxLen 不变, maxFreq上面需要 +=count[i] (同一种长度, 多了更多的做法) +- 如果maxLen 变长, maxFreq 也就是采用了 count[i] = count[j] +- TODO: Is rolling array possible? + +#### 相关 +- 都是 Coordiate DP, DP的鼻祖家族: +- Longest Increasing Subsequence (跟这道题的一部分一模一样) +- Longest Continuous Increasing Subsequence (连续, 只check dp[i - 1]) +- Longest Increasing Continuous Subsequence I, II (Lintcode, II 是matrix) + + + +--- + +**418. [Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)** Level: Medium Tags: [Coordinate DP, DP, Status DP] + + + +#### DP +- 特点: 上一步可能是swaped也可能是fixed +- 考虑A,B之间的现状: `A[i] > A[i - 1] && B[i] > B[i - 1]` 或者 `A[i] > B[i - 1] && B[i] > A[i - 1]` +- 问题: 如何把这个状态变成合理的strick-increasing状态? +- `A[i] > A[i - 1] && B[i] > B[i - 1]`: 1. 已经合理, 也不动. 2. [i], [i-1] 全部都swap +- `A[i] > B[i - 1] && B[i] > A[i - 1]`, 交错开来, 所以调换[i], 或者[i-1]: 1. 换[i-1]. 2. 换[i] +- 注意因为求min, 所以init value应该是 Integer.MAX_VALUE; + + + +--- + +**419. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**420. [Populating Next Right Pointers in Each Node II.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node%20II.java)** Level: Medium Tags: [DFS, Tree] + +time: O(n) +space: O(1) + +给一个binary tree, 用constant space link 所有所有node.next to same level next node. + +#### DFS +- 用constant space 也就是不可以BFS, 但是mention了用dfs stack space没问题 (提示啊!) +- 1. link leftChild -> rightChild +- 2. resolve root.rightMost child -> first possible root.next.left/right child +- 3. dfs connect(rightChild), connect(leftChild) +- Each level should be fully linked from left side, so every reach to parent will have valid path or end. + +#### Trick +- 1. 处理 nextNode -> next -> next ...的case: 找到第一个有child的next node才可以罢休. 这个case很容易miss +- 2. 我们的假设是, 上一个level的所有node都应该是linked, 那么在dfs时候, 就应该先connect(root.right). 右孩子的全处理完毕, 那么trick1才可以施行. + + + +--- + +**421. [Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + +time: log(n) +space: O(1) + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 +- 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` +- `start mid`: start = mid; +- 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- `mid < target < end`: start = mid; +- `target < mid`: end = mid; + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**422. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**423. [Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + +time: O(n) + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**424. [Find the Weak Connected Component in the Directed Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Weak%20Connected%20Component%20in%20the%20Directed%20Graph.java)** Level: Medium Tags: [Union Find] + + +遍历 weak connected graph, 将结果存在 List>种. + +#### Union Find +- 跟传统的UnionFind有两点不同: +- 1. 用 Map 代替 int[], 因为没有给出 graph node label的 boundary. +- 2. find(x)时候, 没有去update `parent[x]/map.put(x, ..)`. 因为我们最终需要找到这个path. +- 无法用传统dfs: directed node 无法point到上一个点; 必须用`存parent的方式把所有node遍历掉` + +#### Identify这是个union-find问题 +- 看到了weak component的形式: 一个点指向所有,那么所有的点都有一个公共的parent,然后就是要找出这些点。 +- 为何不能从一个点出发,比如A,直接print它所有的neighbors呢: +- 如果轮到了B点,那因为是directed,它也不知道A的情况,也不知道改如何继续加,或者下手。 +- 所以,要把所有跟A有关系的点,或者接下去和A的neighbor有关系的点,都放进union-find里面,让这些点有Common parents. +- 最后output的想法: +- 做一个 map 。 +- 之前我们不是给每个num都存好了parent了嘛。 +- 每个num都有个parent, 然后不同的parent就创造一个不同的list。 +- 最后,把Map里面所有的list拿出来就好了。 + + + +--- + +**425. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**426. [Bricks Falling When Hit.java](https://github.com/awangdev/LintCode/blob/master/Java/Bricks%20Falling%20When%20Hit.java)** Level: Hard Tags: [Union Find] + + +给一个matrix of 1 and 0, `1` 代表brick. 连着ceiling的brick就不会drop. 给一串coordinate hits[][], 记录每次take down 1 brick 后, 会drop多少个. + +#### UnionFind +- 1. 我们知道大部分的brick可能都是连着ceiling, 所以每次正向检查都traverse all and timeout +- 2. 能否用union, 把connect都装在一起, 然后drop brick的时候把连着的都drop掉? 难: 因为还是要check所有brick当下的status. +- 受其他人的解答启发, 由于是计算count,我们可以`反向考虑`: +- 把hit-brick全部mark=2 (就当舍弃不算), 观察整个局面的最后一步, 先把所有还连着ceiling的brick算一下总数, 统计在unionFind的 全部统计在count[0] 里面. +- 剩下的不连着ceiling的也就是一个个isolated island +- 做法: 把hit-brick 一个个加回去, 然后再做一次union, 看看最终连到ceiling的有多少个. 增加的count, 就是正向思考时 dropped brick 数量! + +##### Union Find 变种 +- 还是用数字index做union find, 但是把每一个index都+1, 右移一位, 而[0]留下来做特殊用途: +- 用union at 0来 统计总共的remain count of ceiling-connected bricks, where `x = 0`. +- 如果在其他其他题目种, 条件可能就不是`x=0`, 但也可以用这个 union index = 0 来做一个root的统计 +- 关键: 把最后一个hit brick加回去, 然后再重新union一下这个hit-brick周围: count增加的变化, 不就是缺少hit-brick时候掉下去的数量. + + +#### DFS (timeout) +- 考虑每个hit的四周, 全部traverse, 没有连着ceiling就全部: +- 比如是 200 x 200 的 全部是1的matrix, 任何一次traverse都要到顶; 重复计算, 所以timeout +- 算法是没错, 但是不efficient. +- 想要减少重复计算, 但是又不能提前计算: grid在不断变化. 所以看能不能把连着ceiling的都group起来, 可以O(1)快速check? + + + + +--- + +**427. [Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)** Level: Hard Tags: [Binary Search, Lint, Segment Tree] + + +SegmentTree大集合. Methods: `build, query, modify`. 不难。只是要都记得不犯错. + +#### Segment Tree +- build: recursively build children based on index-mid and link to curr node +- query: recursively try to find `node.start == targetStart && node.end == targetEnd`. Compare with node.mid +- modify: recursively try to find `node.start == targetStart && node.end == targetEnd`; modify and recursively assign upper interval with updated interval property. + + + +--- + +**428. [Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字, array size = n. 给一串query: 每个query是一个数, 目的找 count# items smaller than query element. + +#### Segment Tree +- 和平时的segment tree问题不同。 [0 ~ n] 代表实际数字: based on real value的segment tree. +- Modify时,把array里面的value带进去,找到特定的位子, 然后count + 1. +- 最终在SegmentTree leaf上面全是array里面实际的数字。 +- node.count: 在node range里面的有多少个数字 + +##### right use of modify() +- build() 只是 empty segment tree, 没有property +- modify() 需要: 1. 找到left, update count+=1; 2. aggregate all parent when after returning +- 所以每一个modify 都是在整个path上所有的node上 + count + +##### query trick +- 在query前,给进去的start和end是: 0 ~ value-1. +- `value-1`: 找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. + +##### About other basic segment tree setup +- [那么其他做过的SegmentTree是怎么样呢?] +- 那些构成好的SegmentTree(找min,max,sum)也有一个Array。但是构成Tree时候,随Array的index而构架。 +- 也就是说,假如有Array[x,y,....]:在leaf,会有[0,0] with value = x. [1,1] with value = y. +- [但是这题] +- 构成时,是用actual value.也就是比如Array[x,y,....]会产生leaf:[x,x]with value = ..; [y,y]with value =... +- 其实很容易看穿: +- 若给出一个固定的array构成 SegmentTree,那估计很简单:按照index从0~array.lengh,leaf上就是[0,0] with value = x. +- 若题目让构造一个空心SegmentTree, `based on value 0 ~ n-1 (n <= 10000)`, 然后把一个Array的value modify 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**429. [HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/HashHeap.java)** Level: Hard Tags: [HashHeap, Heap] + + +非题.是从九章找来的HashHeap implementation. + + + +--- + +**430. [My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)** Level: Medium Tags: [Array, TreeMap] + + +Given a list of interval as calendar items. Check if newly added calendar item is overlapping. + +Understand it is only checking time, but not requiring to insert into right spot. No need to overthink. + +#### Simply O(n) check on array +- number of test cases is small, like 1000, so less concern about the time complexity +- simply loop over the list of intervals, and check if any overlapping. +- where to insert does not really matter: every time we are just checking for overlaopping, not merging any range +- **IMPORTANT**: if interval over lapping, they will have this property `Math.max(s1, s2) < Math.min(e1, e2)`. This will help detect the overlapping very easily. +- O(n^2) runtime, with simple code. But somehow this approach is faster than the TreeMap solution: maybe the test cause causes avg O(n)? + +#### TreeMap +- One constraint from the simply array solution: it always cost O(n) to find the potential overlapping interval +- We can manually sort and always manually try to find the correct element via binary search, or we could store the interval in a treeMap, where the intervals are sorted by `start`. +- As result, all we need to do for book(start, end) is to find the next element ceiling(start), last element floor(start), and check for overlapping +- This approach also saves the custom data structure +- Overall cost O(nlogn) + +##### About TreeMap +- always with key sorted ascendingly +- more costly than regular HashMap because of the sorting. Building treemap of n items: O(nlogn) + +#### Sweep line +- use `Point{int start, end; boolean start}` to mark start/end of class. Add to pq. +- Adding new item to pq, sort, and check if overlapping occurs by counting started classes +- If started classes > 1, that means we overlapped. +- Every time it could consume all classes to find the overlap, O(n^2). +- Not quite need to sort or insert at correct point, and this solution requires longer code. Not quite worthy it for a simple problem. + + + + +--- + +**431. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + +**432. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**433. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**435. [Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)** Level: Medium Tags: [Heap, MinHeap, PriorityQueue] + + +Same as merge k sorted list, use priorityQueue + +#### Priority Queue +- 由Merge k sorted list启发。用PriorityQueue,存那k个首发element +- PriorityQueue需要存储单位: 自己建一个Class Node 存val, x, y index. +- 因为array里没有 'next' pointer,只能存x,y来推next element +- Not sure why `new PriorityQueue<>(Comparator.comparing(a -> a.val));` is slower + + + +--- + +**436. [Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/Heapify.java)** Level: Medium Tags: [Heap, MinHeap] + + +Turn unsorted array into a min-heap array, where for each A[i], + +A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i]. + +#### Heap +- Heap用的不多. 得用一下, 才好理解. 通常default 的PriorityQueue就是给了一个现成的min-heap: +- 所有后面的对应element都比curr element 小。 +- Heapify里面的**siftdown**的部分: +- 只能从for(i = n/2-1 ~ 0), 而不能从for(i = 0 ~ n/2 -1): 必须中间开花,向上跑的时候才能确保脚下是符合heap规则的 + +#### Heapify/SiftDown做了什么? +- 确保在heap datastructure里面curr node下面的两个孩子,以及下面所有的node都遵循一个规律 +- 比如在这里,若是min-heap,就是后面的两孩子都要比自己大。若不是,就要swap。 + +#### min-heap的判断规律: +- for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. +- siftdown时:在curr node和两个child里面小的比较。如果的确curr < child, 搞定,break while. +- 但若curr 并不比child小,那么就要换位子,而且继续从child的位子往下面盘查。 + + + +--- + +**437. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**438. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**439. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**440. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**441. [Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)** Level: Medium Tags: [BST, Tree] + + +找 Inorder traversal规则里的下一个. + +主要想法是考虑: + 1. 如果 node.right == null, 找上一个unprocessed node alone the inorder traversal path + 2. 如果 node.right != null, successor 一定在这个node.right那个subtree里面 +最后竟然可以简化成几行, 非常全面的BST问题: 有search, 有对inorder traversal的理解, 还有坑. + +#### Short Recursive and Iterative without Stack +- Previous solution, we use stack to hold previous cached/unprocessed items: but do we need use catch to hold them? +- If moving left: `p.val < root.val`, then root (parent of left child) is a successor candidate, so save `rst = root`. +- If moving right or equal: `p.val >= root.val`, the successor has nothing to do with curr node, so just directly dive into root.right. +- Both iterative and recursive solution can be simplified as such. + + +#### Previous Iterative + stack +- Iteratively search +- Still need stack to store previously unprocessed items along the path + +#### Previous Recursive + Stack +- 画inorder图,发现规律.每个node的后继node(successor)有几种情况: +- 1. node.right 是个leaf到底了。那么就return. +- 2. set rightNode = node.right, 但发现rightNode has a lot left children to leaf. +- 3. 比如, node.right == null, 也就是node自己是leaf,要回头看山顶找Inorder traversal规则里的下一个。 +- 发现:其实就是每层都把路过的curr node放在stack里,最上面的,就是当下改return的那个successor:) Done. + + + +--- + +**442. [Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +#### Tree +- Traverse tree: left, right +- Concept of partial compare vs. whole compare + + + +--- + +**443. [Two Sum IV - Input is a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20IV%20-%20Input%20is%20a%20BST.java)** Level: Easy Tags: [Tree] + + +HashSet to store visited items. Same old 2 sum trick. + + + +--- + +**444. [Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4.java)** Level: Easy Tags: [Enumeration, String] + + +Read4 题目. 理解题目: 是有个input object buff, 会被populated with data. + +#### String in char[] format +- 理解题目: 其实就是track `可以read多少bytes by read4() response` +- 另外一个有用的function `System.arraycopy(src, srcIndex, dest, destIndex, length)` + + + +--- + +**445. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + +**446. [Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)** Level: Medium Tags: [BFS, DFS] + + +给一个room 2D grid. 里面有墙-1, 门0, 还有empty space INF(Math.MAX_VALUE). + +对每个empty space而言, fill it with dist to nearest gate. + +#### DFS +- Form empty room: it can reach different gate, but each shortest length will be determined by the 4 directions. +- Option1(NOT applicable). DFS on INF, mark visited, summerize results of 4 directions. +- hard to resue: we do not know the direction in cached result dist[i][j] +- Option2. DFS on gate, and each step taken to each direction will +1 on the spot: distance from one '0'; +- Through dfs from all zeros, update each spot with shorter dist +- Worst time: O(mn), where entre rooms[][] are gates. It takes O(mn) to complete the iteration. Other gates be skipped by `if (rooms[x][y] <= dist) return;` + +#### BFS +- Exact same concept. Init with `Queue queue = new LinkedList()` + + + +--- + +**447. [Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素。 +- 注意,从尾部,是大数字优先排末尾的. + + + +--- + +**448. [Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)** Level: Hard Tags: [Enumeration, Math, String] + + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 + +#### 注意 +- StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 +- 注意加 " " 的时候, 如果多余, 要`trim()` +- 注意, 小于20的数字, 有自己的特殊写法, 需要额外handle +- 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 + + + + +--- + +**449. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**450. [Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome%20II.java)** Level: Easy Tags: [String] + + +#### Palindrome String +- delete an index = jump over the index +- 注意 boolean chance 可以用一个helper function + + + +--- + +**451. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + +**452. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**453. [Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison + + + +--- + +**454. [Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Move%20Zeroes.java)** Level: Easy Tags: [Array, Two Pointers] + + +Move non-zero elements to front of array; preseve order. + +#### Two Pointers +- Outside pointer that moves in certain condition. +- Save appropirate elements + + + +--- + +**455. [Flood Fill.java](https://github.com/awangdev/LintCode/blob/master/Java/Flood%20Fill.java)** Level: Easy Tags: [DFS] + + +Same as MS Paint + +#### DFS +- track `boolean[][] visited`, validate before dfs + + + +--- + +**456. [Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Diameter%20of%20Binary%20Tree.java)** Level: Easy Tags: [Tree] + + +找longest path (include or not include root) + +跟Binary Tree Maximum Path Sum 的想法一样: 处理single path, 或者combined path (do not include curr root) + +#### Singlepath, combined path +- `int[]{combinedPath, singlePath}`; +- pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; +- complete left/right child, or join curr root: `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`; + + + +--- + +**457. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + + +--- + +**458. [Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- Summing space = `width + (size-1)`. maintain: 1. list of candidates, 2. width of actual words +- calculate space in between: `remain/(size - 1)` +- overall for loop; clean up list: 1. over size; 2. last item +- 一点也不难, 但是要小心: deal with list of string的时候, 注意处理干净sum size of list, 就行了. +- `干净处理space`: 只处理 (n-1) items, 然后最后一个拿到for loop 外面, 特殊处理. + +#### Notes +- Clarification, observation: +- can start with greedy approach to stack as many words as possible +- once exceed the length, pop the top, and justify the added words (untouched words tracked by index) +- left justify: given list/stack of words with size t, overall remaining space length m, +- deal with last line with special care: just fill one space, and fill the rest of the row with space +- Does not seem very complicated, but need additional care of calculating the amount of space needed. +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**459. [Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)** Level: Hard Tags: [Enumeration, String] + + +Read N Character using `Read4(char[] buf)` 的加强版: 可以不断读 read(buf, n) + +#### String +- 注意String的index handle, 慢慢写edge case +- 理解题目意思: `read4(char[] buf)` 这样的 `populate input object` 的function稍微少一点. +- 遇到时候, 仔细理解function用法, 不要慌乱. 其实思考方式很简单, 仔细handle string 还有 edge case就好了. + + + +--- + +**460. [Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)** Level: Hard Tags: [DP, Hash Table] + + +Frog jump 的题目稍微需要理解: 每个格子可以 jump k-1, k, k+1 steps, 而k取决于上一步所跳的步数. 默认 0->1 一定是跳了1步. + +注意: int[] stones 里面是stone所在的unit (不是可以跳的步数, 不要理解错). + +#### DP +- 原本想按照corrdiante dp 来做, 但是发现很多问题, 需要track 不同的 possible previous starting spot. +- 根据jiuzhang答案: 按照定义, 用一个 map of > +- 每次在处理一个stone的时候, 都根据他自己的 set of , 来走下三步: k-1, k, or k+1 steps. +- 每次走一步, 查看 stone + step 是否存在; 如果存在, 就加进 next position: `stone+step`的 hash set 里面 + +##### 注意init +- `dp.put(stone, new HashSet<>())` mark 每个stone的存在 +- `dp.get(0).add(0)` init condition, 用来做 dp.put(1, 1) + +##### 思想 +- 最终做下来思考模式, 更像是BFS的模式: starting from (0,0), add all possible ways +- 然后again, try next stone with all possible future ways ... etc + + + +--- + +**461. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**462. [Shortest Distance from All Buildings.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Distance%20from%20All%20Buildings.java)** Level: Hard Tags: [BFS] + + +给Walls and Gates很像, 不同的是, 这道题要选一个 coordinate, having shortest sum distance to all buildings (marked as 1). + +#### BFS +- BFS 可以 mark shortest distance from bulding -> any possible spot. +- Try each building (marked as 1) -> BFS cover all 0. +- time: O(n^2) * # of building; use new visited[][] to mark visited for each building. +- O(n^2) find smallest point/aggregation value. +- 注意, 这道题我们update grid[][] sum up with shortest path value from building. +- 最后找个min value 就好了, 甚至不用return coordinate. +- 分析过, 还没有写. + + + +--- + +**463. [String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- check sign, leading-0, overall size > 11, check max/min in Long format +- if passed all tests, parseInt() + +#### regular expression +- if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**464. [Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Roman%20to%20Integer.java)** Level: Easy Tags: [Math, String] + + +#### String +- 熟悉罗马字母规则 +- 1. 'I V X L C D M' 分别代表的数字 +- 2. 列举combo的情况,需要从原sum里面减掉多加的部分: 'IV, IX'减2, 'XL, XC'减20, 'CD, CM'减200. +- Leading `I(1*2)`, `X(10*2)`, `C(100*2)` causes double counting + +https://en.wikipedia.org/wiki/Roman_numerals + + + +--- + +**465. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**466. [Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math] + + +根据题意枚举, 再根据规则basic implementation + +#### Alter input + +#### HashTable + Two Pointer + + + +--- + +**467. [Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +剥皮过程。解铃还须系铃人 +左边的外皮'{['在stack底部 +右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**468. [First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +方法1: 按照题意, 找到第一个 first index == last index的字母. + +方法2: 用hashmap存字母的index, 有些重复字母的index就会是个list. 找到单一index, 结合成list, sort, return list.get(0) + + + +--- + +**469. [Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)** Level: Easy Tags: [Math, String, Two Pointers] + + +#### Two pointers +- Use two pointers i, j to track the 2 strings +- Add when i and j are applicable. While (i >= 0 || j >= 0) +- StringBuffer.insert(0, x); +- handle carry + +#### wrong: convert to int +- 土办法没技术,把binary换成数字,加起来,再换成binary +- 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**470. [Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph] + + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes. +- 先能复制多少Node复制多少. 然后把neighbor 加上 +- Use `map` to mark visited + +#### DFS +- Given graph node obj `{val, list of neighbor}`: copy the node and all neighbors +- Mark visited using map +- for loop on the each one of the neighbors: map copy, record in map, and further dfs +- once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +- 主要思想是: 一旦复制过了, 不必要重新复制 + +#### BFS +- Copy the root node, then copy all the neighbors. +- Mark copied node in map. +- Use queue to contain the newly added neighbors. Need to work on them in the future. + + + +--- + +**471. [Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Deque, Monotonous queue +- 维持monotonuous queue: one end is always at max and the other end is min. Always need to return the max end of queue. +- when adding new elements x: start from small-end of the queue, drop all smaller elements and append to first element larger than x. +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`. +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! + + + +--- + +**472. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + +**473. [Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)** Level: Medium Tags: [Backtracking, DFS, Permutation] + + +#### Recursive: Backtracking +- Given a remaining list: 取, 或者不取 +- always iterate over full `nums[]`, use list.contains() to check if item has been added. +- Improvement: maintain list (add/remove elements) instead of 'list.contains' +- time O(n!): visit all possible outcome +- T(n) = n * T(n-1) + O(1) + +#### Iterative: Insertion +- 插入法: +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- 但是比dfs要快, 因该是因为 # of checks 少: 不需要check list.size(), 不需要maintain remaining list. + +#### Previous Notes +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- A bit slower, possibly because of the polling and saving the entire list every time + + + + +--- + +**474. [One Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/One%20Edit%20Distance.java)** Level: Medium Tags: [String] + + +如果S, T只用一个operation就能变成相等, return true. + +#### Edit: 删除,增加,和替换 +- 换完之后,理论上换成的String 就应该全等 +- for loop, 一旦找到不一样的char, 就判断那三种可能性: insert/delete/replace +- insert/delete 对于2个string来说, 效果是类似的 +- O(n) + + + +--- + +**475. [4Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/4Sum.java)** Level: Medium Tags: [Hash Table] + + +#### Based on 2sum +- 1. 利用2Sum的原理,把4Sum分为连个2Sum。左一个pair,右一个pair,每个pair里面放2个数字。 +- 2. 以一个点,i,作为分界口,也要列举出所有i之前的pair,作为基础。 +- 3. 再尝试从所有i+1后面,找合适的2nd pair。 +- Time: O(n^2 * x), where x = # of candidates, still slow +- 可以用HashSet, 可以直接比较list里面每一个元素, 保证set不重复. +- Previous Notes: 在造class Pair时候,要做@override的function: hashCode(), equals(Object d). 平时不太想得起来用。 +- 参见 http://lifexplorer.me/leetcode-3sum-4sum-and-k-sum/ + +#### Based on 3Sum +- 3Sum外面再加一层. 参考3Sum. 时间O(n^3)。 但此方法在k-sum时候,无疑过于费时间. O(n^k) + + + +--- + +**476. [Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)** Level: Hard Tags: [BFS] + + + + +--- + +**477. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + + +--- + +**478. [Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Isomorphic%20Strings.java)** Level: Easy Tags: [Hash Table] + + +#### HashMap +- two failture cases: +- same key, value not matching +- two key maps to same value + +#### Previous note +1. Match. 就是map.containsKey, map.containsValue, and char1 == char2. Perfect. +2. Either Key not exist, or Value not exit. False; +3. Both key and Value exist, but map.get(char1) != char2. Miss-match. False. +4. None of Key or Value exist in HashMap. Then add the match. + + + +--- + +**479. [Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)** Level: Hard Tags: [DFS, Greedy, Math] + + +#### Greedy, Iterative +- For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +- We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +- Track the newly formed string; if new, add the new char to overall result +- Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] +- Same concept as dfs + +#### DFS +- Same concept: use window to cut out tail, and append with new candidate +- do this for k^n = Math.pow(k, n) times + + + +--- + +**480. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**481. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**482. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + +**483. [The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)** Level: Medium Tags: [BFS, DFS] + + +#### BFS +- BFS on coordinates +- always attempt to move to end of border +- use boolean[][] visited to alingn with BFS solution in Maze II, III, where it uses Node[][] to store state on each item. + + + +--- + +**484. [The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)** Level: Medium Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- if already found a good/shorter route, skip +- `if (distMap[node.x][node.y] <= node.dist) continue;` +- This always terminates the possibility to go return to original route, because the dist will be double/higher + + + +--- + +**485. [The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)** Level: Hard Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- 跟 Maze I, II 类似, 用一个 Node[][] 来存每一个(x,y)的state. +- Different from traditional BFS(shortest path): `it terminates BFS when good solution exists (distance), but will finish all possible routes` +- 1. `Termination condition`: if we already have a good/better solution on nodeMap[x][y], no need to add a new one +- 2. Always cache the node if passed the test in step1 +- 3. Always offer the moved position as a new node to queue (as long as it fits condition) +- 4. Finally the item at nodeMap[target.x][target.y] will have the best solution. + + + +--- + +**486. [Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)** Level: Medium Tags: [DP, MiniMax] + + +Detailed in `Coins in a Line III` + + + +--- + +**487. [Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack? + + + +--- + +**488. [Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)** Level: Medium Tags: [Hash Table, String] + + + +#### Convert to orginal string +- shit by offset. `int offset = s.charAt(0) - 'a';` +- increase if less than 'a': `if (newChar < 'a') newChar += 26;` + +#### Previous notes +- 相同shift规则的string, 能被推算到同一个零起始点,就是共同减去一个char,最后就相等。以此作为key,用HashMap。一目了然。 +- 记得根据题目意思,一开始要String[] sort一下。 + + + +--- + +**489. [Delete Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Digits.java)** Level: Medium Tags: [Greedy, Priority Queue] + + +#### Priority Queue +- TODO: parse into node(index, digitValue) +- find the top k, and remove from char array +- O(nlogn) time + +#### Greedy +- 数位靠前的,权值更大. 所以硬来把靠前的相对更大的(跟following digit相比)去掉。 + + + +--- + +**490. [Flatten 2D Vector.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%202D%20Vector.java)** Level: Medium Tags: [Design] + + +Implement an iterator to flatten a 2d vector. + +Just move pointers carefully with next(), hashNext() + +#### Basic Implementation using x, y corrdinate +- 就是把2D list里面的element全部遍历一遍。 +- 跟一个nxn的matrix遍历,是没区别的拉; 所有来个x,y,把2d list跑一变。 + +#### Always return item at index 0, and remove from list? +- list 方便remove, 考虑吧reduce input vector (就像给的是linked list 一样) + + + +--- + +**491. [The Spiral Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Spiral%20Matrix%20II.java)** Level: Medium Tags: [Array] + + +#### Move forward till end +- Similar concept as `The Maze`: keep walking until hit wall, turn back +- fix direction `dx[direction % 4]` + + + +--- + +**492. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**493. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + +**494. [Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)** Level: Hard Tags: [Backtracking, DFS] + + +#### DFS +- Different from regular dfs to visit all, the robot move() function need to be called, backtrack needs to move() manually and backtracking path shold not be blocked by visited positions +- IMPORTANT: Mark on the way in using set, but `backtrack directly without re-check against set` +- Mark coordinate 'x@y' +- Backtrack: turn 2 times to revert, move 1 step, and turn 2 times to revert back. +- Direction has to be up, right, down, left. +- `int [] dx = {-1, 0, 1, 0};`, `int[] dy = {0, 1, 0, -1};` + + + +--- + +**495. [Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)** Level: Hard Tags: [DP] + + + + +--- + diff --git a/Solution$ListNode.class b/Solution$ListNode.class deleted file mode 100644 index 9246a9c..0000000 Binary files a/Solution$ListNode.class and /dev/null differ diff --git a/Solution.class b/Solution.class deleted file mode 100644 index f85e850..0000000 Binary files a/Solution.class and /dev/null differ diff --git a/Solution.java b/Solution.java index 2f333ed..ea3e200 100644 --- a/Solution.java +++ b/Solution.java @@ -1,76 +1,103 @@ +import java.io.*; import java.util.*; -public class Solution { - static int index; - static HashMap> map; - static Queue queue; + + + +/* + * To execute Java, please define "static void main" on a class + * named Solution. + * + * If you need more classes, simply define them inline. + */ + +// +class Solution { + + + public static ArrayList validateClock() { + //p:1, n:5, d: 10 + int[] nums = {1,1,1,1,5,5,5,5,10,10,10,10}; + ArrayList rst = new ArrayList(); + ArrayList list = new ArrayList(); + + helper(rst, " ", 4,4,4); + + return rst; - public static void ZigzagIterator(List v1, List v2) { - map = new HashMap>(); - queue = new LinkedList(); - index = 0; - if (v1 != null) { - map.put(0, v1); - } - if (v2 != null) { - map.put(1, v2); - } - - //init with head item - for (Map.Entry> entry : map.entrySet()) { - queue.offer(entry.getValue().get(0)); - entry.getValue().remove(0); - index++; - if (index == map.size()) { - index = 0; - } - } + } + + public static void helper(ArrayList rst, ArrayList list, int[] nums, int p, int n, int d) { + if (p < 0 || n < 0 || d < 0) { + return; + } + if (list.size() == nums.length) { + if (validate(list)) { + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < list.size(); i++) { + if (list.get(i) == 1) sb.append("P"); + if (list.get(i) == 5) sb.append("N"); + if (list.get(i) == 10) sb.append("D"); + } + rst.add(sb.toString()); + } + return; + } + + for (int i = 0; i < nums.length; i++) { + if (nums[i] == 1 && p > 0) { + list.add(nums[i]); + helper(rst, list, nums, p - 1, n, d); + } + else if (nums[i] == 5 && n > 0) { + list.add(nums[i]); + helper(rst, list, nums, p, n - 1, d); + } + + else if (nums[i] == 10 && n > 0) { + list.add(nums[i]); + helper(rst, list, nums, p, n, d - 1); + } + list.remove(list.size() - 1); } - - public static int next() { - int next = queue.poll(); - while (true) { - if (map.get(index).size() != 0) { - queue.offer(map.get(index).get(0)); - map.get(index).remove(0); - index++; - if (index == map.size()) { - index = 0; - } - break; - } - index++; - if (index == map.size()) { - index = 0; - break; - } - } - return next; + + } + + public static boolean validate(String str) {//{p}, 0, c = + if (str == null || str.length() < 12) { + return false; } - - public static boolean hasNext() { - return !queue.isEmpty(); + String[] arr = str.split(" "); + String[] test = {" "," "," "," "," "," "," "," "," "," "," "," "}; + + for (int i = 0; i < arr.length; i++) { + int num = Integer.parseInt(arr[i]); + + if (num + i >= 12) { + int index = (num + i) % 12; + if (!test[index].equals(" ")) { + return false; + } else { + test[i] = arr[i]; + } + } } - - - public static void main(String[] args){ - System.out.println("START"); - List v1 = new ArrayList(); - List v2 = new ArrayList(); - v1.add(1);v1.add(3); - v2.add(2); v2.add(4); - ZigzagIterator(v1, v2); - - System.out.println(hasNext()); - - System.out.println("END " ); + return true; + } + + + + public static void main(String[] args) { + ArrayList rst = validateClock(); + + for (String string : rst) { + System.out.println(string); } + } } - - - +///Generate all possible solutions, then validate them all. diff --git a/SystemDesign.md b/SystemDesign.md new file mode 100755 index 0000000..a913296 --- /dev/null +++ b/SystemDesign.md @@ -0,0 +1,270 @@ +This document is to document the learning process for **System Design**. +A few concepts to research: +How to split data between servers? How do multiple server communicate? MapReduce? +How does server process heavy calculation in background? Threading? NodeJS threading? + +## Good Concepts: +- For any system, consider: how much data does the system handle? how much data does the system need to store? +- Popular hashing algorithm: like 35. What about md5? +- Scalibility + +## Steps: +1. **Scope** the problem: Don't make assumptions; Ask questions; Understand the constraints and use cases. +2. Sketch up an **abstract design** that illustrates the basic components of the system and the relationships between them. +3. Think about the **bottlenecks** these components face when the system scales. +4. Address these bottlenecks by using the fundamentals principles of **scalable** system design. + +- Mine Usual steps: might be too much details on scoping but less efforts onto bottlenecks and scalibility issues. +1. Identify the key requirements && constraints +2. Identify the users && Server +3. Identify inputs/ouputs from different users +*** Calculate the bandwidth and storage: the amount of data the server handles, and the amount of data server stores. +4. Based on (1~3), list out the potential features that needs to be implemented +5. Pick technologies that will be able to implement the features above: + * backend server + * user client + * method of storage + * method of data trasmissio +6. How does the system scale? What are the limits and trade-off in current design? What improvmenet can be done to design it differently? + + + +## Scalibility lecture +- key concepts: + Vertical scaling: add more cpu,ram,hard-drive + Horizontal scaling:add more cheaper computer, distribute traffice && storage into these machiens. + Caching: save some pre-processed data, like sticky session, pre-quried sqls + Load balancing: determine which web server to hit; determine which db to hit if having multiple db + Database replication: for faster read; for reduancy safety + Database partitioning: for faster performance for some contents, or distributed storage + Using NoSQL instead of scaling a relational database + Being asynchronous + +- Load Balancer? + load balancer is even good to protect server: now backend server can be on LAN with load balancer, so they are protected; and we just expose load balancer to clients. + How does load balancer work? + There are lots of trade-offs: look at 30:00 of the video. That's all the tradeoffs (http://www.hiredintech.com/system-design/scalability-fundamentals/) + - round robin: the load balancer works as a fancy DNS machine, take turns to assign task/request to machine 1,2,3,4...etc. (if we do `nsloopup google.com` on google.com, we can see google does that) + - store states/sessions into a dedicated server. but it indroduces server-failure issue. -use RAID, raplication + RAID0: strip data at two hardrives, little on each + RAID1: mirror data. store at both places + RAID10: combination of RAID0 && RAID1. + RAID5: 3 drives only 1 of them is used for reduendency. + RAID6: multiple drives. any drive can die, then replace, and no data lost. + - split by the type of files request: html server, image server; load balancer + - split based on actual load (less possible), send package to less busy server + Options: + Software: + ELB: elastic load balancer + HAProxy + LVS + hardware: + Barracuda + Cisco + Citrix: over-priced, $20k + F5 + Problem: sticky session (if you revisit the site multiple times seprately, you will still hit the same server) + - can store serverID in the cookie (one downside: safety, showed the whole world about server IP). So again, can store a random number/hashed value on Load Balancer. It shows which serverID to hit once revisiting. Remember which back-end server to send cookie to. +- Caching: + file-based caching approach: send .html out. + down-side: 1. space. 2. old generated files are really hard to change. + mysqul-query cashed: for identical sql request + memcached: store in memory of the idetical query. Like a table. store . + next time when checking, try look up key. + Down-side: run out of ram. Solution: LRUCahe, remove old records(double-linked-list) + +- Replication: + Master && multiple Slaves + advantage: 1. Same request/query will be paste to all slaves, so master fail, just put up one salve; 2. If READ heavy, they are redundant server to lots readings. + disadvantage: + Must have at least load balancer: that is one-point-failure if just one load balancer + +- Partitioning: + based on user informaiton, like name. Put common user (same last name) into certain servers. + +- Bottleneck: data traffice and data storage +- +------Example +- 1:26:00 of the video (http://www.hiredintech.com/system-design/scalability-fundamentals/) +We have backend web server +Sticky session: use cookie and store server ID +Use load balancer that two web server connects: +Use load balancer to link to the two master db: + Two master db that talks to each other: master-master replication +Now we need 2 load balancer to prevent one-point-failure at the webServer-db connection. +Now we also need 2 load balancers at the (which web server to hit) level, that is another 1-pointer-failure +Switch for the complex connections: + Now we have so many connections: we need switches to handle the connections. + We need at least two ports on each device to: go to the correct switch. Becareful with loop. +- Eventually we will handle: (1:34:00) + Scalibility + Redundancy + High probablity of up time + Resolution against failure + +- Another big issue: + What is ISP goes down? The whole package mentioned above go down? + Amazon solution, another ISP, called avalibility zone: like west, asia, south american. + How to distribute IP on different data center? Load Balancer at the DNS level, global Load Balancer. (Note, if in one building, could be staying at this building) + Well, if a building is gone, once your TTL(Time to live) is expired, you will be re-route to another ISP building. + +- Before getting into building: + Firewall, only a few ports are open: like tcp80, 443, 22 (ssh) + can allow https before first load balancer inside building, and decrypt it after first load balancer + at db lever, only 3306 is allowed. +Reason to lock, for example, 3306 not allowed at entering building? Because no one needs to inject query to db from outside of building; sql query usually only need to be done within the building, so lock the building up, don't allow bad sql injection : ) + + +### URL convention Example Final (http://www.hiredintech.com/system-design/final-thoughts/) +- simple rules: + Always start a single machine + Benchmark the bottle necks, where it indroduces complexity. (No need to add extra complexity) + Think about the questions like: why sql vs. non-sql? + +- Scalable design: + 1. Application, web server, handle requests/traffic + * start with 1 server + * it's better to measure the spark traffic (highest we get) + * add load balancer, and a cluster of servers. (could use amazon elastic load balancer, to automatically add more servers) + + 2. Data Storage + 1) Billions of object, 2) each object is small, 3) no relationship between object, 4) read 9x more than write (360read/s, 40writes/s), 5) 3TB urls, 36GB of hashes + We can use sql or non-sql + In example, go with MySQL + widely used + Mature tech + Clear scaling paradiams(sharding, master/slave replication, master/master replication) + Used by fb, twitter, google. + * index lookups are very fast (as fast as non-sql) + mappings: + hash: varchar(6) + original_url: varchar(512) + + Over years, it holds 3TB data. Storage-wise, it's okay. However, how to serve up quickly? + + * First, Use one MySQL table with two varchar fields + * Create uinique index on the hash(36GB+). We want to hold it in memory. However, we need to look up fast. + * For 1st stemp, vertical scaling of the MySQL machine, adding more RAM (nowadays ram are cheaper too : ) + * Eventually need to partition the data, into 5 partitions: 600GB of data, 8GB of indexes on each machine. (Partitioning early on, it helps to scale later, just add more nodes) + * One day, if read/write are super different, Master-Slave, ... + write to master, and read from slaves. + +## Design UBER +1. Requirements + Same requirements between user && driver: GPS tracking, detect pickup/drop off, cost calculation + Design a web app (or mobile app) for end user to make/cancel car reservation, make payment, rate driver. + Design a web app (or mobile app) for driver to accept/decline car reservation, accept payment, rate passenger. + Extra features beyond basics: car pool, split cost, rush-hour rate calculation. + +2. Identify the end systems: + Backend server that handles communication, data tracking, payment trasaction...etc + Passenger user app + Driver user app + +3. Inputs and outputs of each end system: + **Server should store every piece of information received or change to database during this process** + Server-input: + * Passenger create account, login, log off + * Driver create account, login, log off + * Passenger/Driver payment information + * driver locations + * passenger locations + * GPS location data before && during the trip + * request to pair driver && passenger + * request to start trip + * request to end trip + * request to drop reservation from driver + * request to drop reservation from passenger + * request to calculate cost per trip + * accept rating, comment, report ... + + Server-output: + * ack passenger account generation, login, logoff + * ack driver account generation, login, logoff + * ack driver/passenger payment info and store to db + * broadcast driver locations to passenger + * broadcast passenger locations to driver + * response pair confirmation to driver && passenger with contact information of driver&&passenger + * start a trip between driver && passenger + * end a trip between driver && passenger + * calculate recommended route and send to driver&&passenger + * response reservation dropped from driver with panelty + * response reservation dropped from passenger with panelty + * response cost calculated to passenger and intiate payment charge to passenger + * response cost calculated to driver and initiate payment to driver + * store rating, comment, report into database. Trigger messages to coresponding party. + + Passenger-input: data that passenger receives + * ack of login and ready to pick driver + * status of drivers around current location + * auto-matched driver and related information about driver, car condition, price rate + * confirmation of car reservation with driver's info + * trip route and estimated time + * GPS tracking of car arriving or GPS tracking during the trip + * notification of payment + * notification of canceled trip + * prompt to rate and comment + * phone call from driver (extenal) + + Passenger-output: action that passenger takes + * create account + * login + * add payment information + * put current location and destination location + * select type of uber car, and start searching(matching) + * accept/decline auto-matched driver + * call driver (extenal) + * cancel reservation + * put comment, rating, or report to the system + * automatically pay (be charged) of the trip cost from server + + Driver-input: + * ack of login and ready to start driving + * auto-recommended passenger's information: location, passenger rating + * confirmation of reservation && current waiting location + * trip route and estimated time + * GPS tracking of car arriving or GPS tracking during the trip + * notification of payment + * notification of trip cancelation from passenger + * prompt to rate and comment passenger + * phone call from passenger (extenal) + * receive payment from server + + Driver-output: + * create account + * login and set status avaialbe + * add payment info + * accept/decline recommended match + * call passenger (extenal) + * cancel reservation + * put comment && rating of passenger + + +4. Summarize core features and tools to implement + * account management: + * payment integration: paypal integration? some other credicar processing tools + * matching algorithm: core problem to solve. Figure out the cheapest match for passenger, and time&&cost efficient match for driver. There might be lots of constraints to think about for the matching algorithm: cost&&time to pick up passenger around multiple driver, based on driver preference to filter type of trip (farest distance to travel) + * trip process monitoring, notification: RESTful calls between server && driver && passenger. + * GPS location tracking and route calculation: Google map? + * trip cost calculation: calculated based on route length, trip time, rate at specific time, complains filed by passenger + * rating && comment system: RESTful post from driver/passenger + +5. Technologies potentially needed: + Tools:A server to handle RESTful calls, support live connection between nodes, handle trip matching algorithm, handle cost calculation. + * Node.js with expressJS: provide RESTful API + * Socket.io for concurrent communication between server, user, driver in the same socket room. + * Data storag: Sql or non-sql? Why? Later on, it seems easy to split data by region, so non-sql db seems okay to use. + * Algorithm: How does heavy calculation handled on Nodejs? Should it be something to do with multi-threading? + +6. How does the system scale? What are the limits and trade-off in current design? What improvmenet can be done to design it differently? + First, assume everything is running based off one giant server: all conversations and calculation are handled via this server. + Second, the size of data might be too costly to search, calculate, and it's not practical to store everything on one machine: + * user/driver account info might be too large + * concurrent calculation for trip matching + * number concurrent trip monitoring and conversation + * concurrent calculation for trip cost + Therefore, it seems wiser to split onto multiple servers. + Third question: in which way to split the data? It seems we need to treat different type of data separately + * For actively moving driver and user: we can store their location data dynamically by region. Once moved into/out of one regin, poll the information from one server into another server. Here, it's wise to use HashTable to store as . Calculation algorithm may also happen on this server, because both driver && passenger are on this server, defined by region. + * Now we've splitted data into servers by region. However, for same region, it might be too much work for one machine. We need to split data aross different machines for same region. Here, use another Hash lookup table to store to split data into even smaller sections to fit in different server. Now we can have more **regionServer**. + * A improvement that can be done: for relative static information account,payment,rating, they are only used once or twice during a trip. It might be okay to store these information on a type of server that specifically handles account look up, let's call it **accountServer**. Again, there might be too much account info to store, we can build lookup tables to store . Double again, do MapReduce to hash the keys so that we can split the look up table if the table is too big to store on one server. diff --git a/TagREADME.md b/TagREADME.md new file mode 100644 index 0000000..5f4e101 --- /dev/null +++ b/TagREADME.md @@ -0,0 +1,1942 @@ + +Table of Contents +================= + +* [Array (107)](#array-107) +* [DFS (95)](#dfs-95) +* [DP (85)](#dp-85) +* [Hash Table (69)](#hash-table-69) +* [String (60)](#string-60) +* [Tree (55)](#tree-55) +* [Binary Search (40)](#binary-search-40) +* [BFS (39)](#bfs-39) +* [Math (38)](#math-38) +* [Two Pointers (38)](#two-pointers-38) +* [Divide and Conquer (35)](#divide-and-conquer-35) +* [Backtracking (32)](#backtracking-32) +* [Linked List (31)](#linked-list-31) +* [Stack (30)](#stack-30) +* [Sort (22)](#sort-22) +* [BST (22)](#bst-22) +* [Design (21)](#design-21) +* [Sequence DP (21)](#sequence-dp-21) +* [PriorityQueue (19)](#priorityqueue-19) +* [Greedy (18)](#greedy-18) +* [Bit Manipulation (17)](#bit-manipulation-17) +* [Coordinate DP (17)](#coordinate-dp-17) +* [Heap (16)](#heap-16) +* [Union Find (15)](#union-find-15) +* [Enumeration (15)](#enumeration-15) +* [Binary Tree (13)](#binary-tree-13) +* [Segment Tree (12)](#segment-tree-12) +* [Memoization (12)](#memoization-12) +* [Trie (11)](#trie-11) +* [Graph (11)](#graph-11) +* [Subarray (11)](#subarray-11) +* [PreSum (10)](#presum-10) +* [Lint (9)](#lint-9) +* [MinHeap (9)](#minheap-9) +* [Backpack DP (8)](#backpack-dp-8) +* [Status DP (7)](#status-dp-7) +* [Double Sequence DP (6)](#double-sequence-dp-6) +* [Sliding Window (6)](#sliding-window-6) +* [Quick Sort (6)](#quick-sort-6) +* [Partition DP (5)](#partition-dp-5) +* [Sweep Line (5)](#sweep-line-5) +* [Topological Sort (5)](#topological-sort-5) +* [Expression Tree (5)](#expression-tree-5) +* [Merge Sort (4)](#merge-sort-4) +* [Game Theory (4)](#game-theory-4) +* [Combination (4)](#combination-4) +* [Interval DP (4)](#interval-dp-4) +* [MaxHeap (4)](#maxheap-4) +* [Permutation (3)](#permutation-3) +* [Binary Indexed Tree (3)](#binary-indexed-tree-3) +* [Minimum Binary Tree (3)](#minimum-binary-tree-3) +* [Basic Implementation (3)](#basic-implementation-3) +* [Queue (3)](#queue-3) +* [Partition (3)](#partition-3) +* [Sequence DFS (2)](#sequence-dfs-2) +* [Double Recursive (2)](#double-recursive-2) +* [TreeSet (2)](#treeset-2) +* [Matrix DFS (2)](#matrix-dfs-2) +* [MiniMax (2)](#minimax-2) +* [Deque (2)](#deque-2) +* [Geometry (2)](#geometry-2) +* [Priority Queue (1)](#priority-queue-1) +* [HashHeap (1)](#hashheap-1) +* [Binary Search Tree (1)](#binary-search-tree-1) +* [KMP (1)](#kmp-1) +* [Brainteaser (1)](#brainteaser-1) +* [Interval (1)](#interval-1) +* [Bitwise DP (1)](#bitwise-dp-1) +* [Quick Select (1)](#quick-select-1) +* [Bucket Sort (1)](#bucket-sort-1) +* [Monotonous Stack (1)](#monotonous-stack-1) +* [PreProduct (1)](#preproduct-1) +* [Reservior Sampling (1)](#reservior-sampling-1) +* [TreeMap (1)](#treemap-1) +* [Two Stacks (1)](#two-stacks-1) +* [Tree DP (1)](#tree-dp-1) + + + + + + +## Array (107) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Missing Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Ranges.java)|Medium|Java|[Array]|| +|1|[Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Permutation.java)|Medium|Java|[Array]|| +|2|[Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Exclude%20Itself.java)|Medium|Java|[Array]|| +|3|[Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Rotated%20Sorted%20Array.java)|Easy|Java|[Array]|| +|4|[Triangle Count.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangle%20Count.java)|Medium|Java|[Array]|| +|5|[Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/Array%20Partition%20I.java)|Easy|Java|[Array]|| +|6|[1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/1-bit%20and%202-bit%20Characters.java)|Easy|Java|[Array]|| +|7|[Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Non-decreasing%20Array.java)|Easy|Java|[Array]|| +|8|[Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Consecutive%20Ones.java)|Easy|Java|[Array]|| +|9|[Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)|Easy|Java|[Array]|| +|10|[Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number%20At%20Least%20Twice%20of%20Others.java)|Easy|Java|[Array]|| +|11|[Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Toeplitz%20Matrix.java)|Easy|Java|[Array]|| +|12|[Game of Life.java](https://github.com/awangdev/LintCode/blob/master/Java/Game%20of%20Life.java)|Medium|Java|[Array]|| +|13|[First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Missing%20Positive.java)|Hard|Java|[Array]|| +|14|[Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element%20II.java)|Medium|Java|[Array]|| +|15|[Merge Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array%20II.java)|Easy|Java|[Array]|| +|16|[Summary Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Summary%20Ranges.java)|Medium|Java|[Array]|| +|17|[Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximize%20Distance%20to%20Closest%20Person.java)|Easy|Java|[Array]|| +|18|[The Spiral Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Spiral%20Matrix%20II.java)|Medium|Java|[Array]|| +|19|[Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)|Medium|Java|[Array, BFS, Backtracking, Bit Manipulation, DFS]|| +|20|[Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)|Medium|Java|[Array, BFS, Backtracking, DFS]|| +|21|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|Java|[Array, BFS, Backtracking, DFS, Hash Table, String]|| +|22|[Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)|Hard|Java|[Array, BST, Binary Search, DP, Queue, TreeSet]|| +|23|[K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)|Hard|Java|[Array, BST, TreeSet]|| +|24|[Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)|Medium|Java|[Array, Backpack DP, DP]|| +|25|[Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|26|[Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|27|[Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|28|[Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)|Medium|Java|[Array, Backtracking, DFS]|| +|29|[Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array%20II.java)|Medium|Java|[Array, Binary Search]|| +|30|[Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)|Medium|Java|[Array, Binary Search]|| +|31|[Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)|Medium|Java|[Array, Binary Search]|| +|32|[Find Minimum in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array%20II.java)|Hard|Java|[Array, Binary Search]|| +|33|[Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)|Medium|Java|[Array, Binary Search]|| +|34|[Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)|Medium|Java|[Array, Binary Search]|| +|35|[Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array.java)|Medium|Java|[Array, Binary Search]|| +|36|[Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)|Hard|Java|[Array, Binary Search, DFS, Divide and Conquer]|| +|37|[Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)|Review|Java|[Array, Binary Search, PreSum]|| +|38|[Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)|Medium|Java|[Array, Binary Search, Subarray, Two Pointers]|| +|39|[Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)|Hard|Java|[Array, Binary Search, Two Pointers]|| +|40|[Two Sum II - Input array is sorted.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20II%20-%20Input%20array%20is%20sorted.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|41|[2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum%20II.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|42|[Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|43|[Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)|Easy|Java|[Array, Bit Manipulation, Divide and Conquer]|| +|44|[Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)|Easy|Java|[Array, Bit Manipulation, Math]|| +|45|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|Java|[Array, Coordinate DP, DFS, DP, Memoization]|| +|46|[Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|47|[Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|48|[Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)|Easy|Java|[Array, Coordinate DP, DP]|| +|49|[Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|50|[Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)|Easy|Java|[Array, Coordinate DP, DP]|| +|51|[Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)|Hard|Java|[Array, Coordinate DP, DP, Greedy]|| +|52|[Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)|Medium|Java|[Array, Coordinate DP, DP, Memoization]|| +|53|[Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)|Easy|Java|[Array, DFS]|| +|54|[Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)|Easy|Java|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|| +|55|[Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Hash Table, Tree]|| +|56|[Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Tree]|| +|57|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|Java|[Array, DP, Game Theory, Interval DP, Memoization]|| +|58|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|Java|[Array, DP, Game Theory, Memoization, MiniMax]|| +|59|[Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)|Medium|Java|[Array, DP, Greedy]|| +|60|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|Java|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|| +|61|[Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|62|[Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)|Medium|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|63|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Subarray]|| +|64|[Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)|Hard|Java|[Array, DP, Hash Table, Stack]|| +|65|[Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)|Hard|Java|[Array, DP, Sequence DP]|| +|66|[Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)|Easy|Java|[Array, DP, Sequence DP]|| +|67|[Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)|Medium|Java|[Array, DP, Subarray]|| +|68|[Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)|Medium|Java|[Array, Design, Hash Table]|| +|69|[Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)|Medium|Java|[Array, Enumeration]|| +|70|[Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)|Medium|Java|[Array, Enumeration]|| +|71|[Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)|Medium|Java|[Array, Enumeration, Greedy, PriorityQueue, Queue]|| +|72|[Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)|Hard|Java|[Array, Geometry, Hash Table, Math]|| +|73|[Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Celebrity.java)|Medium|Java|[Array, Greedy]|| +|74|[Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate.java)|Easy|Java|[Array, Hash Table]|| +|75|[Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20II.java)|Easy|Java|[Array, Hash Table]|| +|76|[Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum.java)|Easy|Java|[Array, Hash Table]|| +|77|[Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Anagrams.java)|Medium|Java|[Array, Hash Table]|| +|78|[Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)|Medium|Java|[Array, Hash Table, PreSum]|| +|79|[Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)|Easy|Java|[Array, Hash Table, PreSum, Subarray]|| +|80|[Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)|Medium|Java|[Array, Hash Table, PreSum, Subarray]|| +|81|[Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)|Hard|Java|[Array, Hash Table, Union Find]|| +|82|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|Java|[Array, Interval, PriorityQueue, Sort, Sweep Line]|| +|83|[Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)|Easy|Java|[Array, Math]|| +|84|[Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)|Medium|Java|[Array, Math]|| +|85|[Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)|Hard|Java|[Array, Monotonous Stack, Stack]|| +|86|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|Java|[Array, Partition, Quick Sort, Sort, Two Pointers]|| +|87|[Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Except%20Self.java)|Medium|Java|[Array, PreProduct]|| +|88|[Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)|Hard|Java|[Array, PriorityQueue, Sort]|| +|89|[Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)|Medium|Java|[Array, PriorityQueue, Sort, Sweep Line]|| +|90|[Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)|Easy|Java|[Array, Quick Select, Quick Sort]|| +|91|[Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)|Medium|Java|[Array, Quick Sort, Sort, Two Pointers]|| +|92|[Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)|Medium|Java|[Array, Sort]|| +|93|[The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)|Medium|Java|[Array, Sort, Two Pointers]|| +|94|[Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)|Hard|Java|[Array, Stack, Two Pointers]|| +|95|[Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Characters.java)|Easy|Java|[Array, String]|| +|96|[Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)|Easy|Java|[Array, Subarray]|| +|97|[My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)|Medium|Java|[Array, TreeMap]|| +|98|[Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)|Easy|Java|[Array, Two Pointers]|| +|99|[3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/3%20Sum%20Closest.java)|Medium|Java|[Array, Two Pointers]|| +|100|[3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum.java)|Medium|Java|[Array, Two Pointers]|| +|101|[3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum%20Smaller.java)|Medium|Java|[Array, Two Pointers]|| +|102|[Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array.java)|Easy|Java|[Array, Two Pointers]|| +|103|[Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array%20II.java)|Medium|Java|[Array, Two Pointers]|| +|104|[Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)|Medium|Java|[Array, Two Pointers]|| +|105|[Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array.java)|Easy|Java|[Array, Two Pointers]|| +|106|[Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Move%20Zeroes.java)|Easy|Java|[Array, Two Pointers]|| + + + + + + +## DFS (95) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)|Medium|Java|[Array, BFS, Backtracking, Bit Manipulation, DFS]|| +|1|[Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)|Medium|Java|[Array, BFS, Backtracking, DFS]|| +|2|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|Java|[Array, BFS, Backtracking, DFS, Hash Table, String]|| +|3|[Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|4|[Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|5|[Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|6|[Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)|Medium|Java|[Array, Backtracking, DFS]|| +|7|[Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)|Hard|Java|[Array, Binary Search, DFS, Divide and Conquer]|| +|8|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|Java|[Array, Coordinate DP, DFS, DP, Memoization]|| +|9|[Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)|Easy|Java|[Array, DFS]|| +|10|[Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)|Easy|Java|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|| +|11|[Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Hash Table, Tree]|| +|12|[Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Tree]|| +|13|[Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)|Medium|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|14|[Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)|Hard|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|15|[Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Nested%20List%20Weight%20Sum.java)|Easy|Java|[BFS, DFS]|| +|16|[Find the Connected Component in the Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Connected%20Component%20in%20the%20Undirected%20Graph.java)|Medium|Java|[BFS, DFS]|| +|17|[Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)|Medium|Java|[BFS, DFS]|| +|18|[The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)|Medium|Java|[BFS, DFS]|| +|19|[Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)|Review|Java|[BFS, DFS, DP]|| +|20|[Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|Java|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|| +|21|[Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)|Medium|Java|[BFS, DFS, Graph]|| +|22|[Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)|Medium|Java|[BFS, DFS, Graph, Topological Sort]|| +|23|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|Java|[BFS, DFS, Graph, Tree, Union Find]|| +|24|[Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|25|[Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|26|[Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|27|[Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)|Medium|Java|[BFS, DFS, Hash Table, Tree]|| +|28|[Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)|Medium|Java|[BFS, DFS, Matrix DFS, Union Find]|| +|29|[Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)|Medium|Java|[BFS, DFS, Matrix DFS, Union Find]|| +|30|[The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)|Medium|Java|[BFS, DFS, PriorityQueue]|| +|31|[The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)|Hard|Java|[BFS, DFS, PriorityQueue]|| +|32|[Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)|Easy|Java|[BFS, DFS, Stack, Tree]|| +|33|[Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)|Medium|Java|[BFS, DFS, Topological Sort]|| +|34|[Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|35|[Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|36|[Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|37|[Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)|Medium|Java|[BFS, DFS, Tree]|| +|38|[Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)|Medium|Java|[BFS, DFS, Tree]|| +|39|[Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List]|| +|40|[Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List, Tree]|| +|41|[Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Divide and Conquer, Tree]|| +|42|[Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)|Medium|Java|[BST, DFS, Stack, Tree]|| +|43|[Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Tree]|| +|44|[Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)|Hard|Java|[BST, DFS, Tree]|| +|45|[Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)|Easy|Java|[Backtracking, Binary Tree, DFS]|| +|46|[Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)|Medium|Java|[Backtracking, Combination, DFS]|| +|47|[Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning.java)|Medium|Java|[Backtracking, DFS]|| +|48|[Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)|Hard|Java|[Backtracking, DFS]|| +|49|[Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)|Medium|Java|[Backtracking, DFS, DP]|| +|50|[Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)|Hard|Java|[Backtracking, DFS, DP, Hash Table, Memoization]|| +|51|[Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)|Hard|Java|[Backtracking, DFS, Divide and Conquer, String]|| +|52|[Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)|Medium|Java|[Backtracking, DFS, Permutation]|| +|53|[Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)|Medium|Java|[Backtracking, DFS, Sequence DFS, String]|| +|54|[Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)|Medium|Java|[Backtracking, DFS, String]|| +|55|[Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)|Easy|Java|[Backtracking, DFS, Tree]|| +|56|[Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)|Hard|Java|[Backtracking, DFS, Trie]|| +|57|[Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)|Hard|Java|[Binary Search, DFS, Divide and Conquer]|| +|58|[Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Binary%20Tree%20to%20Linked%20List.java)|Medium|Java|[Binary Tree, DFS]|| +|59|[Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|60|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|61|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|62|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|| +|63|[Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Stack]|| +|64|[Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Stack]|| +|65|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|Java|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|| +|66|[Flood Fill.java](https://github.com/awangdev/LintCode/blob/master/Java/Flood%20Fill.java)|Easy|Java|[DFS]|| +|67|[Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)|Medium|Java|[DFS, DP]|| +|68|[Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)|Medium|Java|[DFS, DP, Interval DP, Memoization]|| +|69|[House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)|Medium|Java|[DFS, DP, Status DP, Tree]|| +|70|[Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)|Hard|Java|[DFS, DP, Tree, Tree DP]|| +|71|[Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)|Medium|Java|[DFS, Divide and Conquer]|| +|72|[Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)|Medium|Java|[DFS, Divide and Conquer, Double Recursive, Tree]|| +|73|[Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)|Medium|Java|[DFS, Divide and Conquer, Stack]|| +|74|[Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)|Easy|Java|[DFS, Divide and Conquer, Tree]|| +|75|[Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|76|[Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|77|[Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|78|[Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)|Easy|Java|[DFS, Divide and Conquer, Tree]|| +|79|[Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)|Easy|Java|[DFS, Double Recursive, Tree]|| +|80|[Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)|Medium|Java|[DFS, Enumeration, Math, Sequence DFS]|| +|81|[Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)|Hard|Java|[DFS, Graph, Tree, Union Find]|| +|82|[Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)|Hard|Java|[DFS, Greedy, Math]|| +|83|[Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)|Medium|Java|[DFS, Hash Table, Hash Table, Union Find]|| +|84|[Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)|Medium|Java|[DFS, Hash Table, Tree]|| +|85|[Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Same%20Tree.java)|Easy|Java|[DFS, Tree]|| +|86|[Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Balanced%20Binary%20Tree.java)|Medium|Java|[DFS, Tree]|| +|87|[Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Depth%20of%20Binary%20Tree.java)|Easy|Java|[DFS, Tree]|| +|88|[Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)|Easy|Java|[DFS, Tree]|| +|89|[Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)|Easy|Java|[DFS, Tree]|| +|90|[Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)|Easy|Java|[DFS, Tree]|| +|91|[Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)|Medium|Java|[DFS, Tree]|| +|92|[Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum.java)|Easy|Java|[DFS, Tree]|| +|93|[Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)|Medium|Java|[DFS, Tree]|| +|94|[Populating Next Right Pointers in Each Node II.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node%20II.java)|Medium|Java|[DFS, Tree]|| + + + + + + +## DP (85) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)|Hard|Java|[Array, BST, Binary Search, DP, Queue, TreeSet]|| +|1|[Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)|Medium|Java|[Array, Backpack DP, DP]|| +|2|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|Java|[Array, Coordinate DP, DFS, DP, Memoization]|| +|3|[Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|4|[Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|5|[Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)|Easy|Java|[Array, Coordinate DP, DP]|| +|6|[Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|7|[Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)|Easy|Java|[Array, Coordinate DP, DP]|| +|8|[Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)|Hard|Java|[Array, Coordinate DP, DP, Greedy]|| +|9|[Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)|Medium|Java|[Array, Coordinate DP, DP, Memoization]|| +|10|[Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)|Easy|Java|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|| +|11|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|Java|[Array, DP, Game Theory, Interval DP, Memoization]|| +|12|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|Java|[Array, DP, Game Theory, Memoization, MiniMax]|| +|13|[Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)|Medium|Java|[Array, DP, Greedy]|| +|14|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|Java|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|| +|15|[Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|16|[Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)|Medium|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|17|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Subarray]|| +|18|[Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)|Hard|Java|[Array, DP, Hash Table, Stack]|| +|19|[Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)|Hard|Java|[Array, DP, Sequence DP]|| +|20|[Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)|Easy|Java|[Array, DP, Sequence DP]|| +|21|[Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)|Medium|Java|[Array, DP, Subarray]|| +|22|[Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)|Review|Java|[BFS, DFS, DP]|| +|23|[Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)|Medium|Java|[BFS, DP, Math, Partition DP]|| +|24|[Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)|Medium|Java|[BST, DP, Divide and Conquer, Tree]|| +|25|[Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DP, Tree]|| +|26|[Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)|Medium|Java|[Backpack DP, DP]|| +|27|[Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change%202.java)|Medium|Java|[Backpack DP, DP]|| +|28|[Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)|Medium|Java|[Backpack DP, DP]|| +|29|[Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)|Medium|Java|[Backpack DP, DP]|| +|30|[Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)|Medium|Java|[Backpack DP, DP]|| +|31|[Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)|Hard|Java|[Backpack DP, DP]|| +|32|[Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)|Medium|Java|[Backpack DP, DP, Memoization]|| +|33|[Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)|Medium|Java|[Backtracking, DFS, DP]|| +|34|[Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)|Hard|Java|[Backtracking, DFS, DP, Hash Table, Memoization]|| +|35|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|| +|36|[Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|| +|37|[Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)|Hard|Java|[Binary Search, Coordinate DP, DP]|| +|38|[Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)|Medium|Java|[Binary Search, Coordinate DP, DP, Memoization]|| +|39|[Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)|Hard|Java|[Binary Search, DP, Partition DP]|| +|40|[Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)|Medium|Java|[Bit Manipulation, Bitwise DP, DP]|| +|41|[Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)|Easy|Java|[Brainteaser, DP, Game Theory]|| +|42|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|Java|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|| +|43|[Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/Bomb%20Enemy.java)|Medium|Java|[Coordinate DP, DP]|| +|44|[Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Square.java)|Medium|Java|[Coordinate DP, DP]|| +|45|[Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)|Medium|Java|[Coordinate DP, DP]|| +|46|[Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)|Medium|Java|[Coordinate DP, DP, Math, Subarray]|| +|47|[Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)|Medium|Java|[Coordinate DP, DP, Status DP]|| +|48|[Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)|Medium|Java|[DFS, DP]|| +|49|[Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)|Medium|Java|[DFS, DP, Interval DP, Memoization]|| +|50|[House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)|Medium|Java|[DFS, DP, Status DP, Tree]|| +|51|[Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)|Hard|Java|[DFS, DP, Tree, Tree DP]|| +|52|[Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)|Medium|Java|[DP]|| +|53|[k Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/k%20Sum.java)|Hard|Java|[DP]|| +|54|[Best Time to Buy and Sell Stock with Cooldown.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Cooldown.java)|Medium|Java|[DP]|| +|55|[Ones and Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Ones%20and%20Zeroes.java)|Hard|Java|[DP]|| +|56|[Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)|Hard|Java|[DP]|| +|57|[Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)|Hard|Java|[DP, Divide and Conquer, Interval DP, Memoization]|| +|58|[Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)|Medium|Java|[DP, Double Sequence DP, Sequence DP]|| +|59|[Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)|Hard|Java|[DP, Double Sequence DP, Sequence DP, String]|| +|60|[Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)|Medium|Java|[DP, Double Sequence DP, Sequence DP, String]|| +|61|[K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)|Hard|Java|[DP, Double Sequence DP, Sequence DP, Trie]|| +|62|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|Java|[DP, Enumeration, Heap, Math, PriorityQueue]|| +|63|[Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)|Hard|Java|[DP, Enumeration, Partition DP]|| +|64|[Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)|Medium|Java|[DP, Game Theory, Greedy]|| +|65|[Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)|Hard|Java|[DP, Hash Table]|| +|66|[Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)|Medium|Java|[DP, Hash Table, Sequence DP]|| +|67|[Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)|Hard|Java|[DP, Interval DP, String]|| +|68|[Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)|Medium|Java|[DP, Math]|| +|69|[Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)|Easy|Java|[DP, Math, Memoization]|| +|70|[Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)|Easy|Java|[DP, Memoization, Sequence DP]|| +|71|[Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)|Medium|Java|[DP, MiniMax]|| +|72|[Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)|Hard|Java|[DP, Partition DP]|| +|73|[Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)|Medium|Java|[DP, Partition DP, String]|| +|74|[Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/Range%20Sum%20Query%20-%20Immutable.java)|Easy|Java|[DP, PreSum]|| +|75|[House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber.java)|Easy|Java|[DP, Sequence DP]|| +|76|[Best Time to Buy and Sell Stock IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20IV.java)|Hard|Java|[DP, Sequence DP]|| +|77|[Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)|Easy|Java|[DP, Sequence DP]|| +|78|[Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)|Easy|Java|[DP, Sequence DP, Status DP]|| +|79|[House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)|Medium|Java|[DP, Sequence DP, Status DP]|| +|80|[Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)|Hard|Java|[DP, Sequence DP, Status DP]|| +|81|[Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)|Hard|Java|[DP, String]|| +|82|[Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)|Hard|Java|[DP, String]|| +|83|[Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Substring.java)|Medium|Java|[DP, String]|| +|84|[Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)|Medium|Java|[DP, String]|| + + + + + + +## Hash Table (69) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|Java|[Array, BFS, Backtracking, DFS, Hash Table, String]|| +|1|[Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Hash Table, Tree]|| +|2|[Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)|Hard|Java|[Array, DP, Hash Table, Stack]|| +|3|[Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)|Medium|Java|[Array, Design, Hash Table]|| +|4|[Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)|Hard|Java|[Array, Geometry, Hash Table, Math]|| +|5|[Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate.java)|Easy|Java|[Array, Hash Table]|| +|6|[Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20II.java)|Easy|Java|[Array, Hash Table]|| +|7|[Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum.java)|Easy|Java|[Array, Hash Table]|| +|8|[Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Anagrams.java)|Medium|Java|[Array, Hash Table]|| +|9|[Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)|Medium|Java|[Array, Hash Table, PreSum]|| +|10|[Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)|Easy|Java|[Array, Hash Table, PreSum, Subarray]|| +|11|[Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)|Medium|Java|[Array, Hash Table, PreSum, Subarray]|| +|12|[Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)|Hard|Java|[Array, Hash Table, Union Find]|| +|13|[Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)|Medium|Java|[BFS, DFS, Hash Table, Tree]|| +|14|[Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)|Hard|Java|[Backtracking, DFS, DP, Hash Table, Memoization]|| +|15|[Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)|Easy|Java|[Binary Search, Hash Table, Sort, Two Pointers]|| +|16|[Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)|Easy|Java|[Binary Search, Hash Table, Sort, Two Pointers]|| +|17|[H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)|Medium|Java|[Bucket Sort, Hash Table, Sort]|| +|18|[Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)|Medium|Java|[DFS, Hash Table, Hash Table, Union Find]|| +|19|[Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)|Medium|Java|[DFS, Hash Table, Hash Table, Union Find]|| +|20|[Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)|Medium|Java|[DFS, Hash Table, Tree]|| +|21|[Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)|Hard|Java|[DP, Hash Table]|| +|22|[Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)|Medium|Java|[DP, Hash Table, Sequence DP]|| +|23|[Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)|Hard|Java|[Design, Geometry, Hash Table]|| +|24|[LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)|Hard|Java|[Design, Hash Table]|| +|25|[Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)|Medium|Java|[Design, Hash Table]|| +|26|[ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)|Medium|Java|[Design, Hash Table]|| +|27|[LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)|Hard|Java|[Design, Hash Table, Linked List]|| +|28|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|Java|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|| +|29|[Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Sudoku.java)|Easy|Java|[Enumeration, Hash Table]|| +|30|[Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)|Easy|Java|[Enumeration, Hash Table, Math]|| +|31|[Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)|Hard|Java|[Greedy, Hash Table, Heap]|| +|32|[Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)|Hard|Java|[Greedy, Hash Table, Stack]|| +|33|[Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/Jewels%20and%20Stones.java)|Easy|Java|[Hash Table]|| +|34|[Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Anagram%20Mappings.java)|Easy|Java|[Hash Table]|| +|35|[Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/Island%20Perimeter.java)|Easy|Java|[Hash Table]|| +|36|[Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation.java)|Easy|Java|[Hash Table]|| +|37|[Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/Hash%20Function.java)|Easy|Java|[Hash Table]|| +|38|[HashWithCustomizedClass(LinkedList).java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithCustomizedClass(LinkedList).java)|Medium|Java|[Hash Table]|| +|39|[Rehashing.java](https://github.com/awangdev/LintCode/blob/master/Java/Rehashing.java)|Medium|Java|[Hash Table]|| +|40|[Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/Sparse%20Matrix%20Multiplication.java)|Medium|Java|[Hash Table]|| +|41|[Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)|Medium|Java|[Hash Table]|| +|42|[Contiguous Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Contiguous%20Array.java)|Medium|Java|[Hash Table]|| +|43|[4Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/4Sum.java)|Medium|Java|[Hash Table]|| +|44|[Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Isomorphic%20Strings.java)|Easy|Java|[Hash Table]|| +|45|[Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|| +|46|[Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|| +|47|[Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)|Medium|Java|[Hash Table, Linked List]|| +|48|[Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20List%20with%20Random%20Pointer.java)|Medium|Java|[Hash Table, Linked List]|| +|49|[Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)|Medium|Java|[Hash Table, Math]|| +|50|[Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)|Medium|Java|[Hash Table, Math]|| +|51|[Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Primes.java)|Easy|Java|[Hash Table, Math]|| +|52|[Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)|Medium|Java|[Hash Table, Math]|| +|53|[Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)|Medium|Java|[Hash Table, PreSum, Subarray]|| +|54|[Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Anagrams%20in%20a%20String.java)|Easy|Java|[Hash Table, Sliding Window]|| +|55|[Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)|Hard|Java|[Hash Table, Sliding Window, String]|| +|56|[Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)|Hard|Java|[Hash Table, Sliding Window, String, Two Pointers]|| +|57|[Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Anagram.java)|Easy|Java|[Hash Table, Sort]|| +|58|[Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Greater%20Element%20I.java)|Easy|Java|[Hash Table, Stack]|| +|59|[Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)|Easy|Java|[Hash Table, Stack, Tree]|| +|60|[Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Anagrams.java)|Medium|Java|[Hash Table, String]|| +|61|[Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Words.java)|Easy|Java|[Hash Table, String]|| +|62|[First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Unique%20Character%20in%20a%20String.java)|Easy|Java|[Hash Table, String]|| +|63|[Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)|Medium|Java|[Hash Table, String]|| +|64|[Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)|Hard|Java|[Hash Table, String, Trie]|| +|65|[Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)|Medium|Java|[Hash Table, String, Two Pointers]|| +|66|[Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)|Hard|Java|[Hash Table, String, Two Pointers]|| +|67|[Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20II.java)|Easy|Java|[Hash Table, Tree]|| +|68|[Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Word%20in%20Dictionary.java)|Easy|Java|[Hash Table, Trie]|| + + + + + + +## String (60) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|Java|[Array, BFS, Backtracking, DFS, Hash Table, String]|| +|1|[Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Characters.java)|Easy|Java|[Array, String]|| +|2|[Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)|Hard|Java|[Backtracking, DFS, Divide and Conquer, String]|| +|3|[Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)|Medium|Java|[Backtracking, DFS, Sequence DFS, String]|| +|4|[Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)|Medium|Java|[Backtracking, DFS, String]|| +|5|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|| +|6|[Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|| +|7|[Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)|Medium|Java|[Backtracking, String]|| +|8|[Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)|Medium|Java|[Basic Implementation, Enumeration, String]|| +|9|[Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)|Easy|Java|[Basic Implementation, String]|| +|10|[Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)|Hard|Java|[Bit Manipulation, String]|| +|11|[Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)|Hard|Java|[Coordinate DP, Stack, String]|| +|12|[Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)|Hard|Java|[DP, Double Sequence DP, Sequence DP, String]|| +|13|[Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)|Medium|Java|[DP, Double Sequence DP, Sequence DP, String]|| +|14|[Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)|Hard|Java|[DP, Interval DP, String]|| +|15|[Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)|Medium|Java|[DP, Partition DP, String]|| +|16|[Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)|Hard|Java|[DP, String]|| +|17|[Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)|Hard|Java|[DP, String]|| +|18|[Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Substring.java)|Medium|Java|[DP, String]|| +|19|[Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)|Medium|Java|[DP, String]|| +|20|[Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)|Hard|Java|[Enumeration, Math, String]|| +|21|[Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)|Hard|Java|[Enumeration, Math, String]|| +|22|[Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4.java)|Easy|Java|[Enumeration, String]|| +|23|[Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/Text%20Justification.java)|Hard|Java|[Enumeration, String]|| +|24|[Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)|Hard|Java|[Enumeration, String]|| +|25|[Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)|Hard|Java|[Hash Table, Sliding Window, String]|| +|26|[Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)|Hard|Java|[Hash Table, Sliding Window, String, Two Pointers]|| +|27|[Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Anagrams.java)|Medium|Java|[Hash Table, String]|| +|28|[Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Words.java)|Easy|Java|[Hash Table, String]|| +|29|[First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Unique%20Character%20in%20a%20String.java)|Easy|Java|[Hash Table, String]|| +|30|[Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)|Medium|Java|[Hash Table, String]|| +|31|[Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)|Hard|Java|[Hash Table, String, Trie]|| +|32|[Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)|Medium|Java|[Hash Table, String, Two Pointers]|| +|33|[Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)|Hard|Java|[Hash Table, String, Two Pointers]|| +|34|[Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)|Hard|Java|[KMP, String]|| +|35|[Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Multiply%20Strings.java)|Medium|Java|[Math, String]|| +|36|[String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/String%20to%20Integer%20(atoi).java)|Medium|Java|[Math, String]|| +|37|[Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Roman%20to%20Integer.java)|Easy|Java|[Math, String]|| +|38|[Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)|Easy|Java|[Math, String, Two Pointers]|| +|39|[Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)|Medium|Java|[Partition, Sort, String, Two Pointers]|| +|40|[Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Simplify%20Path.java)|Medium|Java|[Stack, String]|| +|41|[Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Parentheses.java)|Easy|Java|[Stack, String]|| +|42|[Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)|Medium|Java|[String]|| +|43|[Judge Route Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/Judge%20Route%20Circle.java)|Easy|Java|[String]|| +|44|[Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Change%20to%20Anagram.java)|Easy|Java|[String]|| +|45|[Compare Version Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Version%20Numbers.java)|Medium|Java|[String]|| +|46|[Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Strings.java)|Easy|Java|[String]|| +|47|[Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game.java)|Easy|Java|[String]|| +|48|[Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/Length%20of%20Last%20Word.java)|Easy|Java|[String]|| +|49|[Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20String.java)|Easy|Java|[String]|| +|50|[Encode and Decode Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20Strings.java)|Medium|Java|[String]|| +|51|[Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Prefix.java)|Easy|Java|[String]|| +|52|[Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String.java)|Medium|Java|[String]|| +|53|[Reverse Words in a String II.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20II.java)|Medium|Java|[String]|| +|54|[Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20III.java)|Easy|Java|[String]|| +|55|[Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome%20II.java)|Easy|Java|[String]|| +|56|[One Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/One%20Edit%20Distance.java)|Medium|Java|[String]|| +|57|[Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Vowels%20of%20a%20String.java)|Easy|Java|[String, Two Pointers]|| +|58|[Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20strStr().java)|Easy|Java|[String, Two Pointers]|| +|59|[Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome.java)|Easy|Java|[String, Two Pointers]|| + + + + + + +## Tree (55) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Hash Table, Tree]|| +|1|[Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Tree]|| +|2|[Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|Java|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|| +|3|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|Java|[BFS, DFS, Graph, Tree, Union Find]|| +|4|[Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)|Medium|Java|[BFS, DFS, Hash Table, Tree]|| +|5|[Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)|Easy|Java|[BFS, DFS, Stack, Tree]|| +|6|[Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|7|[Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|8|[Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|9|[Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)|Medium|Java|[BFS, DFS, Tree]|| +|10|[Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)|Medium|Java|[BFS, DFS, Tree]|| +|11|[Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)|Medium|Java|[BFS, Stack, Tree]|| +|12|[Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)|Easy|Java|[BFS, Tree]|| +|13|[Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)|Medium|Java|[BFS, Tree]|| +|14|[Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)|Easy|Java|[BST, Binary Search, Tree]|| +|15|[Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List, Tree]|| +|16|[Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Divide and Conquer, Tree]|| +|17|[Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)|Medium|Java|[BST, DFS, Stack, Tree]|| +|18|[Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Tree]|| +|19|[Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)|Hard|Java|[BST, DFS, Tree]|| +|20|[Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)|Medium|Java|[BST, DP, Divide and Conquer, Tree]|| +|21|[Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DP, Tree]|| +|22|[Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)|Medium|Java|[BST, Design, Stack, Tree]|| +|23|[Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)|Easy|Java|[BST, Tree]|| +|24|[Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)|Medium|Java|[BST, Tree]|| +|25|[Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)|Easy|Java|[Backtracking, DFS, Tree]|| +|26|[Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Complete%20Tree%20Nodes.java)|Medium|Java|[Binary Search, Tree]|| +|27|[House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)|Medium|Java|[DFS, DP, Status DP, Tree]|| +|28|[Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)|Hard|Java|[DFS, DP, Tree, Tree DP]|| +|29|[Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)|Medium|Java|[DFS, Divide and Conquer, Double Recursive, Tree]|| +|30|[Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)|Easy|Java|[DFS, Divide and Conquer, Tree]|| +|31|[Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|32|[Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|33|[Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|34|[Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)|Easy|Java|[DFS, Divide and Conquer, Tree]|| +|35|[Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)|Easy|Java|[DFS, Double Recursive, Tree]|| +|36|[Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)|Hard|Java|[DFS, Graph, Tree, Union Find]|| +|37|[Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)|Medium|Java|[DFS, Hash Table, Tree]|| +|38|[Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Same%20Tree.java)|Easy|Java|[DFS, Tree]|| +|39|[Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Balanced%20Binary%20Tree.java)|Medium|Java|[DFS, Tree]|| +|40|[Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Depth%20of%20Binary%20Tree.java)|Easy|Java|[DFS, Tree]|| +|41|[Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)|Easy|Java|[DFS, Tree]|| +|42|[Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)|Easy|Java|[DFS, Tree]|| +|43|[Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)|Easy|Java|[DFS, Tree]|| +|44|[Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)|Medium|Java|[DFS, Tree]|| +|45|[Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum.java)|Easy|Java|[DFS, Tree]|| +|46|[Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)|Medium|Java|[DFS, Tree]|| +|47|[Populating Next Right Pointers in Each Node II.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node%20II.java)|Medium|Java|[DFS, Tree]|| +|48|[Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)|Easy|Java|[Hash Table, Stack, Tree]|| +|49|[Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20II.java)|Easy|Java|[Hash Table, Tree]|| +|50|[Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)|Medium|Java|[Linked List, Stack, Tree]|| +|51|[Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)|Medium|Java|[Stack, Tree]|| +|52|[Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)|Medium|Java|[Stack, Tree, Two Stacks]|| +|53|[Two Sum IV - Input is a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20IV%20-%20Input%20is%20a%20BST.java)|Easy|Java|[Tree]|| +|54|[Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Diameter%20of%20Binary%20Tree.java)|Easy|Java|[Tree]|| + + + + + + +## Binary Search (40) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)|Hard|Java|[Array, BST, Binary Search, DP, Queue, TreeSet]|| +|1|[Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array%20II.java)|Medium|Java|[Array, Binary Search]|| +|2|[Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)|Medium|Java|[Array, Binary Search]|| +|3|[Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)|Medium|Java|[Array, Binary Search]|| +|4|[Find Minimum in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array%20II.java)|Hard|Java|[Array, Binary Search]|| +|5|[Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)|Medium|Java|[Array, Binary Search]|| +|6|[Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)|Medium|Java|[Array, Binary Search]|| +|7|[Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array.java)|Medium|Java|[Array, Binary Search]|| +|8|[Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)|Hard|Java|[Array, Binary Search, DFS, Divide and Conquer]|| +|9|[Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)|Review|Java|[Array, Binary Search, PreSum]|| +|10|[Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)|Medium|Java|[Array, Binary Search, Subarray, Two Pointers]|| +|11|[Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)|Hard|Java|[Array, Binary Search, Two Pointers]|| +|12|[Two Sum II - Input array is sorted.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20II%20-%20Input%20array%20is%20sorted.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|13|[2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum%20II.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|14|[Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|15|[Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|Java|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|| +|16|[Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)|Easy|Java|[BST, Binary Search, Tree]|| +|17|[Guess Number Higher or Lower.java](https://github.com/awangdev/LintCode/blob/master/Java/Guess%20Number%20Higher%20or%20Lower.java)|Easy|Java|[Binary Search]|| +|18|[First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Bad%20Version.java)|Easy|Java|[Binary Search]|| +|19|[Wood Cut.java](https://github.com/awangdev/LintCode/blob/master/Java/Wood%20Cut.java)|Medium|Java|[Binary Search]|| +|20|[Classical Binary Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Classical%20Binary%20Search.java)|Easy|Java|[Binary Search]|| +|21|[Closest Number in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Number%20in%20Sorted%20Array.java)|Easy|Java|[Binary Search]|| +|22|[Last Position of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Last%20Position%20of%20Target.java)|Easy|Java|[Binary Search]|| +|23|[H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index%20II.java)|Medium|Java|[Binary Search]|| +|24|[Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)|Hard|Java|[Binary Search, Coordinate DP, DP]|| +|25|[Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)|Medium|Java|[Binary Search, Coordinate DP, DP, Memoization]|| +|26|[Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)|Hard|Java|[Binary Search, DFS, Divide and Conquer]|| +|27|[Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)|Hard|Java|[Binary Search, DP, Partition DP]|| +|28|[Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)|Medium|Java|[Binary Search, Divide and Conquer]|| +|29|[Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)|Medium|Java|[Binary Search, Divide and Conquer, Lint, Segment Tree]|| +|30|[Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)|Easy|Java|[Binary Search, Hash Table, Sort, Two Pointers]|| +|31|[Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)|Easy|Java|[Binary Search, Hash Table, Sort, Two Pointers]|| +|32|[Kth Smallest Element in a Sorted Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20Sorted%20Matrix.java)|Medium|Java|[Binary Search, Heap]|| +|33|[Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)|Medium|Java|[Binary Search, Lint, Segment Tree]|| +|34|[Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)|Hard|Java|[Binary Search, Lint, Segment Tree]|| +|35|[Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)|Medium|Java|[Binary Search, Lint, Segment Tree]|| +|36|[Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Perfect%20Square.java)|Review|Java|[Binary Search, Math]|| +|37|[Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)|Medium|Java|[Binary Search, Math]|| +|38|[Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/Sqrt(x).java)|Easy|Java|[Binary Search, Math]|| +|39|[Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Complete%20Tree%20Nodes.java)|Medium|Java|[Binary Search, Tree]|| + + + + + + +## BFS (39) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)|Medium|Java|[Array, BFS, Backtracking, Bit Manipulation, DFS]|| +|1|[Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)|Medium|Java|[Array, BFS, Backtracking, DFS]|| +|2|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|Java|[Array, BFS, Backtracking, DFS, Hash Table, String]|| +|3|[Word Ladder.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder.java)|Medium|Java|[BFS]|| +|4|[Shortest Distance from All Buildings.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Distance%20from%20All%20Buildings.java)|Hard|Java|[BFS]|| +|5|[Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)|Hard|Java|[BFS]|| +|6|[Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)|Medium|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|7|[Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)|Hard|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|8|[Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Nested%20List%20Weight%20Sum.java)|Easy|Java|[BFS, DFS]|| +|9|[Find the Connected Component in the Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Connected%20Component%20in%20the%20Undirected%20Graph.java)|Medium|Java|[BFS, DFS]|| +|10|[Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)|Medium|Java|[BFS, DFS]|| +|11|[The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)|Medium|Java|[BFS, DFS]|| +|12|[Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)|Review|Java|[BFS, DFS, DP]|| +|13|[Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|Java|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|| +|14|[Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)|Medium|Java|[BFS, DFS, Graph]|| +|15|[Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)|Medium|Java|[BFS, DFS, Graph, Topological Sort]|| +|16|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|Java|[BFS, DFS, Graph, Tree, Union Find]|| +|17|[Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|18|[Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|19|[Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|20|[Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)|Medium|Java|[BFS, DFS, Hash Table, Tree]|| +|21|[Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)|Medium|Java|[BFS, DFS, Matrix DFS, Union Find]|| +|22|[Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)|Medium|Java|[BFS, DFS, Matrix DFS, Union Find]|| +|23|[The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)|Medium|Java|[BFS, DFS, PriorityQueue]|| +|24|[The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)|Hard|Java|[BFS, DFS, PriorityQueue]|| +|25|[Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)|Easy|Java|[BFS, DFS, Stack, Tree]|| +|26|[Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)|Medium|Java|[BFS, DFS, Topological Sort]|| +|27|[Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|28|[Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|29|[Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)|Easy|Java|[BFS, DFS, Tree]|| +|30|[Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)|Medium|Java|[BFS, DFS, Tree]|| +|31|[Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)|Medium|Java|[BFS, DFS, Tree]|| +|32|[Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)|Medium|Java|[BFS, DP, Math, Partition DP]|| +|33|[Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)|Medium|Java|[BFS, Graph]|| +|34|[Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)|Hard|Java|[BFS, Graph]|| +|35|[Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)|Hard|Java|[BFS, Heap, MinHeap, PriorityQueue]|| +|36|[Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)|Medium|Java|[BFS, Stack, Tree]|| +|37|[Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)|Easy|Java|[BFS, Tree]|| +|38|[Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)|Medium|Java|[BFS, Tree]|| + + + + + + +## Math (38) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)|Easy|Java|[Array, Bit Manipulation, Math]|| +|1|[Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)|Hard|Java|[Array, Geometry, Hash Table, Math]|| +|2|[Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)|Easy|Java|[Array, Math]|| +|3|[Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)|Medium|Java|[Array, Math]|| +|4|[Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)|Medium|Java|[BFS, DP, Math, Partition DP]|| +|5|[Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)|Medium|Java|[Backtracking, Math]|| +|6|[Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Perfect%20Square.java)|Review|Java|[Binary Search, Math]|| +|7|[Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)|Medium|Java|[Binary Search, Math]|| +|8|[Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/Sqrt(x).java)|Easy|Java|[Binary Search, Math]|| +|9|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|Java|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|| +|10|[Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)|Easy|Java|[Bit Manipulation, Math]|| +|11|[Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)|Medium|Java|[Coordinate DP, DP, Math, Subarray]|| +|12|[Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)|Medium|Java|[DFS, Enumeration, Math, Sequence DFS]|| +|13|[Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)|Hard|Java|[DFS, Greedy, Math]|| +|14|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|Java|[DP, Enumeration, Heap, Math, PriorityQueue]|| +|15|[Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)|Medium|Java|[DP, Math]|| +|16|[Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)|Easy|Java|[DP, Math, Memoization]|| +|17|[Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)|Easy|Java|[Enumeration, Hash Table, Math]|| +|18|[Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)|Hard|Java|[Enumeration, Math, String]|| +|19|[Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)|Hard|Java|[Enumeration, Math, String]|| +|20|[Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)|Medium|Java|[Hash Table, Math]|| +|21|[Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)|Medium|Java|[Hash Table, Math]|| +|22|[Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Primes.java)|Easy|Java|[Hash Table, Math]|| +|23|[Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)|Medium|Java|[Hash Table, Math]|| +|24|[Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers.java)|Medium|Java|[Linked List, Math]|| +|25|[Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)|Medium|Java|[Linked List, Math, Two Pointers]|| +|26|[Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)|Easy|Java|[Math]|| +|27|[Power of Three.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Three.java)|Easy|Java|[Math]|| +|28|[Add Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Digits.java)|Easy|Java|[Math]|| +|29|[Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Integer.java)|Easy|Java|[Math]|| +|30|[Excel Sheet Column Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Number.java)|Easy|Java|[Math]|| +|31|[Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Title.java)|Easy|Java|[Math]|| +|32|[Ugly Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number.java)|Medium|Java|[Math]|| +|33|[Number of Digit One.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Digit%20One.java)|Hard|Java|[Math]|| +|34|[Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Multiply%20Strings.java)|Medium|Java|[Math, String]|| +|35|[String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/String%20to%20Integer%20(atoi).java)|Medium|Java|[Math, String]|| +|36|[Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Roman%20to%20Integer.java)|Easy|Java|[Math, String]|| +|37|[Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)|Easy|Java|[Math, String, Two Pointers]|| + + + + + + +## Two Pointers (38) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)|Medium|Java|[Array, Binary Search, Subarray, Two Pointers]|| +|1|[Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)|Hard|Java|[Array, Binary Search, Two Pointers]|| +|2|[Two Sum II - Input array is sorted.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20II%20-%20Input%20array%20is%20sorted.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|3|[2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum%20II.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|4|[Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)|Medium|Java|[Array, Binary Search, Two Pointers]|| +|5|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|Java|[Array, Partition, Quick Sort, Sort, Two Pointers]|| +|6|[Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)|Medium|Java|[Array, Quick Sort, Sort, Two Pointers]|| +|7|[The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)|Medium|Java|[Array, Sort, Two Pointers]|| +|8|[Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)|Hard|Java|[Array, Stack, Two Pointers]|| +|9|[Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)|Easy|Java|[Array, Two Pointers]|| +|10|[3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/3%20Sum%20Closest.java)|Medium|Java|[Array, Two Pointers]|| +|11|[3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum.java)|Medium|Java|[Array, Two Pointers]|| +|12|[3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum%20Smaller.java)|Medium|Java|[Array, Two Pointers]|| +|13|[Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array.java)|Easy|Java|[Array, Two Pointers]|| +|14|[Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array%20II.java)|Medium|Java|[Array, Two Pointers]|| +|15|[Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)|Medium|Java|[Array, Two Pointers]|| +|16|[Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array.java)|Easy|Java|[Array, Two Pointers]|| +|17|[Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Move%20Zeroes.java)|Easy|Java|[Array, Two Pointers]|| +|18|[Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)|Easy|Java|[Binary Search, Hash Table, Sort, Two Pointers]|| +|19|[Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)|Easy|Java|[Binary Search, Hash Table, Sort, Two Pointers]|| +|20|[Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)|Hard|Java|[Hash Table, Sliding Window, String, Two Pointers]|| +|21|[Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)|Medium|Java|[Hash Table, String, Two Pointers]|| +|22|[Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)|Hard|Java|[Hash Table, String, Two Pointers]|| +|23|[Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)|Medium|Java|[Linked List, Math, Two Pointers]|| +|24|[Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle.java)|Easy|Java|[Linked List, Two Pointers]|| +|25|[Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Nth%20Node%20From%20End%20of%20List.java)|Medium|Java|[Linked List, Two Pointers]|| +|26|[Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Linked%20List.java)|Easy|Java|[Linked List, Two Pointers]|| +|27|[Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)|Medium|Java|[Linked List, Two Pointers]|| +|28|[Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)|Medium|Java|[Linked List, Two Pointers]|| +|29|[Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)|Easy|Java|[Math, String, Two Pointers]|| +|30|[Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)|Medium|Java|[Partition, Quick Sort, Sort, Two Pointers]|| +|31|[Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)|Medium|Java|[Partition, Sort, String, Two Pointers]|| +|32|[Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)|Easy|Java|[Stack, Two Pointers]|| +|33|[Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Vowels%20of%20a%20String.java)|Easy|Java|[String, Two Pointers]|| +|34|[Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20strStr().java)|Easy|Java|[String, Two Pointers]|| +|35|[Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome.java)|Easy|Java|[String, Two Pointers]|| +|36|[Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20in%20String.java)|Medium|Java|[Two Pointers]|| +|37|[Interleaving Positive and Negative Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20Positive%20and%20Negative%20Numbers.java)|Medium|Java|[Two Pointers]|| + + + + + + +## Divide and Conquer (35) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)|Hard|Java|[Array, Binary Search, DFS, Divide and Conquer]|| +|1|[Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)|Easy|Java|[Array, Bit Manipulation, Divide and Conquer]|| +|2|[Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)|Easy|Java|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|| +|3|[Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Hash Table, Tree]|| +|4|[Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)|Medium|Java|[Array, DFS, Divide and Conquer, Tree]|| +|5|[Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|Java|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|| +|6|[Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|Java|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|| +|7|[Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List]|| +|8|[Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List, Tree]|| +|9|[Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Divide and Conquer, Tree]|| +|10|[Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)|Medium|Java|[BST, DP, Divide and Conquer, Tree]|| +|11|[Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)|Hard|Java|[BST, Divide and Conquer, Merge Sort, PreSum]|| +|12|[Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)|Hard|Java|[Backtracking, DFS, Divide and Conquer, String]|| +|13|[Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)|Medium|Java|[Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree]|| +|14|[The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)|Review|Java|[Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line]|| +|15|[Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)|Hard|Java|[Binary Search, DFS, Divide and Conquer]|| +|16|[Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)|Medium|Java|[Binary Search, Divide and Conquer]|| +|17|[Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)|Medium|Java|[Binary Search, Divide and Conquer, Lint, Segment Tree]|| +|18|[Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|19|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|20|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|21|[Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)|Medium|Java|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|| +|22|[Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)|Medium|Java|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|| +|23|[Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)|Medium|Java|[DFS, Divide and Conquer]|| +|24|[Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)|Medium|Java|[DFS, Divide and Conquer, Double Recursive, Tree]|| +|25|[Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)|Medium|Java|[DFS, Divide and Conquer, Stack]|| +|26|[Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)|Easy|Java|[DFS, Divide and Conquer, Tree]|| +|27|[Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|28|[Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|29|[Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)|Medium|Java|[DFS, Divide and Conquer, Tree]|| +|30|[Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)|Easy|Java|[DFS, Divide and Conquer, Tree]|| +|31|[Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)|Hard|Java|[DP, Divide and Conquer, Interval DP, Memoization]|| +|32|[Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Lists.java)|Medium|Java|[Divide and Conquer, Heap, Linked List, PriorityQueue]|| +|33|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|Java|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|| +|34|[Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)|Medium|Java|[Divide and Conquer, Linked List, Merge Sort, Sort]|| + + + + + + +## Backtracking (32) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)|Medium|Java|[Array, BFS, Backtracking, Bit Manipulation, DFS]|| +|1|[Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)|Medium|Java|[Array, BFS, Backtracking, DFS]|| +|2|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|Java|[Array, BFS, Backtracking, DFS, Hash Table, String]|| +|3|[Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|4|[Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|5|[Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|6|[Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)|Medium|Java|[Array, Backtracking, DFS]|| +|7|[Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)|Medium|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|8|[Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)|Hard|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|9|[Gray Code.java](https://github.com/awangdev/LintCode/blob/master/Java/Gray%20Code.java)|Medium|Java|[Backtracking]|| +|10|[Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations%20II.java)|Medium|Java|[Backtracking]|| +|11|[N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens.java)|Hard|Java|[Backtracking]|| +|12|[N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens%20II.java)|Hard|Java|[Backtracking]|| +|13|[Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)|Easy|Java|[Backtracking, Binary Tree, DFS]|| +|14|[Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)|Medium|Java|[Backtracking, Combination, DFS]|| +|15|[Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning.java)|Medium|Java|[Backtracking, DFS]|| +|16|[Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)|Hard|Java|[Backtracking, DFS]|| +|17|[Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)|Medium|Java|[Backtracking, DFS, DP]|| +|18|[Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)|Hard|Java|[Backtracking, DFS, DP, Hash Table, Memoization]|| +|19|[Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)|Hard|Java|[Backtracking, DFS, Divide and Conquer, String]|| +|20|[Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)|Medium|Java|[Backtracking, DFS, Permutation]|| +|21|[Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)|Medium|Java|[Backtracking, DFS, Sequence DFS, String]|| +|22|[Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)|Medium|Java|[Backtracking, DFS, String]|| +|23|[Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)|Easy|Java|[Backtracking, DFS, Tree]|| +|24|[Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)|Hard|Java|[Backtracking, DFS, Trie]|| +|25|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|| +|26|[Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|| +|27|[Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)|Medium|Java|[Backtracking, Design, Trie]|| +|28|[Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)|Medium|Java|[Backtracking, Math]|| +|29|[Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)|Medium|Java|[Backtracking, Permutation]|| +|30|[Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)|Medium|Java|[Backtracking, String]|| +|31|[Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)|Hard|Java|[Backtracking, Trie]|| + + + + + + +## Linked List (31) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List]|| +|1|[Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List, Tree]|| +|2|[LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)|Hard|Java|[Design, Hash Table, Linked List]|| +|3|[Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Lists.java)|Medium|Java|[Divide and Conquer, Heap, Linked List, PriorityQueue]|| +|4|[Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)|Medium|Java|[Divide and Conquer, Linked List, Merge Sort, Sort]|| +|5|[Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)|Medium|Java|[Hash Table, Linked List]|| +|6|[Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20List%20with%20Random%20Pointer.java)|Medium|Java|[Hash Table, Linked List]|| +|7|[Remove Duplicates from Unsorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Unsorted%20List.java)|Medium|Java|[Linked List]|| +|8|[Two Lists Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Lists%20Sum.java)|Medium|Java|[Linked List]|| +|9|[Add Two Numbers II.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers%20II.java)|Medium|Java|[Linked List]|| +|10|[Swap Nodes in Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Nodes%20in%20Pairs.java)|Medium|Java|[Linked List]|| +|11|[Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Node%20in%20a%20Linked%20List.java)|Easy|Java|[Linked List]|| +|12|[Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Middle%20of%20Linked%20List.java)|Easy|Java|[Linked List]|| +|13|[Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Linked%20List%20Elements.java)|Easy|Java|[Linked List]|| +|14|[Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List.java)|Easy|Java|[Linked List]|| +|15|[Reverse Linked List II .java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List%20II%20.java)|Medium|Java|[Linked List]|| +|16|[Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Linked%20Lists.java)|Easy|Java|[Linked List]|| +|17|[Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Sorted%20Lists.java)|Easy|Java|[Linked List]|| +|18|[Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List.java)|Easy|Java|[Linked List]|| +|19|[Remove Duplicates from Sorted List II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List%20II.java)|Medium|Java|[Linked List]|| +|20|[Reorder List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reorder%20List.java)|Medium|Java|[Linked List]|| +|21|[Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/Nth%20to%20Last%20Node%20in%20List.java)|Easy|Java|[Linked List]|| +|22|[Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers.java)|Medium|Java|[Linked List, Math]|| +|23|[Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)|Medium|Java|[Linked List, Math, Two Pointers]|| +|24|[Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)|Medium|Java|[Linked List, Sort]|| +|25|[Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)|Medium|Java|[Linked List, Stack, Tree]|| +|26|[Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle.java)|Easy|Java|[Linked List, Two Pointers]|| +|27|[Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Nth%20Node%20From%20End%20of%20List.java)|Medium|Java|[Linked List, Two Pointers]|| +|28|[Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Linked%20List.java)|Easy|Java|[Linked List, Two Pointers]|| +|29|[Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)|Medium|Java|[Linked List, Two Pointers]|| +|30|[Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)|Medium|Java|[Linked List, Two Pointers]|| + + + + + + +## Stack (30) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)|Hard|Java|[Array, DP, Hash Table, Stack]|| +|1|[Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)|Hard|Java|[Array, Monotonous Stack, Stack]|| +|2|[Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)|Hard|Java|[Array, Stack, Two Pointers]|| +|3|[Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)|Easy|Java|[BFS, DFS, Stack, Tree]|| +|4|[Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)|Medium|Java|[BFS, Stack, Tree]|| +|5|[Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)|Medium|Java|[BST, DFS, Stack, Tree]|| +|6|[Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)|Medium|Java|[BST, Design, Stack, Tree]|| +|7|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|| +|8|[Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Stack]|| +|9|[Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Stack]|| +|10|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|Java|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|| +|11|[Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)|Hard|Java|[Binary Tree, Expression Tree, Minimum Binary Tree, Stack]|| +|12|[Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)|Hard|Java|[Coordinate DP, Stack, String]|| +|13|[Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)|Medium|Java|[DFS, Divide and Conquer, Stack]|| +|14|[Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Nested%20List%20Iterator.java)|Medium|Java|[Design, Stack]|| +|15|[Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)|Easy|Java|[Design, Stack]|| +|16|[Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)|Easy|Java|[Design, Stack]|| +|17|[Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)|Easy|Java|[Design, Stack]|| +|18|[Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)|Hard|Java|[Greedy, Hash Table, Stack]|| +|19|[Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Greater%20Element%20I.java)|Easy|Java|[Hash Table, Stack]|| +|20|[Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)|Easy|Java|[Hash Table, Stack, Tree]|| +|21|[Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)|Medium|Java|[Linked List, Stack, Tree]|| +|22|[Evaluate Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Reverse%20Polish%20Notation.java)|Medium|Java|[Stack]|| +|23|[Implement Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack.java)|Easy|Java|[Stack]|| +|24|[Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/Exclusive%20Time%20of%20Functions.java)|Medium|Java|[Stack]|| +|25|[Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Simplify%20Path.java)|Medium|Java|[Stack, String]|| +|26|[Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Parentheses.java)|Easy|Java|[Stack, String]|| +|27|[Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)|Medium|Java|[Stack, Tree]|| +|28|[Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)|Medium|Java|[Stack, Tree, Two Stacks]|| +|29|[Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)|Easy|Java|[Stack, Two Pointers]|| + + + + + + +## Sort (22) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|Java|[Array, Interval, PriorityQueue, Sort, Sweep Line]|| +|1|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|Java|[Array, Partition, Quick Sort, Sort, Two Pointers]|| +|2|[Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)|Hard|Java|[Array, PriorityQueue, Sort]|| +|3|[Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)|Medium|Java|[Array, PriorityQueue, Sort, Sweep Line]|| +|4|[Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)|Medium|Java|[Array, Quick Sort, Sort, Two Pointers]|| +|5|[Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)|Medium|Java|[Array, Sort]|| +|6|[The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)|Medium|Java|[Array, Sort, Two Pointers]|| +|7|[Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)|Easy|Java|[Binary Search, Hash Table, Sort, Two Pointers]|| +|8|[Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)|Easy|Java|[Binary Search, Hash Table, Sort, Two Pointers]|| +|9|[H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)|Medium|Java|[Bucket Sort, Hash Table, Sort]|| +|10|[Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)|Medium|Java|[Divide and Conquer, Linked List, Merge Sort, Sort]|| +|11|[Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)|Medium|Java|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|| +|12|[Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Anagram.java)|Easy|Java|[Hash Table, Sort]|| +|13|[Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)|Medium|Java|[Linked List, Sort]|| +|14|[MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/MergeSort.java)|Medium|Java|[Merge Sort, Sort]|| +|15|[Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)|Medium|Java|[Partition, Quick Sort, Sort, Two Pointers]|| +|16|[Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)|Medium|Java|[Partition, Sort, String, Two Pointers]|| +|17|[Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)|Medium|Java|[PreSum, PriorityQueue, Sort, Subarray]|| +|18|[Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/Exam%20Room.java)|Medium|Java|[PriorityQueue, Sort]|| +|19|[Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)|Easy|Java|[PriorityQueue, Sort, Sweep Line]|| +|20|[QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)|Medium|Java|[Quick Sort, Sort]|| +|21|[Largest Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number.java)|Medium|Java|[Sort]|| + + + + + + +## BST (22) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)|Hard|Java|[Array, BST, Binary Search, DP, Queue, TreeSet]|| +|1|[K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)|Hard|Java|[Array, BST, TreeSet]|| +|2|[Insert Node in a Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Node%20in%20a%20Binary%20Search%20Tree%20.java)|Easy|Java|[BST]|| +|3|[Minimum Absolute Difference in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Absolute%20Difference%20in%20BST.java)|Easy|Java|[BST]|| +|4|[Remove Node in Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Node%20in%20Binary%20Search%20Tree.java)|Hard|Java|[BST]|| +|5|[Zigzag Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Zigzag%20Iterator.java)|Medium|Java|[BST]|| +|6|[Contains Duplicate III.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20III.java)|Medium|Java|[BST]|| +|7|[Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|Java|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|| +|8|[Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)|Easy|Java|[BST, Binary Search, Tree]|| +|9|[Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)|Medium|Java|[BST, Binary Tree]|| +|10|[Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List]|| +|11|[Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|Java|[BST, DFS, Divide and Conquer, Linked List, Tree]|| +|12|[Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Divide and Conquer, Tree]|| +|13|[Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)|Medium|Java|[BST, DFS, Stack, Tree]|| +|14|[Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DFS, Tree]|| +|15|[Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)|Hard|Java|[BST, DFS, Tree]|| +|16|[Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)|Medium|Java|[BST, DP, Divide and Conquer, Tree]|| +|17|[Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)|Medium|Java|[BST, DP, Tree]|| +|18|[Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)|Medium|Java|[BST, Design, Stack, Tree]|| +|19|[Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)|Hard|Java|[BST, Divide and Conquer, Merge Sort, PreSum]|| +|20|[Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)|Easy|Java|[BST, Tree]|| +|21|[Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)|Medium|Java|[BST, Tree]|| + + + + + + +## Design (21) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)|Medium|Java|[Array, Design, Hash Table]|| +|1|[Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|Java|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|| +|2|[Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)|Medium|Java|[BST, Design, Stack, Tree]|| +|3|[Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)|Medium|Java|[Backtracking, Design, Trie]|| +|4|[Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)|Easy|Java|[Design]|| +|5|[Peeking Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Peeking%20Iterator.java)|Medium|Java|[Design]|| +|6|[Flatten 2D Vector.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%202D%20Vector.java)|Medium|Java|[Design]|| +|7|[Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)|Hard|Java|[Design, Geometry, Hash Table]|| +|8|[LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)|Hard|Java|[Design, Hash Table]|| +|9|[Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)|Medium|Java|[Design, Hash Table]|| +|10|[ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)|Medium|Java|[Design, Hash Table]|| +|11|[LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)|Hard|Java|[Design, Hash Table, Linked List]|| +|12|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|Java|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|| +|13|[Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap]|| +|14|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|| +|15|[Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)|Easy|Java|[Design, Queue, Sliding Window]|| +|16|[Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Nested%20List%20Iterator.java)|Medium|Java|[Design, Stack]|| +|17|[Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)|Easy|Java|[Design, Stack]|| +|18|[Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)|Easy|Java|[Design, Stack]|| +|19|[Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)|Easy|Java|[Design, Stack]|| +|20|[Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Trie%20(Prefix%20Tree).java)|Medium|Java|[Design, Trie]|| + + + + + + +## Sequence DP (21) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)|Easy|Java|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|| +|1|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|Java|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|| +|2|[Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|3|[Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)|Medium|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|4|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Subarray]|| +|5|[Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)|Hard|Java|[Array, DP, Sequence DP]|| +|6|[Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)|Easy|Java|[Array, DP, Sequence DP]|| +|7|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|| +|8|[Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|| +|9|[Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)|Medium|Java|[DP, Double Sequence DP, Sequence DP]|| +|10|[Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)|Hard|Java|[DP, Double Sequence DP, Sequence DP, String]|| +|11|[Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)|Medium|Java|[DP, Double Sequence DP, Sequence DP, String]|| +|12|[K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)|Hard|Java|[DP, Double Sequence DP, Sequence DP, Trie]|| +|13|[Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)|Medium|Java|[DP, Hash Table, Sequence DP]|| +|14|[Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)|Easy|Java|[DP, Memoization, Sequence DP]|| +|15|[House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber.java)|Easy|Java|[DP, Sequence DP]|| +|16|[Best Time to Buy and Sell Stock IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20IV.java)|Hard|Java|[DP, Sequence DP]|| +|17|[Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)|Easy|Java|[DP, Sequence DP]|| +|18|[Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)|Easy|Java|[DP, Sequence DP, Status DP]|| +|19|[House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)|Medium|Java|[DP, Sequence DP, Status DP]|| +|20|[Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)|Hard|Java|[DP, Sequence DP, Status DP]|| + + + + + + +## PriorityQueue (19) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)|Medium|Java|[Array, Enumeration, Greedy, PriorityQueue, Queue]|| +|1|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|Java|[Array, Interval, PriorityQueue, Sort, Sweep Line]|| +|2|[Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)|Hard|Java|[Array, PriorityQueue, Sort]|| +|3|[Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)|Medium|Java|[Array, PriorityQueue, Sort, Sweep Line]|| +|4|[The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)|Medium|Java|[BFS, DFS, PriorityQueue]|| +|5|[The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)|Hard|Java|[BFS, DFS, PriorityQueue]|| +|6|[Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)|Hard|Java|[BFS, Heap, MinHeap, PriorityQueue]|| +|7|[The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)|Review|Java|[Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line]|| +|8|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|Java|[DP, Enumeration, Heap, Math, PriorityQueue]|| +|9|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|Java|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|| +|10|[Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Lists.java)|Medium|Java|[Divide and Conquer, Heap, Linked List, PriorityQueue]|| +|11|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|Java|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|| +|12|[Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)|Medium|Java|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|| +|13|[Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|| +|14|[Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|| +|15|[Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)|Medium|Java|[Heap, MinHeap, PriorityQueue]|| +|16|[Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)|Medium|Java|[PreSum, PriorityQueue, Sort, Subarray]|| +|17|[Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/Exam%20Room.java)|Medium|Java|[PriorityQueue, Sort]|| +|18|[Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)|Easy|Java|[PriorityQueue, Sort, Sweep Line]|| + + + + + + +## Greedy (18) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)|Hard|Java|[Array, Coordinate DP, DP, Greedy]|| +|1|[Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)|Medium|Java|[Array, DP, Greedy]|| +|2|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|Java|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|| +|3|[Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|4|[Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)|Medium|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|5|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Subarray]|| +|6|[Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)|Medium|Java|[Array, Enumeration, Greedy, PriorityQueue, Queue]|| +|7|[Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Celebrity.java)|Medium|Java|[Array, Greedy]|| +|8|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|| +|9|[Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)|Hard|Java|[DFS, Greedy, Math]|| +|10|[Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)|Medium|Java|[DP, Game Theory, Greedy]|| +|11|[Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)|Medium|Java|[Enumeration, Greedy]|| +|12|[Queue Reconstruction by Height.java](https://github.com/awangdev/LintCode/blob/master/Java/Queue%20Reconstruction%20by%20Height.java)|Medium|Java|[Greedy]|| +|13|[Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/Gas%20Station.java)|Medium|Java|[Greedy]|| +|14|[Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)|Hard|Java|[Greedy, Hash Table, Heap]|| +|15|[Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)|Hard|Java|[Greedy, Hash Table, Stack]|| +|16|[Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)|Medium|Java|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|| +|17|[Delete Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Digits.java)|Medium|Java|[Greedy, Priority Queue]|| + + + + + + +## Bit Manipulation (17) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)|Medium|Java|[Array, BFS, Backtracking, Bit Manipulation, DFS]|| +|1|[Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)|Easy|Java|[Array, Bit Manipulation, Divide and Conquer]|| +|2|[Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)|Easy|Java|[Array, Bit Manipulation, Math]|| +|3|[O(1) Check Power of 2.java](https://github.com/awangdev/LintCode/blob/master/Java/O(1)%20Check%20Power%20of%202.java)|Easy|Java|[Bit Manipulation]|| +|4|[Single Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20II.java)|Medium|Java|[Bit Manipulation]|| +|5|[Single Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20III.java)|Medium|Java|[Bit Manipulation]|| +|6|[Sum of Two Integers.java](https://github.com/awangdev/LintCode/blob/master/Java/Sum%20of%20Two%20Integers.java)|Easy|Java|[Bit Manipulation]|| +|7|[Swap Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Bits.java)|Easy|Java|[Bit Manipulation]|| +|8|[Update Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Update%20Bits.java)|Medium|Java|[Bit Manipulation]|| +|9|[Convert Integer A to Integer B.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Integer%20A%20to%20Integer%20B.java)|Easy|Java|[Bit Manipulation]|| +|10|[Count 1 in Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%201%20in%20Binary.java)|Easy|Java|[Bit Manipulation]|| +|11|[Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)|Medium|Java|[Bit Manipulation]|| +|12|[Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)|Easy|Java|[Bit Manipulation]|| +|13|[Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)|Medium|Java|[Bit Manipulation, Bitwise DP, DP]|| +|14|[Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)|Easy|Java|[Bit Manipulation, Math]|| +|15|[Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)|Hard|Java|[Bit Manipulation, String]|| +|16|[Maximum XOR of Two Numbers in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20XOR%20of%20Two%20Numbers%20in%20an%20Array.java)|Medium|Java|[Bit Manipulation, Trie]|| + + + + + + +## Coordinate DP (17) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|Java|[Array, Coordinate DP, DFS, DP, Memoization]|| +|1|[Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|2|[Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|3|[Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)|Easy|Java|[Array, Coordinate DP, DP]|| +|4|[Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)|Medium|Java|[Array, Coordinate DP, DP]|| +|5|[Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)|Easy|Java|[Array, Coordinate DP, DP]|| +|6|[Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)|Hard|Java|[Array, Coordinate DP, DP, Greedy]|| +|7|[Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)|Medium|Java|[Array, Coordinate DP, DP, Memoization]|| +|8|[Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)|Hard|Java|[Binary Search, Coordinate DP, DP]|| +|9|[Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)|Medium|Java|[Binary Search, Coordinate DP, DP, Memoization]|| +|10|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|Java|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|| +|11|[Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/Bomb%20Enemy.java)|Medium|Java|[Coordinate DP, DP]|| +|12|[Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Square.java)|Medium|Java|[Coordinate DP, DP]|| +|13|[Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)|Medium|Java|[Coordinate DP, DP]|| +|14|[Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)|Medium|Java|[Coordinate DP, DP, Math, Subarray]|| +|15|[Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)|Medium|Java|[Coordinate DP, DP, Status DP]|| +|16|[Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)|Hard|Java|[Coordinate DP, Stack, String]|| + + + + + + +## Heap (16) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)|Hard|Java|[BFS, Heap, MinHeap, PriorityQueue]|| +|1|[The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)|Review|Java|[Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line]|| +|2|[Kth Smallest Element in a Sorted Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20Sorted%20Matrix.java)|Medium|Java|[Binary Search, Heap]|| +|3|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|Java|[DP, Enumeration, Heap, Math, PriorityQueue]|| +|4|[Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)|Hard|Java|[Deque, Heap, Sliding Window]|| +|5|[Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap]|| +|6|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|| +|7|[Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Lists.java)|Medium|Java|[Divide and Conquer, Heap, Linked List, PriorityQueue]|| +|8|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|Java|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|| +|9|[Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)|Hard|Java|[Greedy, Hash Table, Heap]|| +|10|[Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)|Medium|Java|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|| +|11|[Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|| +|12|[Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|| +|13|[HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/HashHeap.java)|Hard|Java|[HashHeap, Heap]|| +|14|[Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/Heapify.java)|Medium|Java|[Heap, MinHeap]|| +|15|[Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)|Medium|Java|[Heap, MinHeap, PriorityQueue]|| + + + + + + +## Union Find (15) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)|Hard|Java|[Array, Hash Table, Union Find]|| +|1|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|Java|[BFS, DFS, Graph, Tree, Union Find]|| +|2|[Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|3|[Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|4|[Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|5|[Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)|Medium|Java|[BFS, DFS, Matrix DFS, Union Find]|| +|6|[Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)|Medium|Java|[BFS, DFS, Matrix DFS, Union Find]|| +|7|[Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)|Hard|Java|[DFS, Graph, Tree, Union Find]|| +|8|[Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)|Medium|Java|[DFS, Hash Table, Hash Table, Union Find]|| +|9|[Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)|Medium|Java|[Union Find]|| +|10|[Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)|Medium|Java|[Union Find]|| +|11|[Connecting Graph III.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20III.java)|Medium|Java|[Union Find]|| +|12|[Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands%20II.java)|Hard|Java|[Union Find]|| +|13|[Find the Weak Connected Component in the Directed Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Weak%20Connected%20Component%20in%20the%20Directed%20Graph.java)|Medium|Java|[Union Find]|| +|14|[Bricks Falling When Hit.java](https://github.com/awangdev/LintCode/blob/master/Java/Bricks%20Falling%20When%20Hit.java)|Hard|Java|[Union Find]|| + + + + + + +## Enumeration (15) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)|Medium|Java|[Array, Enumeration]|| +|1|[Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)|Medium|Java|[Array, Enumeration]|| +|2|[Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)|Medium|Java|[Array, Enumeration, Greedy, PriorityQueue, Queue]|| +|3|[Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)|Medium|Java|[Basic Implementation, Enumeration, String]|| +|4|[Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)|Medium|Java|[DFS, Enumeration, Math, Sequence DFS]|| +|5|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|Java|[DP, Enumeration, Heap, Math, PriorityQueue]|| +|6|[Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)|Hard|Java|[DP, Enumeration, Partition DP]|| +|7|[Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)|Medium|Java|[Enumeration, Greedy]|| +|8|[Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Sudoku.java)|Easy|Java|[Enumeration, Hash Table]|| +|9|[Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)|Easy|Java|[Enumeration, Hash Table, Math]|| +|10|[Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)|Hard|Java|[Enumeration, Math, String]|| +|11|[Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)|Hard|Java|[Enumeration, Math, String]|| +|12|[Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4.java)|Easy|Java|[Enumeration, String]|| +|13|[Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/Text%20Justification.java)|Hard|Java|[Enumeration, String]|| +|14|[Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)|Hard|Java|[Enumeration, String]|| + + + + + + +## Binary Tree (13) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)|Medium|Java|[BST, Binary Tree]|| +|1|[Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)|Easy|Java|[Backtracking, Binary Tree, DFS]|| +|2|[Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Binary%20Tree%20to%20Linked%20List.java)|Medium|Java|[Binary Tree, DFS]|| +|3|[Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|4|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|5|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|6|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|| +|7|[Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Stack]|| +|8|[Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Stack]|| +|9|[Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)|Medium|Java|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|| +|10|[Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)|Medium|Java|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|| +|11|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|Java|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|| +|12|[Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)|Hard|Java|[Binary Tree, Expression Tree, Minimum Binary Tree, Stack]|| + + + + + + +## Segment Tree (12) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|Java|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|| +|1|[Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)|Medium|Java|[Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree]|| +|2|[The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)|Review|Java|[Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line]|| +|3|[Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)|Medium|Java|[Binary Search, Divide and Conquer, Lint, Segment Tree]|| +|4|[Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)|Medium|Java|[Binary Search, Lint, Segment Tree]|| +|5|[Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)|Hard|Java|[Binary Search, Lint, Segment Tree]|| +|6|[Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)|Medium|Java|[Binary Search, Lint, Segment Tree]|| +|7|[Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|8|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|9|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|10|[Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)|Medium|Java|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|| +|11|[Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)|Medium|Java|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|| + + + + + + +## Memoization (12) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|Java|[Array, Coordinate DP, DFS, DP, Memoization]|| +|1|[Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)|Medium|Java|[Array, Coordinate DP, DP, Memoization]|| +|2|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|Java|[Array, DP, Game Theory, Interval DP, Memoization]|| +|3|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|Java|[Array, DP, Game Theory, Memoization, MiniMax]|| +|4|[Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)|Medium|Java|[Backpack DP, DP, Memoization]|| +|5|[Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)|Hard|Java|[Backtracking, DFS, DP, Hash Table, Memoization]|| +|6|[Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)|Medium|Java|[Binary Search, Coordinate DP, DP, Memoization]|| +|7|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|Java|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|| +|8|[Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)|Medium|Java|[DFS, DP, Interval DP, Memoization]|| +|9|[Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)|Hard|Java|[DP, Divide and Conquer, Interval DP, Memoization]|| +|10|[Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)|Easy|Java|[DP, Math, Memoization]|| +|11|[Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)|Easy|Java|[DP, Memoization, Sequence DP]|| + + + + + + +## Trie (11) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)|Hard|Java|[Backtracking, DFS, Trie]|| +|1|[Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)|Medium|Java|[Backtracking, Design, Trie]|| +|2|[Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)|Hard|Java|[Backtracking, Trie]|| +|3|[Maximum XOR of Two Numbers in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20XOR%20of%20Two%20Numbers%20in%20an%20Array.java)|Medium|Java|[Bit Manipulation, Trie]|| +|4|[K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)|Hard|Java|[DP, Double Sequence DP, Sequence DP, Trie]|| +|5|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|Java|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|| +|6|[Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Trie%20(Prefix%20Tree).java)|Medium|Java|[Design, Trie]|| +|7|[Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|| +|8|[Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)|Hard|Java|[Hash Table, String, Trie]|| +|9|[Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Word%20in%20Dictionary.java)|Easy|Java|[Hash Table, Trie]|| +|10|[Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Prefix%20and%20Suffix%20Search.java)|Hard|Java|[Trie]|| + + + + + + +## Graph (11) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)|Medium|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|1|[Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)|Hard|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|2|[Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)|Medium|Java|[BFS, DFS, Graph]|| +|3|[Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)|Medium|Java|[BFS, DFS, Graph, Topological Sort]|| +|4|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|Java|[BFS, DFS, Graph, Tree, Union Find]|| +|5|[Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|6|[Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|7|[Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)|Medium|Java|[BFS, DFS, Graph, Union Find]|| +|8|[Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)|Medium|Java|[BFS, Graph]|| +|9|[Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)|Hard|Java|[BFS, Graph]|| +|10|[Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)|Hard|Java|[DFS, Graph, Tree, Union Find]|| + + + + + + +## Subarray (11) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)|Medium|Java|[Array, Binary Search, Subarray, Two Pointers]|| +|1|[Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)|Easy|Java|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|| +|2|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|Java|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|| +|3|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Subarray]|| +|4|[Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)|Medium|Java|[Array, DP, Subarray]|| +|5|[Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)|Easy|Java|[Array, Hash Table, PreSum, Subarray]|| +|6|[Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)|Medium|Java|[Array, Hash Table, PreSum, Subarray]|| +|7|[Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)|Easy|Java|[Array, Subarray]|| +|8|[Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)|Medium|Java|[Coordinate DP, DP, Math, Subarray]|| +|9|[Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)|Medium|Java|[Hash Table, PreSum, Subarray]|| +|10|[Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)|Medium|Java|[PreSum, PriorityQueue, Sort, Subarray]|| + + + + + + +## PreSum (10) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)|Review|Java|[Array, Binary Search, PreSum]|| +|1|[Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)|Easy|Java|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|| +|2|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|Java|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|| +|3|[Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)|Medium|Java|[Array, Hash Table, PreSum]|| +|4|[Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)|Easy|Java|[Array, Hash Table, PreSum, Subarray]|| +|5|[Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)|Medium|Java|[Array, Hash Table, PreSum, Subarray]|| +|6|[Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)|Hard|Java|[BST, Divide and Conquer, Merge Sort, PreSum]|| +|7|[Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/Range%20Sum%20Query%20-%20Immutable.java)|Easy|Java|[DP, PreSum]|| +|8|[Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)|Medium|Java|[Hash Table, PreSum, Subarray]|| +|9|[Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)|Medium|Java|[PreSum, PriorityQueue, Sort, Subarray]|| + + + + + + +## Lint (9) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)|Medium|Java|[Binary Search, Divide and Conquer, Lint, Segment Tree]|| +|1|[Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)|Medium|Java|[Binary Search, Lint, Segment Tree]|| +|2|[Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)|Hard|Java|[Binary Search, Lint, Segment Tree]|| +|3|[Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)|Medium|Java|[Binary Search, Lint, Segment Tree]|| +|4|[Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|5|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|6|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|Java|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|| +|7|[Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)|Medium|Java|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|| +|8|[Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)|Medium|Java|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|| + + + + + + +## MinHeap (9) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)|Hard|Java|[BFS, Heap, MinHeap, PriorityQueue]|| +|1|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|Java|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|| +|2|[Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap]|| +|3|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|| +|4|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|Java|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|| +|5|[Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|| +|6|[Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|| +|7|[Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/Heapify.java)|Medium|Java|[Heap, MinHeap]|| +|8|[Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)|Medium|Java|[Heap, MinHeap, PriorityQueue]|| + + + + + + +## Backpack DP (8) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)|Medium|Java|[Array, Backpack DP, DP]|| +|1|[Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)|Medium|Java|[Backpack DP, DP]|| +|2|[Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change%202.java)|Medium|Java|[Backpack DP, DP]|| +|3|[Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)|Medium|Java|[Backpack DP, DP]|| +|4|[Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)|Medium|Java|[Backpack DP, DP]|| +|5|[Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)|Medium|Java|[Backpack DP, DP]|| +|6|[Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)|Hard|Java|[Backpack DP, DP]|| +|7|[Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)|Medium|Java|[Backpack DP, DP, Memoization]|| + + + + + + +## Status DP (7) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|1|[Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)|Medium|Java|[Array, DP, Greedy, Sequence DP, Status DP]|| +|2|[Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)|Medium|Java|[Coordinate DP, DP, Status DP]|| +|3|[House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)|Medium|Java|[DFS, DP, Status DP, Tree]|| +|4|[Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)|Easy|Java|[DP, Sequence DP, Status DP]|| +|5|[House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)|Medium|Java|[DP, Sequence DP, Status DP]|| +|6|[Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)|Hard|Java|[DP, Sequence DP, Status DP]|| + + + + + + +## Double Sequence DP (6) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|| +|1|[Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)|Hard|Java|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|| +|2|[Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)|Medium|Java|[DP, Double Sequence DP, Sequence DP]|| +|3|[Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)|Hard|Java|[DP, Double Sequence DP, Sequence DP, String]|| +|4|[Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)|Medium|Java|[DP, Double Sequence DP, Sequence DP, String]|| +|5|[K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)|Hard|Java|[DP, Double Sequence DP, Sequence DP, Trie]|| + + + + + + +## Sliding Window (6) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)|Hard|Java|[Deque, Heap, Sliding Window]|| +|1|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|| +|2|[Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)|Easy|Java|[Design, Queue, Sliding Window]|| +|3|[Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Anagrams%20in%20a%20String.java)|Easy|Java|[Hash Table, Sliding Window]|| +|4|[Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)|Hard|Java|[Hash Table, Sliding Window, String]|| +|5|[Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)|Hard|Java|[Hash Table, Sliding Window, String, Two Pointers]|| + + + + + + +## Quick Sort (6) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|Java|[Array, Partition, Quick Sort, Sort, Two Pointers]|| +|1|[Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)|Easy|Java|[Array, Quick Select, Quick Sort]|| +|2|[Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)|Medium|Java|[Array, Quick Sort, Sort, Two Pointers]|| +|3|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|Java|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|| +|4|[Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)|Medium|Java|[Partition, Quick Sort, Sort, Two Pointers]|| +|5|[QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)|Medium|Java|[Quick Sort, Sort]|| + + + + + + +## Partition DP (5) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)|Medium|Java|[BFS, DP, Math, Partition DP]|| +|1|[Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)|Hard|Java|[Binary Search, DP, Partition DP]|| +|2|[Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)|Hard|Java|[DP, Enumeration, Partition DP]|| +|3|[Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)|Hard|Java|[DP, Partition DP]|| +|4|[Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)|Medium|Java|[DP, Partition DP, String]|| + + + + + + +## Sweep Line (5) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|Java|[Array, Interval, PriorityQueue, Sort, Sweep Line]|| +|1|[Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)|Medium|Java|[Array, PriorityQueue, Sort, Sweep Line]|| +|2|[The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)|Review|Java|[Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line]|| +|3|[Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)|Medium|Java|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|| +|4|[Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)|Easy|Java|[PriorityQueue, Sort, Sweep Line]|| + + + + + + +## Topological Sort (5) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)|Medium|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|1|[Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)|Hard|Java|[BFS, Backtracking, DFS, Graph, Topological Sort]|| +|2|[Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)|Medium|Java|[BFS, DFS, Graph, Topological Sort]|| +|3|[Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)|Medium|Java|[BFS, DFS, Topological Sort]|| +|4|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|Java|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|| + + + + + + +## Expression Tree (5) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|| +|1|[Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Stack]|| +|2|[Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Stack]|| +|3|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|Java|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|| +|4|[Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)|Hard|Java|[Binary Tree, Expression Tree, Minimum Binary Tree, Stack]|| + + + + + + +## Merge Sort (4) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)|Hard|Java|[BST, Divide and Conquer, Merge Sort, PreSum]|| +|1|[Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)|Medium|Java|[Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree]|| +|2|[Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)|Medium|Java|[Divide and Conquer, Linked List, Merge Sort, Sort]|| +|3|[MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/MergeSort.java)|Medium|Java|[Merge Sort, Sort]|| + + + + + + +## Game Theory (4) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|Java|[Array, DP, Game Theory, Interval DP, Memoization]|| +|1|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|Java|[Array, DP, Game Theory, Memoization, MiniMax]|| +|2|[Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)|Easy|Java|[Brainteaser, DP, Game Theory]|| +|3|[Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)|Medium|Java|[DP, Game Theory, Greedy]|| + + + + + + +## Combination (4) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|1|[Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|2|[Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)|Medium|Java|[Array, Backtracking, Combination, DFS]|| +|3|[Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)|Medium|Java|[Backtracking, Combination, DFS]|| + + + + + + +## Interval DP (4) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|Java|[Array, DP, Game Theory, Interval DP, Memoization]|| +|1|[Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)|Medium|Java|[DFS, DP, Interval DP, Memoization]|| +|2|[Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)|Hard|Java|[DP, Divide and Conquer, Interval DP, Memoization]|| +|3|[Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)|Hard|Java|[DP, Interval DP, String]|| + + + + + + +## MaxHeap (4) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap]|| +|1|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|Java|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|| +|2|[Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|| +|3|[Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)|Medium|Java|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|| + + + + + + +## Permutation (3) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)|Medium|Java|[Backtracking, DFS, Permutation]|| +|1|[Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)|Medium|Java|[Backtracking, Permutation]|| +|2|[Shuffle an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Shuffle%20an%20Array.java)|Medium|Java|[Permutation]|| + + + + + + +## Binary Indexed Tree (3) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|Java|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|| +|1|[Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)|Medium|Java|[Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree]|| +|2|[The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)|Review|Java|[Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line]|| + + + + + + +## Minimum Binary Tree (3) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|Java|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|| +|1|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|Java|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|| +|2|[Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)|Hard|Java|[Binary Tree, Expression Tree, Minimum Binary Tree, Stack]|| + + + + + + +## Basic Implementation (3) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)|Easy|Java|[Basic Implementation]|| +|1|[Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)|Medium|Java|[Basic Implementation, Enumeration, String]|| +|2|[Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)|Easy|Java|[Basic Implementation, String]|| + + + + + + +## Queue (3) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)|Hard|Java|[Array, BST, Binary Search, DP, Queue, TreeSet]|| +|1|[Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)|Medium|Java|[Array, Enumeration, Greedy, PriorityQueue, Queue]|| +|2|[Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)|Easy|Java|[Design, Queue, Sliding Window]|| + + + + + + +## Partition (3) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|Java|[Array, Partition, Quick Sort, Sort, Two Pointers]|| +|1|[Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)|Medium|Java|[Partition, Quick Sort, Sort, Two Pointers]|| +|2|[Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)|Medium|Java|[Partition, Sort, String, Two Pointers]|| + + + + + + +## Sequence DFS (2) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)|Medium|Java|[Backtracking, DFS, Sequence DFS, String]|| +|1|[Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)|Medium|Java|[DFS, Enumeration, Math, Sequence DFS]|| + + + + + + +## Double Recursive (2) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)|Medium|Java|[DFS, Divide and Conquer, Double Recursive, Tree]|| +|1|[Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)|Easy|Java|[DFS, Double Recursive, Tree]|| + + + + + + +## TreeSet (2) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)|Hard|Java|[Array, BST, Binary Search, DP, Queue, TreeSet]|| +|1|[K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)|Hard|Java|[Array, BST, TreeSet]|| + + + + + + +## Matrix DFS (2) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)|Medium|Java|[BFS, DFS, Matrix DFS, Union Find]|| +|1|[Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)|Medium|Java|[BFS, DFS, Matrix DFS, Union Find]|| + + + + + + +## MiniMax (2) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|Java|[Array, DP, Game Theory, Memoization, MiniMax]|| +|1|[Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)|Medium|Java|[DP, MiniMax]|| + + + + + + +## Deque (2) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|Java|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|| +|1|[Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)|Hard|Java|[Deque, Heap, Sliding Window]|| + + + + + + +## Geometry (2) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)|Hard|Java|[Array, Geometry, Hash Table, Math]|| +|1|[Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)|Hard|Java|[Design, Geometry, Hash Table]|| + + + + + + +## Priority Queue (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Delete Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Digits.java)|Medium|Java|[Greedy, Priority Queue]|| + + + + + + +## HashHeap (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/HashHeap.java)|Hard|Java|[HashHeap, Heap]|| + + + + + + +## Binary Search Tree (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)|Medium|Java|[Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree]|| + + + + + + +## KMP (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)|Hard|Java|[KMP, String]|| + + + + + + +## Brainteaser (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)|Easy|Java|[Brainteaser, DP, Game Theory]|| + + + + + + +## Interval (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|Java|[Array, Interval, PriorityQueue, Sort, Sweep Line]|| + + + + + + +## Bitwise DP (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)|Medium|Java|[Bit Manipulation, Bitwise DP, DP]|| + + + + + + +## Quick Select (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)|Easy|Java|[Array, Quick Select, Quick Sort]|| + + + + + + +## Bucket Sort (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)|Medium|Java|[Bucket Sort, Hash Table, Sort]|| + + + + + + +## Monotonous Stack (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)|Hard|Java|[Array, Monotonous Stack, Stack]|| + + + + + + +## PreProduct (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Except%20Self.java)|Medium|Java|[Array, PreProduct]|| + + + + + + +## Reservior Sampling (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Random%20Pick%20Index.java)|Medium|Java|[Reservior Sampling]|| + + + + + + +## TreeMap (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)|Medium|Java|[Array, TreeMap]|| + + + + + + +## Two Stacks (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)|Medium|Java|[Stack, Tree, Two Stacks]|| + + + + + + +## Tree DP (1) +| Squence | Problem | Level | Language | Tags | Video Tutorial| +|:-------:|:--------------|:------:|:---------:|:----:|:-------------:| +|0|[Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)|Hard|Java|[DFS, DP, Tree, Tree DP]|| + + + diff --git a/TagReviewPage.md b/TagReviewPage.md new file mode 100644 index 0000000..a626869 --- /dev/null +++ b/TagReviewPage.md @@ -0,0 +1,26424 @@ + + + +## Priority Queue (1) +**0. [Delete Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Digits.java)** Level: Medium Tags: [Greedy, Priority Queue] + + +#### Priority Queue +- TODO: parse into node(index, digitValue) +- find the top k, and remove from char array +- O(nlogn) time + +#### Greedy +- 数位靠前的,权值更大. 所以硬来把靠前的相对更大的(跟following digit相比)去掉。 + + + +--- + + + + + + + +## Merge Sort (4) +**0. [MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/MergeSort.java)** Level: Medium Tags: [Merge Sort, Sort] + + +#### Merge Sort +- Divide and conquer, recursively +- 先从中间分段, merge sort 左边 (dfs), merge sort 右边 +- 最后merge起来 +- merge的时候因为是做int[], 所以没办法必须要O(n) space +- Time O(nlogn), Space O(n) + + + +--- + +**1. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**2. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**3. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + + + + + + + +## Sequence DFS (2) +**0. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**1. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + + + + + + + +## Lint (9) +**0. [Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**1. [Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 + + + +--- + +**2. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**3. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**4. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**5. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**6. [Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的sum. + +#### Segment Tree + Binary Search +- 其实是segment tree 每个node上面加个sum +- 记得Segment Tree methods: Build, Query +- Note: 存在SegmentTreeNode里面的是sum. 其他题目可能是min,max,count ... or something else. + + + +--- + +**7. [Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)** Level: Hard Tags: [Binary Search, Lint, Segment Tree] + + +SegmentTree大集合. Methods: `build, query, modify`. 不难。只是要都记得不犯错. + +#### Segment Tree +- build: recursively build children based on index-mid and link to curr node +- query: recursively try to find `node.start == targetStart && node.end == targetEnd`. Compare with node.mid +- modify: recursively try to find `node.start == targetStart && node.end == targetEnd`; modify and recursively assign upper interval with updated interval property. + + + +--- + +**8. [Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字, array size = n. 给一串query: 每个query是一个数, 目的找 count# items smaller than query element. + +#### Segment Tree +- 和平时的segment tree问题不同。 [0 ~ n] 代表实际数字: based on real value的segment tree. +- Modify时,把array里面的value带进去,找到特定的位子, 然后count + 1. +- 最终在SegmentTree leaf上面全是array里面实际的数字。 +- node.count: 在node range里面的有多少个数字 + +##### right use of modify() +- build() 只是 empty segment tree, 没有property +- modify() 需要: 1. 找到left, update count+=1; 2. aggregate all parent when after returning +- 所以每一个modify 都是在整个path上所有的node上 + count + +##### query trick +- 在query前,给进去的start和end是: 0 ~ value-1. +- `value-1`: 找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. + +##### About other basic segment tree setup +- [那么其他做过的SegmentTree是怎么样呢?] +- 那些构成好的SegmentTree(找min,max,sum)也有一个Array。但是构成Tree时候,随Array的index而构架。 +- 也就是说,假如有Array[x,y,....]:在leaf,会有[0,0] with value = x. [1,1] with value = y. +- [但是这题] +- 构成时,是用actual value.也就是比如Array[x,y,....]会产生leaf:[x,x]with value = ..; [y,y]with value =... +- 其实很容易看穿: +- 若给出一个固定的array构成 SegmentTree,那估计很简单:按照index从0~array.lengh,leaf上就是[0,0] with value = x. +- 若题目让构造一个空心SegmentTree, `based on value 0 ~ n-1 (n <= 10000)`, 然后把一个Array的value modify 进去。 +- 这样八成是另外一种咯。 + + + +--- + + + + + + + +## String (60) +**0. [Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)** Level: Medium Tags: [String] + + + + +--- + +**1. [Judge Route Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/Judge%20Route%20Circle.java)** Level: Easy Tags: [String] + + +简单的character checking. 各个方向, 加加减减. + + + +--- + +**2. [Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i: + 一旦有重复, rest map. + 没有重复时候, 不断map.put(), 然后求max值 + +问题: 每次reset map之后就开始从新从一个最早的index计算, 最坏情况是O(n^2): +'abcdef....xyza' + + + + +--- + +**9. [Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, String, Two Pointers] + + +基本思想: 用个char[]存string的frequency. 然后2pointer, end走到底, 不断validate. +符合的就process as result candidate. + +HashMap的做法比char[]写起来要复杂一点, 但是更generic + + + +--- + +**10. [Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String] + + +大清洗 O(nk) +map.size一旦>k,要把longest string最开头(marked by pointer:start)的那个char抹掉 +一旦某一个char要被清除,所以在这个char 的1st and last appearance之间的char都要被清洗from map + + + + +--- + +**11. [Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)** Level: Hard Tags: [Hash Table, String, Trie] + + +Obvious的做法是全部试一遍, 判断, 变成 O(n^2) * O(m) = O(mn^2). O(m): isPalindrome() time. + +当然不行了, 那就看是O(nlogN), 还是O(n)? + +#### 方法1: Hash Table + Palindrome的性质. 复合型. +O(mn) + +##### 思路 +- 每一个word, 都可以拆分成 front + mid + end. 如果这个word + 其他word可以组成palindrome,那就是说 +- 砍掉 (mid+end), front.reverse() 应该存在 words[] 里面. +- 砍掉 (front+mid), end.reverse() 应该存在 words[] 里面. +- 我们用HashMap存所有的, 然后reverse, 找配对就好. + +##### Corner case +- 如果有 empty string "", 那么它跟任何一个palindrome word, 都可以配对, 并且根据位置前后变换, 凑成2份 distinct indexes. +- 这样就有了那个 `if (reverseEnd.equals("")) {...}` 的logic. +- 注意: 虽然在处理砍头/砍尾的两个 for loop里面都在根据 empty string 重复记录, + 但因为 "" 自己本身不能作为起点, 所以overall只会在被其他palindrome配对时记录一次. + + +#### 方法2: Trie +还要做一下那. + + + +--- + +**12. [Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Change%20to%20Anagram.java)** Level: Easy Tags: [String] + + +HackerRank里面的random 题目: 给一个string, 一切成两半, 看两半要变化多少个字符, 能变成anagram. + +- 切两半成两个String A,B. 分别在int count[26]里面++, --. +- 记录 26个小写字母的频率. 如果全部抵消, 就是anagram. +- 注意: 最后count出来要除以2:字母不同,会在不同的字母位上加减count,那么就是刚好重复计算了一遍。所以除以二 + +- Note: HackerRank里要注意自己写: Scanner, import java.util, non-static method ...etc. + + + +--- + +**13. [Compare Version Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Version%20Numbers.java)** Level: Medium Tags: [String] + + +给两串version number, 由数字和'.' 组成. 比较先后顺序. + +If version1 > version2 return 1, if version1 < version2 return -1, otherwise return 0. + +#### divide and conquer +- 用 str.split("\\.") 分割string +- Convert成integer, 逐个击破 + +#### 注意 +- '1.0' 和 '0' 是相等的 +- 如果可以假设version integer都是valid, 直接Integer.parseInt()就可以了 +- 不然的话, 可以compare string + + + +--- + +**14. [Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Strings.java)** Level: Easy Tags: [String] + + +看StringA是不是包括所有 StringB的字符. + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + + + +--- + +**15. [Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)** Level: Easy Tags: [Basic Implementation, String] + + +介绍一种count数字的方法, 然后每一行读出上一行的结果, 一行一行推算. 问nth行是啥样? + +#### Basic Implementation +- 主要是题意很难理解, 非常misleading, 等到看明白题目, 其实没有什么算法要求. +- Count duplicates and print + + + +--- + +**16. [Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Partition DP +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- 确定末尾的2种状态: single letter or combos. 然后计算出单个letter的情况, 和双数的情况 +- 定义`dp[i] = 前i个digits最多有多少种decode的方法`. new dp[n + 1]. +- 加法原理: 把不同的情况, single-digit, double-digit 的情况加起来 +- dp[i] += dp[i - x], where x = 1, 2 +- note: calculate number from characters, need to - '0' to get the correct integer mapping. +- 注意: check value != '0', 因为'0' 不在条件之中(A-Z) +- Space, Time O(n) + +#### 引申 +- 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + + + +--- + +**17. [Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Anagrams.java)** Level: Medium Tags: [Hash Table, String] + + +给一串string, return list of list, 把anagram 放在一起. + +#### Hash Table, key 是 character frequency +- 存anagram +- 用 character frequency 来做unique key +- 用固定长度的char[26] arr 存每个字母的frequency; 然后再 new string(arr). +- 因为每个位子上的frequency的变化,就能构建一个unique的string +- O(nk), k = max word length + + +#### Hash Table, key 是 sorted string +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**18. [Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game.java)** Level: Easy Tags: [String] + + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + + + +--- + +**19. [Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20strStr().java)** Level: Easy Tags: [String, Two Pointers] + + +给两个string A, B, 找一个 B 在 A 种的起始位置. + +#### Two Pointer +- 找到B在A中的起始位置, 然后看一下从这个点开始的substring是否等于B就可以了 +- 还挺多坑的, 这些可以帮助优化: +- 1. 当B是“”的时候,也就是能在A的其实位置找到B....index = 0. +- 2. edge condition: 如果 haystack.length() < needle.length() 的话, 必须错, return -1 +- 3. 如果在某个index, A后面剩下的长度, 比B的长度短, 也是误解, return -1 + + + +--- + +**20. [Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/Length%20of%20Last%20Word.java)** Level: Easy Tags: [String] + + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + + + +--- + +**21. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**22. [Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Check Palindrome +- 前后两个指针, 往中间移动, 查看是否字母重合 + +#### 过滤 alphanumeric +- 可以用 ASCII code 来手动过滤, 只要 '0' ~ '9', 'a' ~ 'z', 'A' - 'Z' 之间的 +- 也可以用 regular expression: match 所有这些字母, 是 [a-zA-Z0-9] +- 那凡是不是这些字母的 match, 就是取反: "[^a-zA-Z0-9]". 测试: https://regex101.com/ + + + +--- + +**23. [Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20String.java)** Level: Easy Tags: [String] + + +给两个String, 看A rotate之后 能不能变成B + +#### LeetCode +- Basics +- StringBuffer.deleteCharAt(xx), StringBuffer.append(xx) +- O(n) + + +#### LintCode +- Different problem: 给一个char[], 要rotate offset times. +- *三步rotate* +- 有个坑:offset可能很长,那么要%length,才能得到真正需要rotate的部分。 +- Note: rotate 一个 full length之后,是string 不变 + + + +--- + +**24. [Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)** Level: Medium Tags: [Basic Implementation, Enumeration, String] + + +给一个时间string"12:09", 用里面的4个integer组合成其他时间string, 目标找最小的next time. + +如果组合出的time string 在input time之前, 默认 + 24 hours. + +#### String +- enumerate all candidates and filter to keep the correct ones +- String.compareTo(string) -> gives lexicographical comparision + + + +--- + +**25. [Encode and Decode Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20Strings.java)** Level: Medium Tags: [String] + + +如题. + +#### String +- 'word.length()#word' 这样encode, 可以避免遇到# +- 基于我们自己定的规律, 在decode的里面不需要过多地去check error input, assume所有input都是规范的. +- decode就是找"#",然后用"#"前的数字截取后面的string. + + + + +--- + +**26. [Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Prefix.java)** Level: Easy Tags: [String] + + +找一串String里面最长的公共prefix. + +#### Sort, compare string +- Sort O(nlogn) +- first and last string should share common prefix +- 这里假设题目要求的是所有string的公共 prefix, 而不是部分strings + +#### Brutle +- Nested loop, 每一次比较所有string 同位是否相等 +- 相等,append string. 不等,return. +- O(mn) + + + +--- + +**27. [Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)** Level: Medium Tags: [Backtracking, DFS, String] + + +给一串数字, 检查是否是valid IP, 如果合理, 给出所有valid 的IP组合方式. + +#### Backtracking +- 递归的终点:list.zie() == 3, 解决最后一段 +- 递归在一个index上面, pass s.toCharArray() +- validate string要注意leading '0' +- 注意: 递归的时候可以用一个start/level/index来跑路 +- 但是尽量不要去改变Input source, 会变得非常confusing. +- note: code有点messy, 因为要考虑IP的valid情况 +- 那个'remainValid', 其实是一个对于remain substring的判断优化, 不成立的就不dfs了 + + + +--- + +**28. [Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String.java)** Level: Medium Tags: [String] + + +#### Break by space, then flip +- 结尾不能有空格 +- trim() output +- 如果Input是 ""的话,split以后就啥也没有了 +- 另个题目Reverse Words in String (char[]) 可以in-place, 条件是char[]里面是没有首尾空格. +- Time, Space: O(n) + +#### Other methods +- flip entire string, then flip each individual string (代码有点多, 这道题犯不着) + + + +--- + +**29. [Reverse Words in a String II.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20II.java)** Level: Medium Tags: [String] + + +#### In-place reverse +- reverse用两回. 全局reverse。局部:遇到空格reverse +- 注意ending index: `i == str.length - 1`, 结尾点即使没有' '也要给reverse一下最后一个词 + + + + +--- + +**30. [Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20III.java)** Level: Easy Tags: [String] + + +给一个String, 里面的Word被single space split开来, 目的是reverse里面所有的Word, 但preserve Word 和 space order. + +#### Reverse function +- Reverse Words in a String II 的降级版, 去掉第一个overall reverse就好了 + + + +--- + +**31. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**32. [Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)** Level: Hard Tags: [KMP, String] + + +#### Divide by mid point, Brutle +- check (mid, mid+1), or (mid-1, mid+1). +- If the two position matches, that is a palindrome candidate +- 比较front string 是否是 end string 的substring +- O(n^2) +- timeout on last case: ["aaaaaa....aaaacdaaa...aaaaaa"] + +#### KMP +- TODO + + + +--- + +**33. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**34. [Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)** Level: Hard Tags: [Bit Manipulation, String] + + +#### String +- 首先要分两半解决,断点是'.': str.split("\\."); +- Integer那一半好弄,whie loop里: num%2, num/2. 做一个 `parseInteger()` function +- Decimal那边复杂点. 做一个 `parseDecimal()` function: +- bit == 1的数学条件: 当下num * 2 >= 1。 更新: num = num * 2 - 1; +- bit == 0的数学条件: num * 2 < 1. 更新: num = num * 2 + +#### 注意 +- num是 double, 小数在 `num = num * 2 - 1` 的公式下可能无限循环 +- 因此check: num重复性,以及binary code < 32 bit. +- 所以题目也才有了32BIT的要求! + + + +--- + +**35. [Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Substring.java)** Level: Medium Tags: [DP, String] + + +给一个string, 找到最长的palindrome substring. + +Related: Longest Palindromic Subsequence, Palindrome Partioning II + +O(n^2) is not too hard to think of. How about O(n)? + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + +#### DP: isPalin[][] +- 穷举double for loop. O(n^2) +- boolean isPalin[i][j], 每次确认有palindrome就记录下来true / false +- 穷举的for loop计算顺序: end point j, and stat point i = [0, j] +- 在计算 isPalin[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- double for loop: O(n^2). slower, because it guarantees O(n^2) due to the for loop + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**36. [Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)** Level: Hard Tags: [Coordinate DP, Stack, String] + + +给一串string, 里面只有`(`, `)`. 找最长valid parentheses 的长度. + +#### 1D Coordinate DP +- use dp[i] track local max, maintain global max +- int[] dp. dp[i]: longest valid string that ends on i. +- 结尾是 ')', 2种情况: 1. 刚好s[i-1]是'('; 2. s[i]的')'更前面的一个起始'(' 对应 +- 注意, 结尾如果是'('属于不合理情况, 忽略. +- init: dp[0] = 0, 单个char不可能成型. +- 计算顺序: 从左到右, 找local max, maintain global max +- O(n) space, O(n) runtime + +#### Stack +- Stack 里面存所有的open/close parentheses. +- 如果遇到stack.top()刚好开合结掉, 就stack.pop(). +- 剩下的都是不合理的elements. +- 有点像negatively找 solution: `endIndex - 最后一个failedIndex(stack.pop()) - 1`, 应该就是最后一个succeeded string的长度 +- 每次更新 endIndex 为stack.top(), 然后从stack继续找下一个failedIndex +- 所有的length作比较, 就可以找出最长length +- O(n) stack space, O(n) runtime. 应该比dp慢一点, 因为做了2遍O(n) + + + + +--- + +**37. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + +**38. [Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Words.java)** Level: Easy Tags: [Hash Table, String] + + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**39. [Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Characters.java)** Level: Easy Tags: [Array, String] + + +determine if characters are unique in string + +#### HashSet +- space O(n), time O(n) + +#### char[] +- space O(n), time O(nlogn) + +#### no additional data structure +- double for loop: O(n^2) + + + + +--- + +**40. [Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)** Level: Medium Tags: [DP, String] + + +根据题意, count # of palindromic substring. (不同index截取出来的substring算不同的情况) + +#### isPalin[][] +- build boolean[][] to check isPalin[i][j] with DP concept +- check all candidates isPalin[][] +- O(n^2) + +#### odd/even split check +https://leetcode.com/problems/palindromic-substrings/discuss/105689/Java-solution-8-lines-extendPalindrome + + + +--- + +**41. [Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Multiply%20Strings.java)** Level: Medium Tags: [Math, String] + + +给两个integer String, 求乘积 + +#### String calculation, basic implementation +- let num1 = multipier, num2 = base. +- mutiply and save into int[m + n], without carry. Loop over num1, each row num1[x] * num2 +- move carry to the correct index and direclty save result +- calculate carry on rst[]: sb.insert(0, c) such that no need to reverse() later +- remove leading '0', but do not delete string "0" +- time,space O(mn) + +#### Previous notes. +- Bad solution: reversing makes it complicated, no need to reverse. +- 1. 数字‘123’, 在数组里面, index == 0 是 ‘1’。 但是我们平时习惯从最小位数开始乘积,就是末尾的'3'开始。 +- 所以!翻转两个数字先!我去。这个是个大坑。 +- 2. 乘积product,和移动Carrier都很普通。 +- 3. !!最后不能忘了再翻转。 +- 4. 最后一个看坑。要是乘积是0,就返回‘0’。 但是这个其实可以在开头catch到没必要做到结尾catch。 +- 用到几个StringBuffer的好东西: reverse(), sb.deleteCharAt(i) +- 找数字,或者26个字母,都可以: s.charAt(i) - '0'; s.charAt(i) - 'a'; + + + +--- + +**42. [Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + +time: O(n) +space: O(n) + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path 的情况, 不懂得要问: +- 1. `.` 代表current directory, 可以忽略. +- 2. `../` 表示previous level. +- 3. double slash 可以忽略. +- 4. empty string 要output `/` +- 最终就是用stack (`上一个加进去的item, 用来备选pop() out`), 遇到 `../` pop()掉上一个加上去的item, 其余加进stack +- 最终用 '/' 把所有item连接起来. + + + +--- + +**43. [Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + +time: O(n) + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**44. [Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4.java)** Level: Easy Tags: [Enumeration, String] + + +Read4 题目. 理解题目: 是有个input object buff, 会被populated with data. + +#### String in char[] format +- 理解题目: 其实就是track `可以read多少bytes by read4() response` +- 另外一个有用的function `System.arraycopy(src, srcIndex, dest, destIndex, length)` + + + +--- + +**45. [Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)** Level: Hard Tags: [Enumeration, Math, String] + + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 + +#### 注意 +- StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 +- 注意加 " " 的时候, 如果多余, 要`trim()` +- 注意, 小于20的数字, 有自己的特殊写法, 需要额外handle +- 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 + + + + +--- + +**46. [Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome%20II.java)** Level: Easy Tags: [String] + + +#### Palindrome String +- delete an index = jump over the index +- 注意 boolean chance 可以用一个helper function + + + +--- + +**47. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**48. [Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- Summing space = `width + (size-1)`. maintain: 1. list of candidates, 2. width of actual words +- calculate space in between: `remain/(size - 1)` +- overall for loop; clean up list: 1. over size; 2. last item +- 一点也不难, 但是要小心: deal with list of string的时候, 注意处理干净sum size of list, 就行了. +- `干净处理space`: 只处理 (n-1) items, 然后最后一个拿到for loop 外面, 特殊处理. + +#### Notes +- Clarification, observation: +- can start with greedy approach to stack as many words as possible +- once exceed the length, pop the top, and justify the added words (untouched words tracked by index) +- left justify: given list/stack of words with size t, overall remaining space length m, +- deal with last line with special care: just fill one space, and fill the rest of the row with space +- Does not seem very complicated, but need additional care of calculating the amount of space needed. +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**49. [Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)** Level: Hard Tags: [Enumeration, String] + + +Read N Character using `Read4(char[] buf)` 的加强版: 可以不断读 read(buf, n) + +#### String +- 注意String的index handle, 慢慢写edge case +- 理解题目意思: `read4(char[] buf)` 这样的 `populate input object` 的function稍微少一点. +- 遇到时候, 仔细理解function用法, 不要慌乱. 其实思考方式很简单, 仔细handle string 还有 edge case就好了. + + + +--- + +**50. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**51. [String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- check sign, leading-0, overall size > 11, check max/min in Long format +- if passed all tests, parseInt() + +#### regular expression +- if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**52. [Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Roman%20to%20Integer.java)** Level: Easy Tags: [Math, String] + + +#### String +- 熟悉罗马字母规则 +- 1. 'I V X L C D M' 分别代表的数字 +- 2. 列举combo的情况,需要从原sum里面减掉多加的部分: 'IV, IX'减2, 'XL, XC'减20, 'CD, CM'减200. +- Leading `I(1*2)`, `X(10*2)`, `C(100*2)` causes double counting + +https://en.wikipedia.org/wiki/Roman_numerals + + + +--- + +**53. [Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +剥皮过程。解铃还须系铃人 +左边的外皮'{['在stack底部 +右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**54. [First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +方法1: 按照题意, 找到第一个 first index == last index的字母. + +方法2: 用hashmap存字母的index, 有些重复字母的index就会是个list. 找到单一index, 结合成list, sort, return list.get(0) + + + +--- + +**55. [Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)** Level: Easy Tags: [Math, String, Two Pointers] + + +#### Two pointers +- Use two pointers i, j to track the 2 strings +- Add when i and j are applicable. While (i >= 0 || j >= 0) +- StringBuffer.insert(0, x); +- handle carry + +#### wrong: convert to int +- 土办法没技术,把binary换成数字,加起来,再换成binary +- 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**56. [One Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/One%20Edit%20Distance.java)** Level: Medium Tags: [String] + + +如果S, T只用一个operation就能变成相等, return true. + +#### Edit: 删除,增加,和替换 +- 换完之后,理论上换成的String 就应该全等 +- for loop, 一旦找到不一样的char, 就判断那三种可能性: insert/delete/replace +- insert/delete 对于2个string来说, 效果是类似的 +- O(n) + + + +--- + +**57. [Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)** Level: Medium Tags: [Hash Table, String] + + + +#### Convert to orginal string +- shit by offset. `int offset = s.charAt(0) - 'a';` +- increase if less than 'a': `if (newChar < 'a') newChar += 26;` + +#### Previous notes +- 相同shift规则的string, 能被推算到同一个零起始点,就是共同减去一个char,最后就相等。以此作为key,用HashMap。一目了然。 +- 记得根据题目意思,一开始要String[] sort一下。 + + + +--- + +**58. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**59. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + + + + + + + +## Math (38) +**0. [Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)** Level: Medium Tags: [Hash Table, Math] + + +TODO: no need of hashMap, just use set to store the existing + +不难想到处理除法:考虑正负,考虑小数点前后。主要是小数点以后的要着重考虑。 +很容易忽略的是integer的益处。 + + +--- + +**1. [Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)** Level: Medium Tags: [Backtracking, Math] + + +TODO: what about regular DFS/backtracking to compute the kth? dfs(rst, list, candiate list, k) + +k是permutation的一个数位。而permutation是有规律的。 + +也就是说,可以根据k的大小来判断每一个数位的字符(从最大数位开始,因为默认factorio从最大数位开始变化)。 + +于是先求出n!, 然后 k/n!就可以推算出当下这一个数位的字符。然后分别把factorio 和 k减小。 + +另外, 用一个boolean[] visited来确保每个数字只出现一次。 + +这个方法比计算出每个permutation要efficient许多。 + + + + +--- + +**2. [Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)** Level: Easy Tags: [Math] + + + + +--- + +**3. [Power of Three.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Three.java)** Level: Easy Tags: [Math] + + +方法1: +Power of 3: 3 ^ x == n ? +意思是 n / 3 一直除, 最后是可以等于1的, 那么就有了 n/=3, check n%3, 最后看结果是不是整除到1的做法. 用while loop. + +方法2: +如果n是power of 3, 那么 3 ^ x的这个 x一定是个比n小的数字. 那么可以在 0 ~ n 之间做binary serach, 但是就比较慢. + +方法3: +巧妙的想法.最大的3^x integer是 3^19. 那么找到这个数, 一定可以被n整除. 一步到位. + + + +--- + +**4. [Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)** Level: Easy Tags: [Array, Math] + + +简单的实现, 加1, 进位. 唯一取巧的地方, 最后如果要多一位, 一定是10000...这个模式, 可以走捷径, 直接来个+1size的array, 然后第一位=1. +注意,转换成long也不合理,用太多memory. + + +--- + +**5. [Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)** Level: Easy Tags: [Bit Manipulation, Math] + + +跟powerOfThree一样: 可以loop, check mod; 也可以用binary search找合适的数字. + + + +--- + +**6. [Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)** Level: Medium Tags: [Hash Table, Math] + + +其实想到了切入点, 是个可难可简单的题目. 这里的encode就是想办法把url存起来, 然后给个 key. +那么具体怎么做这个key, 简单就可以用一个map, 然后counting. 复杂一点就可以做random letter/number组成key. + + + +--- + +**7. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**8. [Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Perfect%20Square.java)** Level: Review Tags: [Binary Search, Math] + + +Binary找sqrt. 基本 mid+1, mid-1 template. +注意: define index as long. + + + +--- + +**9. [Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)** Level: Medium Tags: [Binary Search, Math] + + +傻做就O(n), 要更好就考虑O(logN). +减少重复计算, 一切两半. + +注意: +- n/2的奇数偶数 +- n的正负 +- n == 0的情况, x == 0, x == 1 的情况. + + +--- + +**10. [Add Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Digits.java)** Level: Easy Tags: [Math] + + +方法1: 普通做法就是按照题意, double-while loop把数字加起来. 第一层循环是O(n), 然后第二层循环就少很多数位, overall O(n) + +方法2: 找数学规律, 每过9个数字, 取mod就会开始重复, 所以给所有数字取mod 就可以间接找到答案. O(1) + + + +--- + +**11. [Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. +遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + +跟Add Binary的理解方式一模一样. + +注意: +Linked List 没有天然size. +用DummyNode(-1).next来hold住结果. + + + + +--- + +**12. [Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Linked List, Math, Two Pointers] + + +LinkedList 里面有 cycle, 找到cycle的起始点(第一个重复出现的element). + +#### Slow, fast Pointer +- 快慢指针, O(1)space. +- 1. 确认有cycle后 2. 数学问题:找到开头. +- 当head == slow.next时候, head就是cycle starting point. +- 也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +#### 证明 +- 1. 假设慢指针走t步, 快指针走快一倍, 也就是2t. +- 2. 我们假设cycle的长度是Y, 而进入cycle之前的长度为X. +- 3. 假设慢指针走了m圈cycle, 而快指针走了n圈cycle之后, 两个pointer相遇. +- 4. 最终在Y cycle里面的K点相遇, 也就是两个指针都在这最后一圈里面走了K 步. +- 那么: +- t = X + mY + K +- 2t = X + nY + K +- 整合公式: X + K = (n - 2m)Y +- 这里的m和n不过是整数的跑圈数, 也就是说X和K加在一起, 总归是结束cycle. X 和 K 互补 +- 结论: 当slow/fast 指针在K点相遇后, 再走X步, 就到了cycle的起点, 也就是题目要求的起点. + +#### Hash Table, O(n) space + + + + +--- + +**13. [Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Integer.java)** Level: Easy Tags: [Math] + + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + + + +--- + +**14. [Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + + +#### s- qrt(int x) +- 理解题意, 从[0, x]找一个可以m*m=x的值. +- 注意, 如果找不到, 最后问考官该return一个什么值:按道理,因为return int, 会取整,那么return一个平方最close to x就可以. +- 注意 mid 用 long, 因为很可能超过最大int. + +#### sqrt(double x) +- 二分float number, 应该用精度来定义结尾. +- 还是二分, 但是判断条件变成: while ( end - start > eps) +- eps = 1e-12,也就是精度到1e-12 + + + +--- + +**15. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**16. [Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Primes.java)** Level: Easy Tags: [Hash Table, Math] + + +计数: 所有小于n的prime number. + +#### prime number定义 +- >=2的没有除自己和1以外公约数的数。 +- 还有另外一个定义方法: 这个n,有没有小于n的一个i, 而达到: i * i + # of i = n. 如果有,那就不是 prime + +#### Steps +- 一个boolean长条,存isPrime[]。 然后从i=2, 全部变true. +- hash key: the number itself +- 然后利用这个因子的性质,非prime满足条件: self*self, self * self + self ... etc. +- 所以就check每一个j, j+i, j+i+i, 然后把所有non-prime全部mark成false. +- 最后,数一遍还剩下的true个数就好了 + + + +--- + +**17. [Excel Sheet Column Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Number.java)** Level: Easy Tags: [Math] + + +#### Math +- 26位的运算, 根据10位运算去思考 +- 'A' - 'A' = 0. 所以 char - 'A' + 1 = 题目里的对应数位 +- 或者: 26位运算和10位一样:num += 每位的digit * Math.pow(26, 数位号) + + + + +--- + +**18. [Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Title.java)** Level: Easy Tags: [Math] + + +#### 基本换算 +- 26位 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + + + +--- + +**19. [Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)** Level: Easy Tags: [DP, Math, Memoization] + + +#### Memoization +- fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + +#### DP array. +- 滚动数组, 简化DP + +#### recursively calculate +- recursively calculate fib(n - 1) + fib(n - 2). 公式没问题, 但是时间太长, timeout. + + + + +--- + +**20. [Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)** Level: Easy Tags: [Array, Bit Manipulation, Math] + + +给一串unique数字, 数字取自 [0 ~ n], 无序, 找第一个skipped的数字. + +#### Swap +- 跟First Missing Positive 非常像, 只有一行代码的区别. +- swap 所有的数字, 到自己的correct position +- 最后一个for loop找到错位的index, 也就是缺的数字. + +#### Bit Manipulation +- XOR will only retain bits that are different 1 ^ 0 = 1, but 0^0, 1^1 == 0 +- Use that feature, 把所有value都和index XOR了 +- 剩下的多余的数字, 其实是那个index无法被XOR消掉, 也就是那个缺的number value. +- 注意: 题目告诉数字是 [0 ~ n], 然而缺一个数字, 那么在[0 ~ n - 1] 里面, 最大的数字(不管缺没缺), 一定是 n = nums.length. + +#### HastSet +- 全存, 找missing +- O(n) space, 不合题意 + +#### sorting +- sort, 找1st missing +- O(n log n) 太慢, 不合题意 + + + +--- + +**21. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + +**22. [Ugly Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number.java)** Level: Medium Tags: [Math] + + +LeetCode: 判断数字是否是ugly number. (definition: factor only have 2, 3, 5) + +#### Math +- 看是否可以整除. +- 看整除最终结果是否== 1 + +LintCode: 找kth ugly number, 应该与 Ugly Number II是一样的 + +- 方法1: PriorityQueue排序。用ArrayList check 新的ugly Number是否出现过。 +- 方法1-1:(解释不通,不可取)用PriorityQueue排序。神奇的3,5,7走位:按照题目答案的出发,定了3,5,7以什么规律出现。但是题目并没有特殊表明。 +- 方法2: DP . Not Done yet. + + + + +--- + +**23. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + +**24. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + +**25. [Number of Digit One.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Digit%20One.java)** Level: Hard Tags: [Math] + + +Pure math problem, not quite representative + +Explanation +https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython + + + +--- + +**26. [Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)** Level: Medium Tags: [DP, Math] + + +具体看题目: count # of valid rectangles (four corner are 1) in a grid[][]. + +#### basic thinking + Math +- Fix two rows and count matching columns +- Calculate number rectangles with `combination` concept: +- total number of combinations of pick 2 points randomly: count * (count - 1) / 2 + +#### DP +- TODO. HOW? + +#### Brutle +- O(m^2 * n^2), times out + + + +--- + +**27. [Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Multiply%20Strings.java)** Level: Medium Tags: [Math, String] + + +给两个integer String, 求乘积 + +#### String calculation, basic implementation +- let num1 = multipier, num2 = base. +- mutiply and save into int[m + n], without carry. Loop over num1, each row num1[x] * num2 +- move carry to the correct index and direclty save result +- calculate carry on rst[]: sb.insert(0, c) such that no need to reverse() later +- remove leading '0', but do not delete string "0" +- time,space O(mn) + +#### Previous notes. +- Bad solution: reversing makes it complicated, no need to reverse. +- 1. 数字‘123’, 在数组里面, index == 0 是 ‘1’。 但是我们平时习惯从最小位数开始乘积,就是末尾的'3'开始。 +- 所以!翻转两个数字先!我去。这个是个大坑。 +- 2. 乘积product,和移动Carrier都很普通。 +- 3. !!最后不能忘了再翻转。 +- 4. 最后一个看坑。要是乘积是0,就返回‘0’。 但是这个其实可以在开头catch到没必要做到结尾catch。 +- 用到几个StringBuffer的好东西: reverse(), sb.deleteCharAt(i) +- 找数字,或者26个字母,都可以: s.charAt(i) - '0'; s.charAt(i) - 'a'; + + + +--- + +**28. [Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)** Level: Medium Tags: [Array, Math] + + +#### Array, Math +- 这个问题更在于问题本身的分析 (而且还有多余条件); 最终的for loop 也比较不standard. +- People younger than 15 cannot make requests due to the first rule. +- From the age of 15, people can make requests to the same age: a[i] * (a[i] - 1) requests. +- People can make requests to younger people older than 0.5 * i + 7: a[j] * a[i] requests. +- The third rule is redundant as the condition is already covered by the second rule. +- TODO: the approach. + + + +--- + +**29. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + +time: O(n) +space: O(n) + +给一串点, 找是否有一个所有点中间的, 跟y-axis平行的中线. + +#### Hash Table +- 1. store in `Map>`, 2. iterate over map, check head,tail against the mid point +- 很好的细节题目: +- 1. 除以2, 需要存double +- 2. (问面试官)可以有重复的点! 所以track `set` +- 3. 处理 left==right时候, 就当做两个点来处理. +- 4. 存进set里面没有sort, 但是最后做check的时候, 需要sort list +- 时间: visit all nodes 两遍, O(n) + + + +--- + +**30. [Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + +time: O(n) + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**31. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**32. [Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)** Level: Hard Tags: [Enumeration, Math, String] + + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 + +#### 注意 +- StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 +- 注意加 " " 的时候, 如果多余, 要`trim()` +- 注意, 小于20的数字, 有自己的特殊写法, 需要额外handle +- 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 + + + + +--- + +**33. [String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- check sign, leading-0, overall size > 11, check max/min in Long format +- if passed all tests, parseInt() + +#### regular expression +- if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**34. [Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Roman%20to%20Integer.java)** Level: Easy Tags: [Math, String] + + +#### String +- 熟悉罗马字母规则 +- 1. 'I V X L C D M' 分别代表的数字 +- 2. 列举combo的情况,需要从原sum里面减掉多加的部分: 'IV, IX'减2, 'XL, XC'减20, 'CD, CM'减200. +- Leading `I(1*2)`, `X(10*2)`, `C(100*2)` causes double counting + +https://en.wikipedia.org/wiki/Roman_numerals + + + +--- + +**35. [Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math] + + +根据题意枚举, 再根据规则basic implementation + +#### Alter input + +#### HashTable + Two Pointer + + + +--- + +**36. [Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)** Level: Easy Tags: [Math, String, Two Pointers] + + +#### Two pointers +- Use two pointers i, j to track the 2 strings +- Add when i and j are applicable. While (i >= 0 || j >= 0) +- StringBuffer.insert(0, x); +- handle carry + +#### wrong: convert to int +- 土办法没技术,把binary换成数字,加起来,再换成binary +- 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**37. [Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)** Level: Hard Tags: [DFS, Greedy, Math] + + +#### Greedy, Iterative +- For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +- We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +- Track the newly formed string; if new, add the new char to overall result +- Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] +- Same concept as dfs + +#### DFS +- Same concept: use window to cut out tail, and append with new candidate +- do this for k^n = Math.pow(k, n) times + + + +--- + + + + + + + +## DP (85) +**0. [Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)** Level: Medium Tags: [DP] + + +这个DP有点诡异. 需要斟酌。 +NOT DONE YET + + +--- + +**1. [Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + +#### DP +- 找对方程dp[x], 积累到amount x最少用多少个coin: #coin是value, index是 [0~x]. +- 子问题的关系是: 如果用了一个coin, 那么就应该是f[x - coinValue]那个位置的#coins + 1 + +##### initialization +- 处理边界, 一开始0index的时候, 用value0. +- 中间利用Integer.MAX_VALUE来作比较, initialize dp[x] +- 注意, 一旦 Integer.MAX_VALUE + 1 就会变成负数. 这种情况会在coin=0的时候发生. + +##### Optimization +- 方法1: 直接用Integer.MAX_VALUE +- 方法2: 用-1, 稍微简洁一点, 每次比较dp[i]和 dp[i - coin] + 1, 然后save. 不必要做多次min比较. + +#### Memoization +- dp[i] 依然表示: min # of coints to make amount i +- initialize dp[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录dp[amount] 如果已经给过value, 不要重复计算, 直接return. +- 但是这道题没必要强行做memoization, 普通DP的状态和方程相对来说很好找到 + + + +--- + +**2. [Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### DP +- 求最值, 想到DP. Time/Space O (n) +- 两个特别处: +- 1. 正负数情况, 需要用两个DP array. +- 2. continuous prodct 这个条件决定了在Math.min, Math.max的时候, +- 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. +- 这也就注定了需要一个global variable 来hold result. + +#### Space optimization, rolling array +- maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins +- Time: O(n), space: O(1) + + + +--- + +**3. [k Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/k%20Sum.java)** Level: Hard Tags: [DP] + + +DP. 公式如何想到, 还需要重新理解. + +dp[i][j][m]: # of possibilities such that from j elements, pick m elements and sum up to i. +i: [0~target] + +dp[i][j][m] = dp[i][j-1][m] + dp[i - A[j - 1]][j-1][m-1] + (i not included) (i included) + + + +--- + +**4. [Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DP, Tree] + + +Not quite clear. +根据左右分割而总结出了原理, 每次分割, 左右两边都会有一定数量的permutation, 总体上的情况数量当然是相乘. +然后每一个不同的分割点都加一遍: +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +然后把数学公式转换成DP的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**5. [Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +跟unique path的grid一样, 目标走到右下角, 但是grid里面可能有obstacle, 不能跨越. 求unique path 的count. + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- 考虑最终结尾需要的状态:如何组成,写出公式. +- 公式中注意处理能跳掉的block, marked as 1. '到不了', 即为 0 path in dp[i][j]. + + + +--- + +**6. [Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Corrdinate DP +- Space, Time: O(MN) +- dp[i][j] 就是(i, j)上最多能炸掉的enemy数量 +- dp[i][j] 需要从4个方向加起来, 也就是4个方向都要走一遍, 所以分割成 UP/Down/Left/Right 4个 int[][] +- 最后一步的时候求max +- 分方向考虑的方法很容易想到,但是四个方向移动的代码比较繁琐. +- 往上炸: 要从顶向下考虑 +- 往下炸: 要从下向上考虑 +- 熟练写2D array index 的变换. + +似乎还有一个更简洁的方法, 用col count array: http://www.cnblogs.com/grandyang/p/5599289.html + + + +--- + +**7. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**8. [Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)** Level: Medium Tags: [Backpack DP, DP] + + +给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法. + +nums 里的数字, 可以重复使用. 不同的order可以算作不同的拼法. + +#### Backpack DP +- dp[i] 表示: # of ways to fill weight i +- 1维: dp[w]: fill weigth w 有多少种方法. 前面有多少种可能性, 就sum多少个: +- dp[w] = sum{dp[w - nums[i]]}, i = 0~n + +##### 分析 +- 拼背包时, 可以有重复item, 所以考虑'最后被放入的哪个unique item' 就没有意义了. +- 背包问题, 永远和weight分不开关系. +- 这里很像coin chagne: 考虑最后被放入的东西的value/weigth, 而不考虑是哪个. + + + + + + +--- + +**9. [Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)** Level: Hard Tags: [Binary Search, DP, Partition DP] + + +给一串书pages[i], k个人, pages[i] 代表每本书的页数. k个人从不同的点同时开始抄书. + +问, 最快什么时候可以抄完? + +#### Partition DP +- 第一步, 理解题目要求的问题: 前k个人copy完n本书, 找到最少的用时; 也可以翻译成: `n本书, 让k个人来copy, 也就是分割成k段`. +- 最后需要求出 dp[n][k]. 开: int[n+1][k+1]. +- 原理: +- 1. 考虑最后一步: 在[0 ~ n - 1]本书里, 最后一个人可以选择copy 1 本, 2 本....n本, 每一种切割的方法的结果都不一样 +- 2. 讨论第k个人的情况, 在 j = [0 ~ i] 循环. 而循环j时候最慢的情况决定 第k个人的结果(木桶原理): `Math.max(dp[j][k - 1], sum)`. +- 3. 其中: `dp[j][k-1]` 是 [k-1]个人读完j本书的结果, 也就是著名的`上一步`. 这里循环考虑的是第k个人不同的j种上一步 : ) +- 4. 循环的结果, 是要存在 dp[i][k] = Math.min(Math.max(dp[j][k - 1], sum[j, i]), loop over i, k, j = [i ~ 0]) +- Time: O(kn^2), space O(nk) + +##### Init +- Init: dp[0][0] = 0, 0个人0本书 +- Integer.MAX_VALUE的运用: +- 当 i = 1, k = 1, 表达式: dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); +- 唯一可行的情况就只有一种: i=0, k=0, 刚好 0 个人 copy 0 本书, dp[0][0] = 0. +- 其他情况, i = 1, k = 0, 0 个人读 1本书, 不可能发生: 所以用Integer.MAX_VALUE来冲破 Math.max, 维持荒谬值. +- 当 i=0, k=0 的情况被讨论时候, 上面的方程式才会按照实际情况计算出 dp[i][k] +- 这道题的init是非常重要而tricky的 + +##### 计算顺序 +- k个人, 需要一个for loop; +- k个人, 从copy1本书开始, 2, 3, ... n-1,所以 i=[1, n], 需要第二个for loop +- 在每一个i上, 切割的方式可以有[0 ~ i] 中, 我们要计算每一种的worst time + +##### 滚动数组 +- [k] 只有和 [k - 1] 相关 +- Space: O(n) + +#### Binary Search +- 根据: 每个人花的多少时间(time)来做binary search: 每个人花多久时间, 可以在K个人之内, 用最少的时间完成? +- time variable的范围不是index, 也不是page大小. 而是[minPage, pageSum] +- validation 的时候注意3种情况: 人够用 k>=0, 人不够所以结尾减成k<0, 还有一种是time(每个人最多花的时间)小于当下的页面, return -1 +- O(nLogM). n = pages.length; m = sum of pages. + + + + +--- + +**10. [Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)** Level: Hard Tags: [DP, Interval DP, String] + + +- 给两个string S, T. 检验他们是不是scramble string. +- scramble string 定义: string可以被分拆成binary tree的形式, 也就是切割成substring; +- 旋转了不是leaf的node之后, 形成新的substring, 这就是原来string的 scramble. + + +#### Interval DP 区间型 +- 降维打击, 分割, 按照长度来dp. +- dp[i][j][k]: 数组S从index i 开始, T从index j 开始, 长度为k的子串, 是否为scramble string + +##### Break down +- 一切两半以后, 看两种情况: , 或者不rotate这两半. 对于这些substring, 各自验证他们是否scramble. +- 不rotate分割的两半: S[part1] 对应 T[part1] && S[part2] 对应 T[part2]. +- rotate分割的两半: S[part1] 对应 T[part2] && S[part2] 对应 T[part1]. + +##### Initialization +- len == 1的时候, 其实无法旋转, 也就是看S,T的相对应的index是否字符相等. +- initialization非常非常重要. 很神奇, 这个initailization 打好了DP的基础, 后面一蹴而就, 用数学表达式就算出了结果. +- input s1, s2 在整个题目的主要内容里面, 几乎没有用到, 只是用在initialization时候. +- More details, 看解答 + + + +--- + +**11. [Best Time to Buy and Sell Stock with Cooldown.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Cooldown.java)** Level: Medium Tags: [DP] + + +Sequence DP +跟StockIII很像. 分析好HaveStock && NoStock的状态, 然后看最后一步. + + + +--- + +**12. [Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP] + + +给两个string, A, B. 找这两个string里面的LCS: 最长公共字符长度 (不需要是continuous substring) + +#### Double Sequence DP +- 设定dp长度为(n+1), 因为dp[i]要用来表示前i个(ith)时候的状态, 所以长度需要时i+1才可以在i位置, hold住i. +- 双序列: 两个sequence之间的关系, 都是从末尾字符看起, 分析2种情况: +- 1. A最后字符不在common sequence 或者 B最后字符不在common sequence. +- 2. A/B最后字符都在common sequence. 总体count + 1. + + + +--- + +**13. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**14. [Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + +time: O(MN) +Space: O(N) + +两个字符串, A要变成B, 可以 insert/delete/replace, 找最小变化operation count + +#### Double Sequence +- 考虑两个字符串的末尾index s[i], t[j]: 如果需要让这两个字符一样, 可能使用题目给出的三种operation: insert/delete/replace? +- 先calculate最坏的情况, 3种operation count + 1; 然后在比较match的情况. +- 注意, 在i或者j为0的时候, 变成另外一个数字的steps只能是全变. +- 第一步, 空间时间都是O(MN), O(MN) +- 滚动数组优化, 空间O(N) + +##### Detail analysis +- insert: assume insert on s, `#ofOperation = (s[0 ~ i] to t[0 ~ j-1]) + 1;` +- delete: assume delete on t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j]) + 1;` +- replace: replace both s and t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j - 1]) + 1;` +- dp[i][j]代表了两个 sequence 互相之间的性质: s[0 ~ i] 转换成 s[0~j] 所需要的最少 operation count +- init: 当i==0, dp[0][j] = j; 每次都要 + j 个character; 同理, 当j==0, dp[i][0] = i; +- 而dp[i][j]有两种情况处理: `s[i] == t[j]` or `s[i] != t[j]` + +##### 何时initialize +- 这种判断取决于经验: 如果知道initialization可以再 double for loop 里面一起做, 那么可以留着那么做 +- 这样属于 `需要什么, initialize什么` +- 事后在做space optimization的时候, 可以轻易在 1st dimension 上做rolling array + +#### Search +- 可以做, 但是不建议:这道题需要找 min count, 而不是search/find all solutions, 所以search会写的比较复杂, 牛刀杀鸡. + + + +--- + +**15. [Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)** Level: Hard Tags: [DP, String] + + +Double Sequence DP: +0. DP size (n+1): 找前nth的结果, 那么dp array就需要开n+1, 因为结尾要return dp[n][m] +1. 在for loop 里面initialize dp[0][j] dp[i][0] +2. Rolling array 优化成O(N): 如果dp[i][j]在for loop里面, 就很好替换 curr/prev + + + +--- + +**16. [Ones and Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Ones%20and%20Zeroes.java)** Level: Hard Tags: [DP] + + +还是Double Sequence, 但是考虑第三种状态: 给的string array的用量. +所以开了3维数组. + +如果用滚动数组优化空间, 需要把要滚动的那个for loop放在最外面, 而不是最里面. +当然, 这个第三位define在 dp[][][]的哪个位置, 问题都不大. + +另外, 注意在外面calcualte zeros and ones, 节约时间复杂度. + + + +--- + +**17. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**18. [Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +##### DP +- 计数DP.注意方程式前两位置加在一起: 前两种情况没有overlap, 也不会缺情况. +- 注意initialization, 归1. +- 需要initialize的原因是,也是一个reminder: 在方程中会出现-1index +- Of course, row i = 0, or col j = 0, there is only 1 way to access +- time O(mn), space O(mn) + +##### 滚动数组 +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + + + + +--- + +**19. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**20. [Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Square.java)** Level: Medium Tags: [Coordinate DP, DP] + + +只能往右边,下面走, 找面积最大的 square. 也就是找到变最长的 square. + +#### DP +- 正方形, 需要每条边都是一样长度. +- 以右下角为考虑点, 必须满足条件: left/up/diagonal的点都是1 +- 并且, 如果三个点分别都衍生向三个方向, 那么最长的 square 边就是他们之中的最短边 (受最短边限制) +- dp[i][j]: max square length when reached at (i, j), from the 3 possible directions +- dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; +- Space, time O(mn) + +##### init +每个点都可能是边长1, 如果 matrix[i][j] == '1' + +##### 滚动数组 +[i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + +--- + +**21. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**22. [Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)** Level: Medium Tags: [DP, Game Theory, Greedy] + + +拿棋子游戏, 每个人可以拿1个或者2个, 拿走最后一个子儿的输. 问: 根据给的棋子输, 是否能确定先手的输赢? + +Game Theory: 如果我要赢, 后手得到的局面一定要'有输的可能'. + +#### DP, Game Theory +- 要赢, 必须保证对手拿到棋盘时, 在所有他可走的情况中, '有可能败', 那就足够. +- 设计dp[i]:表示我面对i个coins的局面时是否能赢, 取决于我拿掉1个,或者2个时, 对手是不是会可能输? +- dp[i] = !dp[i - 1] || !dp[i-2] +- 时间: O(n), 空间O(n) +- 博弈问题, 常从'我的第一步'角度分析, 因为此时局面最简单. + +#### Rolling Array +空间优化O(1). Rolling array, %2 + + + +--- + +**23. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**24. [Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)** Level: Easy Tags: [DP, Memoization, Sequence DP] + + +每一步可以走1步或者2步, 求总共多少种方法爬完梯子. + +#### Recursive + Memoization +- 递归很好写, 但是重复计算, timeout. time: O(2^n) +- O(2^n): each n can spawn 2 dfs child, at next level, it will keep spawn. Total 2^n nodes will spawn. +- 用全局变量int[] memo 帮助减少重复计算 +- O(n) time, space + +#### DP +- 加法原理, 最后一步被前两种走法决定: dp[i] = dp[i - 1] + dp[i - 2] +- 基础sequence DP, int[] dp = int[n + 1]; +- DP[]存的是以 1-based index的状态 +- dp[i]: count # of ways to finish 前i个 台阶 +- 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1] +- dp[0]往往是有特殊状态的 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**25. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**26. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**27. [Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)** Level: Easy Tags: [Brainteaser, DP, Game Theory] + + +#### Brainteaser +- 著名Nim游戏 +- 写一些,发现n=4,5,6,7,8...etc之后的情况有规律性: 谁先手拿到4就输了. +- 最终很简单n%4!=0就可以了, time, space O(1) + +#### DP +- 正规地找规律做, 就跟 coins in a line 一样, 按照先手后手来做 +- 可以rolling array 优化空间 +- Time O(n), 当然啦, 这个题目这样会timeout, 可以使用brainteaser的做法写出结果. + + + +--- + +**28. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**29. [Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)** Level: Medium Tags: [Array, DP, Greedy] + + +给出步数,看能不能jump to end. + +#### Greedy - start from index = 0 +- Keep track of farest can go +- 一旦 farest >= nums.length - 1, 也就是到了头, 就可以停止, return true. +- 一旦 farest <= i, 也就是说, 在i点上, 已经走过了步数, 不能再往前跳, 于是 return false +- This can be done using DP. However, greedy algorithm is fast in this particular problem. + +#### Greedy - start from index = n - 1 +- greedy: start from end, and mark last index +- loop from i = [n - 2 -> 0], where i + nums[i] should always >= last index +- check if last == 0 when returning. It means: can we jump from index=0 to the end? +- Time: O(n), beat 100% + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (A[j] >= i - j), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- Time: O(n^2) + + + + +--- + +**30. [Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change%202.java)** Level: Medium Tags: [Backpack DP, DP] + + +给串数字, target amount, 求总共多少种方式可以reach the amount. + +#### DP +- O(MN): M, total target amount; N: size of coins +- 类似于: 网格dp, unique path 里面的2种走法: 从上到下, 从左到右 +- 状态: dp[i]: sum of ways that coins can add up to i. +- Function: dp[j] += dp[j - coins[i]]; +- Init: dp[0] = 1 for ease of calculation; other dp[i] = 0 by default +- note: 避免重复count, 所以 j = coins[i] as start +- 注意 coins 需要放在for loop 外面, 主导换coin的流程, 每个coin可以用无数次, 所以在每一个sum value上都尝试用一次每个coin + +#### knapsack problem: backpack problem + + + +--- + +**31. [Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + +time: O(nm), m = # of colors +space: O(nm) + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, 但是不知道最后一个房子选什么颜色, 那么就遍历最后一个房子(i - 1)的颜色 +- 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- 考虑DP最后一个位置的情况(颜色选择):需要附带颜色status在DP[i]上: 定义二维数组, 其中一位是status +- dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. +- dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- 然后选ith 房子的 color, 再选(i-1)th 房子的color. Double for loop, skip same color + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 + + + +--- + +**32. [Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Partition DP +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- 确定末尾的2种状态: single letter or combos. 然后计算出单个letter的情况, 和双数的情况 +- 定义`dp[i] = 前i个digits最多有多少种decode的方法`. new dp[n + 1]. +- 加法原理: 把不同的情况, single-digit, double-digit 的情况加起来 +- dp[i] += dp[i - x], where x = 1, 2 +- note: calculate number from characters, need to - '0' to get the correct integer mapping. +- 注意: check value != '0', 因为'0' 不在条件之中(A-Z) +- Space, Time O(n) + +#### 引申 +- 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + + + +--- + +**33. [Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +找连续的持续上升子序列的长度. + +#### Coordinate DP +- 1D coordinate, dp 的角标, 就是代表 index i 的状态 +- 求最值, dp[i] = 在index i位置的最长子序列 +- 如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1 +- 如果没有持续上升, 那么dp[i] = 1, 重头来过 +- maintain max + +#### Basic +- 用一个数存current count, maintain max + + + +--- + +**34. [Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +#### DP +- Time, Space O(MN) +- 往右下角走, 计算最短的 path sum. 典型的坐标型. +- 注意: init 第一行的时候, 要accumulate dp[0][j - 1] + grid[i][j], 而不是单纯assign grid[i][j] + +#### Rolling Array +- Time O(MN), Space O(1) +- 需要在同一个for loop里面完成initialization, 和使用dp[i][j] +- 原因: dp[i % 2][j] 在被计算出来的时候, 是几乎马上在下一轮是要被用的; 被覆盖前不备用,就白算 +- 如果按照第一种方法, 在开始initialize dp, 看起来固然简单, 但是不方便空间优化 + + + +--- + +**35. [Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)** Level: Medium Tags: [Bit Manipulation, Bitwise DP, DP] + + +给一个数组, 算里面有多少bit 1. + +#### Bitwise DP +- 对于每一个数字, 其实很简单就能算出来: 每次 >>1, 然后 & 1 就可以count 1s. Time: 一个数字可以 >>1 O(logN) 次 +- 现在要对[0 ~ num] 都计算, 也就是N个数字, 时间复杂度: O(nLogN). +- 用DP来优化, 查找过的number的1s count, 存下来在 dp[number]里面. +- 计算你顺序从 0 -> num, count过的数字就可以重复利用. +- Bit题目 用num的数值本身表示DP的状态. +- 这里, dp[i] 并不是和 dp[i-1]有逻辑关系; 而是dp[i] 和dp[i>>1], 从binary representation看出有直接关系. + + + +--- + +**36. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**37. [House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) or rolling array O(1) + +搜刮房子, 相邻的不能碰. 每个房子里有value, 求max. + +#### Sequence DP +- dp[i]: 前i个房子拿到的max gain +- 看最后结尾状态的前一个或前两个的情况,再综合考虑当下的 +- 搞清楚当下[i]的和之前[i-x]的情况的关系: 不可以连着house, 那么就直接考虑 dp[i-2]的情况 +- Sequence DP, new dp[n + 1]; + +#### Rolling Array +- [i]'只和前两个位子 [i-1], [i - 2]'相关 +- 用%2来标记 [i], [i - 1], [i - 2]三个位置. +- 其他滚动时惯用curr/prev来表示坐标, 这里%2虽然抽象, 但是更加实用. + + + + +--- + +**38. [House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)** Level: Medium Tags: [DP, Sequence DP, Status DP] + + +和House Robber I 类似, 搜刮房子, 相邻不能动. 特点是: 现在nums排成了圈, 首尾相连. + +#### Sequence DP +- dp[i][status]: 在 status=[0,1] 情况下, 前i个 房子拿到的 max rob gain. status=0, 1st house robbed; status=1, 1st house skipped +- 根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]); +- 特别的是,末尾的last house 和 first house相连. 这里就需要分别讨论两种情况: 第一个房子被搜刮, 或者第一个房子没被搜刮 +- be careful with edge case nums = [0], only with 1 element. +- Time,space: O(n) + +#### 两个状态 +- 是否搜刮了第一个房子, 分出两个branch, 可以看做两种状态. +- 可以考虑用两个DP array; 也可以加一dp维度, 补充这个状态. +- 连个维度表示的是2种状态(1st house being robbed or not); 这两种状态是平行世界的两种状态, 互不相关. + +#### Rolling array +- 与House Robber I一样, 可以用%2 来操作rolling array, space reduced to O(1) + + + +--- + +**39. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**40. [Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + +time: O(NK^2): +space: (NK) + +一排n个房子, 每个房子可涂成k种颜色, 涂每个房子的价钱不一样, 用costs[][]表示. + +costs[0][1]表示涂了index是0的房子, 用了color 1. + +规则: 相邻的两个房子不能使同一种颜色 + +求: 最少的cost + +#### DP +- 跟Paint House I 几乎一模一样, 只不过paint color更多了: k colors. +- 先考虑单纯地用dp[i]表示涂前 i 个房子的最小cost +- 但是 dp[i] 和 dp[i-1] 两个index选什么颜色会互相影响, 难讨论, 于是加状态: 序列DP被加了状态变成2D. +- 考虑最后位, 而前一位i-1又被i位的颜色限制, 于是在考虑 min dp[i] 时候, 又多了一层iteration. +- 做dp[i][j]: # cost for 前 i 个房子, 所以要先pick (i-1) 房子的cost, 然后在找出 (i-2)房子的cost +- K种颜色 => O(NK^2) +- 如果不优化, 跟Paint House I 几乎是一模一样的代码 +- Time O(NK^2), space(NK) +- Rolling array: reduce space to O(K) + +#### 注意 +- 序列型dp[i]表示'前i-1个'的结果. 所以dp最好设定为 int[n + 1] size. +- 然而, 颜色在这里是状态, 所以保留在 j: [ 0~k) +- [[8]] 这样的edge case. 跑不进for loop, 所以特殊handle. + +#### Optimization Solution +- Time: O(NK) +- 如果已知每次都要从cost里面选两个不同的最小cost,那么先把最小两个挑出来, 就不必有第三个for loop 找 min +- 每次在数列里面找: 除去自己之外的最小值, 利用最小值/次小值的思想 +- 维持2个最值: 最小值/次小值. +- 计算的时候, 如果除掉的不是最小值的index, 就给出最小值; 如果除掉的是最小值的index, 就给出次小值. +- Every loop: 1. calculate the two min vlaues for each i; 2. calcualte dp[i][j] +- 如何想到优化: 把表达式写出来, 然后看哪里可以优化 +- 另外, 还是可以rolling array, reduce space complexity to O(K) + + + +--- + +**41. [Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)** Level: Hard Tags: [Array, DP, Sequence DP] + + +比stock II 多了一个限制:只有2次卖出机会. + +#### DP加状态 +- 只卖2次, 把买卖分割成5个状态模块. +- 在状态index 0, 2, 4: 没有持有股票. 1. 一直在此状态, max profit不变; 2. 刚卖掉, dp[i][前状态] + profit +- 在状态index 1, 3: 持有股票. 1. 一直在此状态, daily profit. 2. 刚刚买进, 状态改变, 但是没有profit yet: dp[i][前状态] + +##### Partial profit +- 把每天的partial profit (diff)加在一起, 最终的overall profit是一样的. 唯一更好的是, 不需要记录中间买入的时间点. +- 什么时候会积累profit呢? +- 1. 原本就持有股票的, 如果毫无动作, 那么状态不变, 积累profit diff. +- 2. 卖出了股票, 状态改变, 积累profit diff. +- 注意: 只有在状态index: 0, 2, 4, 也就是卖掉股票的时候, 才可以积累profit + +##### Rolling Array +- [i] 只有和 [i-1] 打交道, reduce space +- O(1) space, O(n) time + +#### 找峰头 +- 找峰头;然后往下再找一个峰头。 +- 怎么样在才能Optimize两次巅峰呢?从两边同时开始找Max!(棒棒的想法) +- leftProfit是从左往右,每个i点上的最大Profit。 +- rightProfit是从i点开始到结尾,每个点上的最大profit. +- 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 +- 三个O(n),还是O(n) + + + +--- + +**42. [Best Time to Buy and Sell Stock IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20IV.java)** Level: Hard Tags: [DP, Sequence DP] + + +有int[] price of stock, 最多做 k transactions. 求最大profit. + +#### DP +- 根据StockIII, 不难发现StockIV就是把状态划分为2k+1份. 那么同样的代码, 移植. + +##### 注意1: +- 如果k很大, k>n/2, 那么长度为n的数组里面, 最多也只能n/2个transaction +- 那么题目简化为stockII, 给n数组, 无限次transaction. +- 注意, status的数量是 2k+1 +- Time O(NK), Space O(2k+1) to store the status + +##### 注意2: +- 最后状态是'没有stock'的都该考虑, 做一个 for 循环比较max. +- 当然, 来一个profit variable, 不断比较, 也是可以的. + +#### 方法2 +- (previous notes, 熟练第一种方法的思考就可以) +- 记得要理解:为什么 i-1天的卖了又买,可以和第 i 天的卖合成一次交易? +- 因为每天交易的price是定的。所以卖了又买,等于没卖!这就是可以合并的原因。要对价格敏感啊少年。 +- Inspired from here: http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html + +##### 局部最优解 vs. 全局最优解: +- local[i][j] = max(global[i – 1][j – 1] + diff, local[i – 1][j] + diff) +- global[i][j] = max(global[i – 1][j], local[i][j]) +- local[i][j]: 第i天,当天一定进行第j次交易的profit +- global[i][j]: 第i天,总共进行了j次交易的profit. + +- local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生。 +- 当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff; +- 当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。 +- (Note:在我下面这个solution里面没有省去 +diff) + +- global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况: +- 如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j]; +- 如果第i天有交易(卖出),那么global[i][j]=local[i][j]。 + + + + + +--- + +**43. [Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)** Level: Hard Tags: [Binary Search, Coordinate DP, DP] + + +俄罗斯套娃, 这里用envelope来表现. 给一串array, 每一个[x, y] 是envelope 长宽. [[5,4],[6,4],[6,7],[2,3]]. + +看用这些套娃, 可以最多套几个. + +#### DP: 1D Coordinate +- envelopes没有顺序, 先排序 (主要根据第一个index排序) +- 然后观察: 排序过后, 就变成了1D的坐标动态规划. +- max number 取决于上一个成功Russian doll的 max value + 1 +- 上一个index不知道, 所以遍历找上一个index. +- 当下index i 的状态, 取决于前面index j 的状态, 所以遍历两个index. +- O(n^2)的DP, n = envelopes.length; + +#### DP: 2D Coordinate +- 这个方法是自己想出来的, 但是时间复杂度太大, timeout +- 把envelop标记在2D grid上面, 然后像走机器人一样, 求到最右下角的最大 count max. +- count 当下能存在多少Russian doll +- 两种情况: 当下coordinate 没有target, 当下coordinate有target +- 当下coordinate 没有target: 如同机器人走法, Math.max(dp[i - 1][j], dp[i][j - 1]) +- 当下coordinate 有target: dp[i - 1][j - 1] + dp[i][j] +- timeout: O(n^2), n = largest coordinate. + + + + +--- + +**44. [Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 背包有自己的大小M, 看最多能放多少重量的书? + +#### Backpack DP 1 +- 简单直白的思考 dp[i][m]: 前i本书, 背包大小为M的时候, 最多能装多种的书? +- **注意**: 背包问题, 重量weight一定要是一维. +- dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + A[i - 1]); +- 每一步都track 最大值 +- 最后return dp[n][m] +- 时间空间 O(mn) +- Rolling array, 空间O(m) + +#### Backpack DP 2 +- true/false求解, 稍微曲线救国: 重点是, 最后, 按照weight从大到小遍历, 第一个遇到true的, index就是最大值. +- 考虑: 用i个item (可跳过地取), 是否能装到weight w? +- 需要从'可能性'的角度考虑, 不要搞成单一的最大值问题. +- 1. 背包可装的物品大小和总承重有关. +- 2. 不要去找dp[i]前i个物品的最大总重, 找的不是这个. + dp[i]及时找到可放的最大sum, 但是i+1可能有更好的值, 把dp[i+1]变得更大更合适. + +##### 做法 +- boolean[][] dp[i][j]表示: 有前i个item, 用他们可否组成size为j的背包? true/false. +- (反过来考虑了,不是想是否超过size j, 而是考虑是否能拼出exact size == j) +- **注意**: 虽然dp里面一直存在i的位置, 实际上考虑的是在i位置的时候, 看前i-1个item. + +##### 多项式规律 +- 1. picked A[i-1]: 就是A[i-1]被用过, weight j 应该减去A[i-1]. 那么dp[i][j]就取决于dp[i-1][j-A[i-1]]的结果. +- 2. did not pick A[i-1]: 那就是说, 没用过A[i-1], 那么dp[i][j]就取决于上一行d[i-1][j] +- dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]] + +##### 结尾 +- 跑一遍dp 最下面一个row. 从末尾开始找, 最末尾的一个j (能让dp[i][j] == true)的, 就是最多能装的大小 :) +- 时间,空间都是:O(mn) + + + + +--- + +**45. [Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 每本书有value int[] V + +背包有自己的大小M, 看最多能放多少value的书? + +#### Backpack DP +- 做了Backpack I, 这个就如出一辙, 只不过: dp存的不是max weight, 而是 value的最大值. +- 想法还是,选了A[i - 1] 或者没选A[i - 1]时候不同的value值. +- 时间空间O(mn) +- Rolling Array, 空间O(m) + +#### Previous DP Solution +- 如果无法达到的w, 应该mark as impossible. 一种简单做法是mark as -1 in dp. +- 如果有负数value, 就不能这样, 而是要开一个can[i][w]数组, 也就是backpack I 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**46. [Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)** Level: Medium Tags: [Backpack DP, DP] + + +#### Backpack DP +- 与背包1不同: 这里不是check可能性(OR)或者最多能装的size是多少; 而是计算有多少种正好fill的可能性. +- dp[i][w]: 用前i本书, 正好fill到 w weight的可能性. +- 对于末尾, 还是两种情况: +- 1. i-1位置没有加bag +- 2. i-1位置加了bag +- 两种情况可以fill满w的情况加起来, 就是我们要的结果. +- 如常: dp[n + 1][w + 1] +- 重点: dp[0][0] 表示0本书装满weight=0的包, 这里我们必须 dp[0][0] = 1, 给后面的 dp function 做base +- Space, time: O(MN) +- Rolling array, 空间优化, 滚动数组. Space: O(M) + +#### 降维打击, 终极优化 +- 分析row(i-1)的规律, 发现所有row(i)的值, 都跟row(i-1)的左边element相关, 而右边element是没用的. +- 所以可以被override. +- Space: O(M), 真*一维啊! +- Time: O(MN) + + + +--- + +**47. [Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)** Level: Hard Tags: [DP, Enumeration, Partition DP] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +其中字符可能是 "*", 可以代表 [1 - 9] + +#### DP +- 乘法原理 +- 跟decode way I 一样, 加法原理, 切割点时: 当下是取了 1 digit 还是 2 digits 来decode +- 定义dp[i] = 前i个digits最多有多少种decode的方法. new dp[n + 1]. +- 不同的情况是: 每一个partition里面, 如果有"*", 就会在自身延伸出很多不同的可能 +- 那么: dp[i] = dp[i - 1] * (#variations of ss[i]) + dp[i - 2] * (#variations of ss[i,i+1]) + +##### 特点 +- 枚举的能力: 具体分析 '*' 出现的位置, 枚举出数字, 基本功. +- 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) +- 理解取MOD的原因: 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. +- 枚举好以后, 其实这个题目的写法和思考过程都不难 + + + + +--- + +**48. [Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)** Level: Hard Tags: [DP, Partition DP] + + +给一个String s, 找出最少用多少cut, 使致 切割出的每一个substring, 都是palindrome + +#### Partition DP +- Find minimum cut: 分割型DP +- dp[i]: 最少cut多少刀, 使得前 i 长度的string, 割出来都是palindrome +- 最终要得到 dp[n], 所以 int[n + 1] +- 移动切刀, 看在哪里切, index j in [0 ~ i] +- 考虑[j, i - 1] 是否是回文串, 如果是, 那么: dp[i]= min(dp[i], d[j] + 1). +- note: 估计遍历 j 的时候, 反过来遍历也可以. + +#### 计算Palindrome的优化 +- 利用palindrome的性质, 可以算出 boolean palindrome[i, j]的情况. +- 找一个任意mid point: +- 1. 假设palindrome是奇数长度, 那么 mid 是单独的字符, 而两边的字符 [mid-1], [mid+1] 应该完全相等. +- 2. 假设palindrome是偶数长度, 那么 [mid] 和 [mid + 1] 这样位置的字符应该相等. +- 这样做出来 palindrome[i, j]: 从字符 i 到 字符 j 的 substring 是否是 palindrome +- 这样就给我们的问题合理降维, 目前是time: O(n^2). +- 不然求一次palindrome, 就是n, 会变成O(n^3) + +#### Previous Notes +- Double for loop 检查每种substring string (i~j). 若i,j相邻或者同点,那么肯定isPal;否则,i,j之间的(i+1, j-1)一定得isPal。 +- 看上去,在检查i,j的时候,中间按的(i+1, j-1)怎么可能先知道? 其实不然..在j慢慢长大的时候,所有的0~j的substring都检查过。所以isPal[i+1][j-1]一定是已经知道结果的。 +- okay.那么假如以上任意一种情况成立,也就是说isPal[i][j] == true。那就要判断,切到第一层循环参数j的末尾点时,有多少种切法? +- 想法很顺:我们naturally会想到,把i之前的cut加上i~j之间发生的不就好了。 +- 反正现在j不变,现在就看吧i定在哪里,cut[i - 1]是否更小/最小; 再在cut[i-1]基础上+1就完了。 +- 当然,如果i==0, 而 i~j又是isPal,那没啥好谈的,不必切,0刀。 +- 最终,刷到cut[s.length() - 1] 也就是最后一点。 return的理所应当。 + + + + +--- + +**49. [Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)** Level: Hard Tags: [Backpack DP, DP] + + +给n种不同的物品, int[] A weight, int[] V value, 每种物品可以用无限次 + +问最大多少value可以装进size是 m 的包? + +#### DP +- 可以无限使用物品, 就失去了last i, last unique item的意义: 因为可以重复使用. +- 所以可以转换一个角度: +- 1. 用i **种** 物品, 拼出w, 并且满足题目条件(max value). 这里因为item i可以无限次使用, 所以考虑使用了多少次K. +- 2. K虽然可以无限, 但是也被 k*A[i]所限制: 最大不能超过背包大小. +- dp[i][w]: 前i种物品, fill weight w 的背包, 最大价值是多少. +- dp[i][w] = max {dp[i - 1][w - k*A[i-1]] + kV[i-1]}, k >= 0 +- Time O(nmk) +- 如果k = 0 或者 1, 其实就是 Backpack II: 拿或者不拿 + +#### 优化 +- 优化时间复杂度, 画图发现: +- 所计算的 (dp[i - 1][j - k*A[i - 1]] + k * V[i - 1]) +- 其实跟同一行的 dp[i][j-A[i-1]] 那个格子, 就多出了 V[i-1] +- 所以没必要每次都 loop over k times +- 简化: dp[i][j] 其中一个可能就是: dp[i][j - A[i - 1]] + V[i - 1] +- Time O(mn) + +#### 空间优化到1维数组 +- 根据上一个优化的情况, 画出 2 rows 网格 +- 发现 dp[i][j] 取决于: 1. dp[i - 1][j], 2. dp[i][j - A[i - 1]] +- 其中: dp[i - 1][j] 是上一轮 (i-1) 的结算结果, 一定是已经算好, ready to be used 的 +- 然而, 当我们 i++,j++ 之后, 在之前 row = i - 1, col < j的格子, 全部不需要. +- 降维简化: 只需要留着 weigth 这个 dimension, 而i这个dimension 可以省略: +- (i - 1) row 不过是需要用到之前算出的旧value: 每一轮, j = [0 ~ m], 那么dp[j]本身就有记录旧值的功能. +- 变成1个一位数组 +- 降维优化的重点: 看双行的左右计算方向 +- Time(mn). Space(m) + + + +--- + +**50. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**51. [Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + +O(n)跑2遍for. +O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. +特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 + + + +--- + +**52. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**53. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**54. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**55. [Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)** Level: Easy Tags: [DP, Math, Memoization] + + +#### Memoization +- fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + +#### DP array. +- 滚动数组, 简化DP + +#### recursively calculate +- recursively calculate fib(n - 1) + fib(n - 2). 公式没问题, 但是时间太长, timeout. + + + + +--- + +**56. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + +**57. [Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)** Level: Medium Tags: [Array, Backpack DP, DP] + + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + +#### Backpack DP +- 计数问题, 可以想到DP. 其实就是Backpack VI. +- 从x个数字里面找candidate(可以重复用同一个数字), 来sum up to target. 找: # of ways to form the sequence. +- Backpack VI: 给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法 +- dp[i]: # of ways to build up to target i +- consider last step: 如果上一步取的是 candidate A, 那么就该加到dp[i]: +- dp[i] += dp[i - A] +- 要找overall dp[i], 就做一个for loop: dp[i] = sum{dp[i - num]}, where for (num: nums) +- Time: O(mn). m = size of nums, n = target +- If we optimize dp for loop, 需要Sort nums. O(mlogm). will efficient 如果m是constant或者relatively small. Overall: O(n) + +#### DFS, backtracking +- 尽管思考方式是对的, 但是 times out +- 可以重复使用数字的时候, 比如用1 来拼出 999, 这里用1就可以走999 dfs level, 不efficient + + + +--- + +**58. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**59. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**60. [Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)** Level: Medium Tags: [Backtracking, DFS, DP] + + +String 只包含 + , - 两个符号. 两个人轮流把consecutive连续的`++`, 翻转成 `--`. + +如果其中一个人再无法翻转了, 另一个人就赢. 求: 给出string, 先手是否能赢. + +#### Backtracking +- curr player 每走一步, 就生成一种新的局面, dfs on this +- 等到dfs结束, 不论成功与否, 都要backtracking +- curr level: 把"++" 改成 "--"; backtrack的时候, 改回 '--' +- 换成boolean[] 比 string/stringBuilder要快很多, 因为不需要重新生成string. +- ++ 可以走 (n - 1)个位置: +- T(N) = (N - 2) * T(N - 2) = (N - 4) * (N - 2) * T(N - 4) ... = O(N!) + +##### iterate based on "++" +- 做一个String s的 replica: string or stringBuilder +- 每次dfs后, 然后更替里面的字符 "+" => "-" +- 目的只是Mark已经用过的index +- 真正的dfs 还是在 original input string s 身上展开 +- 每次都重新生成substring, 并不是很efficient + +##### Game theory +- 保证p1能胜利,就必须保持所有p2的move都不能赢 +- 或者说, 在知道棋的所有情况时, 只要p2有一种路子会输, p1就一定能走对路能赢. +- 同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +- p1: player1, p2: player2 + +#### O(N^2) 的 DP +- 需要Game Theory的功底, Nim game. https://www.jiuzhang.com/qa/941/ +- http://www.1point3acres.com/bbs/thread-137953-1-1.html +- TODO: https://leetcode.com/problems/flip-game-ii/discuss/73954/Theory-matters-from-Backtracking(128ms)-to-DP-(0ms) + + + +--- + +**61. [Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Substring.java)** Level: Medium Tags: [DP, String] + + +给一个string, 找到最长的palindrome substring. + +Related: Longest Palindromic Subsequence, Palindrome Partioning II + +O(n^2) is not too hard to think of. How about O(n)? + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + +#### DP: isPalin[][] +- 穷举double for loop. O(n^2) +- boolean isPalin[i][j], 每次确认有palindrome就记录下来true / false +- 穷举的for loop计算顺序: end point j, and stat point i = [0, j] +- 在计算 isPalin[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- double for loop: O(n^2). slower, because it guarantees O(n^2) due to the for loop + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**62. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + +**63. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**64. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**65. [Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- 就是pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**66. [Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)** Level: Review Tags: [BFS, DFS, DP] + + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- in dfs: remove the incorrect parentheses one at a time +- detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` +- once detected, remove the char from middle of s, and dfs on the rest of the s that has not been tested yet. + +##### Core concept: reverse test +- `if a parenthese string is valid, the reverse of it should also be valid` +- Test s with open='(', close=')' first; **reverse s**, and test it with open=')', close='(' + +##### Minor details +- only procceed to remove invalid parenthese when `count<0`, and also break && return dfs after the recursive calls. +- The above 2 facts eliminates all the redundant results. +- Reverse string before alternating open and close parentheses, so when returning final result, it will return the correct order. +- Open questions: how does it guarantee minimum removals? + +##### Backtracking +- 如果用stringbuffer, 那么久不会每次create new string, 但是需要maintain这个string buffer, 就会backtracking + +##### Complexity +- Seems to be O(n), but need to derive + +#### BFS +TODO + +#### DP + + + +--- + +**67. [Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)** Level: Medium Tags: [DP, Math] + + +具体看题目: count # of valid rectangles (four corner are 1) in a grid[][]. + +#### basic thinking + Math +- Fix two rows and count matching columns +- Calculate number rectangles with `combination` concept: +- total number of combinations of pick 2 points randomly: count * (count - 1) / 2 + +#### DP +- TODO. HOW? + +#### Brutle +- O(m^2 * n^2), times out + + + +--- + +**68. [Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)** Level: Medium Tags: [DP, String] + + +根据题意, count # of palindromic substring. (不同index截取出来的substring算不同的情况) + +#### isPalin[][] +- build boolean[][] to check isPalin[i][j] with DP concept +- check all candidates isPalin[][] +- O(n^2) + +#### odd/even split check +https://leetcode.com/problems/palindromic-substrings/discuss/105689/Java-solution-8-lines-extendPalindrome + + + +--- + +**69. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) + +#### DP +- 最多2个fence 颜色相同 +- 假设i是和 i-1不同,那么结果就是 (k-1)*dp[i - 1] +- 假设i是何 i-1相同,那么根据条件,i-1和i-2肯定不同。那么所有的结果就是(k-1)*dp[i-2] +- dp[i]: count # of ways to paint 前i个 fence +- 加法原理 +- time, space: O(n) +- rolling array: space O(1) + +#### Previous Notes +- 这题目很有意思. 一开始分析的太复杂, 最后按照这个哥们的想法(http://yuanhsh.iteye.com/blog/2219891) 的来做,反而简单了许多。 +- 设定T(n)的做法,最后题目化简以后就跟Fibonacci number一样一样的。详细分析如下。 +- 做完,还是觉得如有神。本来是个Easy题,想不到,就是搞不出。 + + + + +--- + +**70. [Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- true/false problem, think about dp +- 子问题: 前i个字母, 是否可以有valid break +- 检查dp[j] && `if substring(j, i) valid`, for all j = [0 ~ i] +- dp = new boolean[n + 1]; dp[0] = true; +- goal: if there is a j, `dp[j] == true && word[j, n] in dict`. Need iterate over i = [0 ~ n], also j = [0, i] +- 注意, 用set代替list, 因为要用 contains(). + +#### Previous notes +##### 方法2(attempt4 code) +- 与Word BreakII用同样的DP。 +- valid[i]: 记录从i到valid array末尾是否valid. + +##### 方法1:(attempt3 code) +- state,rst[i]: 从[0~i] inclusive的string是否可以在dict中break开来找到? +- function: rst[i] = true if (rst[i - j] && set.contains(s.substring(i - j, i))); j in[0~i] +- 1. rst[i - j] 记录的是[0, i-j]这一段是否可以break后在dict找到。 +- 2. 若true,再加上剩下所有[i-j, i]都能在dict找到,那么rst[i] = rst[0, i - j] && rst[i-j, i] == true +- 优化:找dict里面最长string, 限制j的增大。 + + + + +--- + +**71. [Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### 理解意思是关键 +- 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出 +- 记录每天最小值Min是多少. O(n) +- 每天都算和当下的Min买卖,profit最大多少. + +#### DP +- Find min value for first i items, new dp[n + 1]. +- dp[i]: 前i天, prices最小的price是多少: min cost of first i days +- 然后用当天的price做减法dp[i]算max profit. +- Time, Space: O(n) +- 更进一步, 用一个min来表示min[i], 因为计算中只需要当下的min. + +#### Rolling array +- index i only depend on [i - 2] +- Space O(n) + +#### Brutle Failed +- 每天都试着买进,然后之后的每一天尝试卖出. double for loop, O(n^2). timeout. +- 其中很多都是没必要的计算:[7, 1, 5, 3, 6, 4]。 if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. + + + +--- + +**72. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**73. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**74. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**75. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + + +// 如何想到从中间initialize + + + +--- + +**76. [Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Coordinate DP, DP] + +time: O(n^2) +time: O(n) + +给一串 unsorted sequence, 找到长 increasing subsequence 的个数! + +#### Coordinate DP +- 需要能够判断综合题, 分清楚情况和套路: combination of `longest subsequence` and `ways to do`, as well as global variable. +- len[i] (我们平时的dp[i]): 在前i个元素中, 最长的 increasing subsequence length; +- count[i]: 在前i个元素中, 并且以 len[i]这个长度为准的 subsequence的 count. 或者: 在前i个元素中, ways to reach longest increasing subsequence. +- `len[i] == len[j] + 1`: same length, but different sequence, so add all `count[i] += count[j]` +- `len[i] < len[j] + 1`: 这就是更长的情况找到了, 那么有多少次 count[j] 有多少, count[i] 就有多少. 仔细想sequence: 长度增长了, 但是ways to reach i 没有增长. +- 同样的判断需要用在 maxLen 和 maxFreq上: +- 如果没有增长 maxLen 不变, maxFreq上面需要 +=count[i] (同一种长度, 多了更多的做法) +- 如果maxLen 变长, maxFreq 也就是采用了 count[i] = count[j] +- TODO: Is rolling array possible? + +#### 相关 +- 都是 Coordiate DP, DP的鼻祖家族: +- Longest Increasing Subsequence (跟这道题的一部分一模一样) +- Longest Continuous Increasing Subsequence (连续, 只check dp[i - 1]) +- Longest Increasing Continuous Subsequence I, II (Lintcode, II 是matrix) + + + +--- + +**77. [Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)** Level: Medium Tags: [Coordinate DP, DP, Status DP] + + + +#### DP +- 特点: 上一步可能是swaped也可能是fixed +- 考虑A,B之间的现状: `A[i] > A[i - 1] && B[i] > B[i - 1]` 或者 `A[i] > B[i - 1] && B[i] > A[i - 1]` +- 问题: 如何把这个状态变成合理的strick-increasing状态? +- `A[i] > A[i - 1] && B[i] > B[i - 1]`: 1. 已经合理, 也不动. 2. [i], [i-1] 全部都swap +- `A[i] > B[i - 1] && B[i] > A[i - 1]`, 交错开来, 所以调换[i], 或者[i-1]: 1. 换[i-1]. 2. 换[i] +- 注意因为求min, 所以init value应该是 Integer.MAX_VALUE; + + + +--- + +**78. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**79. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**80. [Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)** Level: Hard Tags: [DP, Hash Table] + + +Frog jump 的题目稍微需要理解: 每个格子可以 jump k-1, k, k+1 steps, 而k取决于上一步所跳的步数. 默认 0->1 一定是跳了1步. + +注意: int[] stones 里面是stone所在的unit (不是可以跳的步数, 不要理解错). + +#### DP +- 原本想按照corrdiante dp 来做, 但是发现很多问题, 需要track 不同的 possible previous starting spot. +- 根据jiuzhang答案: 按照定义, 用一个 map of > +- 每次在处理一个stone的时候, 都根据他自己的 set of , 来走下三步: k-1, k, or k+1 steps. +- 每次走一步, 查看 stone + step 是否存在; 如果存在, 就加进 next position: `stone+step`的 hash set 里面 + +##### 注意init +- `dp.put(stone, new HashSet<>())` mark 每个stone的存在 +- `dp.get(0).add(0)` init condition, 用来做 dp.put(1, 1) + +##### 思想 +- 最终做下来思考模式, 更像是BFS的模式: starting from (0,0), add all possible ways +- 然后again, try next stone with all possible future ways ... etc + + + +--- + +**81. [Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)** Level: Medium Tags: [DP, MiniMax] + + +Detailed in `Coins in a Line III` + + + +--- + +**82. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**83. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + +**84. [Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)** Level: Hard Tags: [DP] + + + + +--- + + + + + + + +## Double Sequence DP (6) +**0. [Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP] + + +给两个string, A, B. 找这两个string里面的LCS: 最长公共字符长度 (不需要是continuous substring) + +#### Double Sequence DP +- 设定dp长度为(n+1), 因为dp[i]要用来表示前i个(ith)时候的状态, 所以长度需要时i+1才可以在i位置, hold住i. +- 双序列: 两个sequence之间的关系, 都是从末尾字符看起, 分析2种情况: +- 1. A最后字符不在common sequence 或者 B最后字符不在common sequence. +- 2. A/B最后字符都在common sequence. 总体count + 1. + + + +--- + +**1. [Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + +time: O(MN) +Space: O(N) + +两个字符串, A要变成B, 可以 insert/delete/replace, 找最小变化operation count + +#### Double Sequence +- 考虑两个字符串的末尾index s[i], t[j]: 如果需要让这两个字符一样, 可能使用题目给出的三种operation: insert/delete/replace? +- 先calculate最坏的情况, 3种operation count + 1; 然后在比较match的情况. +- 注意, 在i或者j为0的时候, 变成另外一个数字的steps只能是全变. +- 第一步, 空间时间都是O(MN), O(MN) +- 滚动数组优化, 空间O(N) + +##### Detail analysis +- insert: assume insert on s, `#ofOperation = (s[0 ~ i] to t[0 ~ j-1]) + 1;` +- delete: assume delete on t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j]) + 1;` +- replace: replace both s and t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j - 1]) + 1;` +- dp[i][j]代表了两个 sequence 互相之间的性质: s[0 ~ i] 转换成 s[0~j] 所需要的最少 operation count +- init: 当i==0, dp[0][j] = j; 每次都要 + j 个character; 同理, 当j==0, dp[i][0] = i; +- 而dp[i][j]有两种情况处理: `s[i] == t[j]` or `s[i] != t[j]` + +##### 何时initialize +- 这种判断取决于经验: 如果知道initialization可以再 double for loop 里面一起做, 那么可以留着那么做 +- 这样属于 `需要什么, initialize什么` +- 事后在做space optimization的时候, 可以轻易在 1st dimension 上做rolling array + +#### Search +- 可以做, 但是不建议:这道题需要找 min count, 而不是search/find all solutions, 所以search会写的比较复杂, 牛刀杀鸡. + + + +--- + +**2. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**3. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**4. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**5. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + + + + + + + +## BFS (39) +**0. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**1. [Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)** Level: Medium Tags: [BFS, Graph] + + +#### Graph + BFS +- Build graph `map` +- BFS to find the shortest path: when the neibhbor has the curr node as the only one neighbor, it is leaf. +- record shortest path in Map> as result +- TODO: code it up. + +#### Previous Solution +- removing leaf && edge + + + +--- + +**2. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**3. [Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS] + + +给一串integers, list里面可能有nest list. 算总的sum. 规则, 如果是nested list, 每深一个depth, sum要乘以depth. + +#### DFS +- New interface to understand: object contains integer or object +- Visit all && sum, consider dfs. +- bottom->up is easier: pick nested object and execute dfs, which returns sum of it, add with (level value * weight). +- 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) +- Note1: not multiplying on overall level sum. Only multiply level with single value at this level. +- Note2:top->bottom is not necessary: there is not need of passing added object into next level. + +#### BFS +- bfs, queue, 处理queue.size(). +- use a level variable to track levels + + + +--- + +**4. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**5. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + +**6. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**7. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**8. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**9. [Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +A complete binary tree is a binary tree in which every level, except possibly the last, + +is completely filled, and all nodes are as far left as possible + +#### BFS +- 当出现了第一次有 null children的node的时候, 说明到了leaf level, mark flag = true; +- 自此以后,queue再不该有node再有child; queue后面出现的node的left/right child应该都是null +- 否则就是有问题, return false; + + + + +--- + +**10. [Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### DFS +- 简单处理swap +- recursively swap children + +#### BFS +- BFS with Queue +- 每次process一个node, swap children; 然后把child加进queue里面 +- 直到queue process完 + + + +--- + +**11. [Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### BFS +- Shortest path; minimum depth: 想到BFS, check level by level, BFS更能确保更快找到结果 +- depth definition: reach to a leaf node, where node.left == null && node.right == null +- BFS using queue, track level. + + +#### DFS +- Divide and Conquery一个最小值. +- 注意处理Leaf的null: null leaf 出现的时候, 就忽略这个leaf, 直接return算有leaf +- 另一种count的方法: 用Integer.MAX_VALUE代替 null leaf,这样可以避免错误counting. (不能直接recursive) +- 这个无论如何都要走所有node, 所以dfs应该比较适合. + + + + +--- + +**12. [Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**13. [Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**14. [Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)** Level: Medium Tags: [BFS, Tree] + + +如题, 但是output要倒序. + +#### BFS +- 跟Binary Tree Level Order Traversal一样,只不过存result一直存在存在0位. + + +#### DFS +- 根据level来append每个list +- rst里面add(0,...)每次都add在list开头 + + + +--- + +**15. [Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右:即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list + +#### DFS +- Use Map 来存每一个level的结果 +- dfs function 里, 如果 input depth 不存在, 就add to map. +- dfs function 里面先: dfs(node.right), 然后 dfs(node.left) +- 由于always depth search on right side, 所以map会被right branch populate; 然后才是 leftChild.right + + + + +--- + +**16. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**17. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**18. [Word Ladder.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder.java)** Level: Medium Tags: [BFS] + + +给一串string[], 需要找shortest distance to change from wordA -> wordB. (限制条件细节见原题) + +#### BFS +- 通常, 给一个graph(这道题可以把beginWord看成一个graph的起始node), 找shortest path用BFS +- 在start string基础上,string的每个字母都遍历所有26个字母 +- visited 过的 从wordList里去掉 +- time: word length m, there can be n candidates => O(mn) +- 但是总是exceed time limit on LeetCode. However, it passes LintCode: +- 原因是 LeetCode给的是list, list.contains(), list.remove() 都是 O(logn) time!!! +- convert to set first. + +#### Trie +- timeout, overkill + + + +--- + +**19. [Find the Connected Component in the Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Connected%20Component%20in%20the%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS] + + +给一个undirected graph, return 所有的component. (这道题找不到了) + +#### BFS +- BFS遍历,把每个node的neighbor都加进来. +- 一定注意要把visit过的node Mark一下。因为curr node也会是别人的neighbor,会无限循环。 +- Component的定义:所有Component内的node必须被串联起来via path (反正这里是undirected, 只要链接上就好) +- 这道题:其实component在input里面都已经给好了,所有能一口气visit到的,全部加进queue里面,他们就是一个component里面的了。 +- 而我们这里不需要判断他们是不是Component + +#### DFS +- DFS 应该也可以 visit all nodes, mark visited. + + + +--- + +**20. [Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Topological Sort] + + +#### Topological Sort BFS +- indegree tracking: Track all neighbors/childrens. 把所有的children都存在 inDegree里面 +- Process with a queue: 先把所有的root加一遍(indegree == 0),可能多个root。并且全部加到queue里面。 +- BFS with Queue: +- Only when map.get(label) == 0, add into queue && rst. (indegree剪完了, 就是root啦) +- inDegree在这里就 count down indegree, 确保在后面出现的node, 一定最后process. + + +#### Basics about graph +- 几个graph的condition: +- 1. 可能有多个root +- 2. directed node, 可以direct backwards. + +TODO: +- build`Map inDegree = new HashMap<>();` and include the root itself +- that is more traditional indegree building + + + +--- + +**21. [Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)** Level: Review Tags: [BFS, DFS, DP] + + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- in dfs: remove the incorrect parentheses one at a time +- detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` +- once detected, remove the char from middle of s, and dfs on the rest of the s that has not been tested yet. + +##### Core concept: reverse test +- `if a parenthese string is valid, the reverse of it should also be valid` +- Test s with open='(', close=')' first; **reverse s**, and test it with open=')', close='(' + +##### Minor details +- only procceed to remove invalid parenthese when `count<0`, and also break && return dfs after the recursive calls. +- The above 2 facts eliminates all the redundant results. +- Reverse string before alternating open and close parentheses, so when returning final result, it will return the correct order. +- Open questions: how does it guarantee minimum removals? + +##### Backtracking +- 如果用stringbuffer, 那么久不会每次create new string, 但是需要maintain这个string buffer, 就会backtracking + +##### Complexity +- Seems to be O(n), but need to derive + +#### BFS +TODO + +#### DP + + + +--- + +**22. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**23. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**24. [Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + +time: O(n) +space: O(n) + +#### Queue +- 简单的level traversal.根据level奇数偶数而add到不同位子. +- Option1: based on level % 2, insert to front/end of list +- Option2: based on level, insert right/left of node into queue + + + +--- + +**25. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**26. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**27. [Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)** Level: Medium Tags: [BFS, DFS] + + +给一个room 2D grid. 里面有墙-1, 门0, 还有empty space INF(Math.MAX_VALUE). + +对每个empty space而言, fill it with dist to nearest gate. + +#### DFS +- Form empty room: it can reach different gate, but each shortest length will be determined by the 4 directions. +- Option1(NOT applicable). DFS on INF, mark visited, summerize results of 4 directions. +- hard to resue: we do not know the direction in cached result dist[i][j] +- Option2. DFS on gate, and each step taken to each direction will +1 on the spot: distance from one '0'; +- Through dfs from all zeros, update each spot with shorter dist +- Worst time: O(mn), where entre rooms[][] are gates. It takes O(mn) to complete the iteration. Other gates be skipped by `if (rooms[x][y] <= dist) return;` + +#### BFS +- Exact same concept. Init with `Queue queue = new LinkedList()` + + + +--- + +**28. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**29. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**30. [Shortest Distance from All Buildings.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Distance%20from%20All%20Buildings.java)** Level: Hard Tags: [BFS] + + +给Walls and Gates很像, 不同的是, 这道题要选一个 coordinate, having shortest sum distance to all buildings (marked as 1). + +#### BFS +- BFS 可以 mark shortest distance from bulding -> any possible spot. +- Try each building (marked as 1) -> BFS cover all 0. +- time: O(n^2) * # of building; use new visited[][] to mark visited for each building. +- O(n^2) find smallest point/aggregation value. +- 注意, 这道题我们update grid[][] sum up with shortest path value from building. +- 最后找个min value 就好了, 甚至不用return coordinate. +- 分析过, 还没有写. + + + +--- + +**31. [Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph] + + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes. +- 先能复制多少Node复制多少. 然后把neighbor 加上 +- Use `map` to mark visited + +#### DFS +- Given graph node obj `{val, list of neighbor}`: copy the node and all neighbors +- Mark visited using map +- for loop on the each one of the neighbors: map copy, record in map, and further dfs +- once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +- 主要思想是: 一旦复制过了, 不必要重新复制 + +#### BFS +- Copy the root node, then copy all the neighbors. +- Mark copied node in map. +- Use queue to contain the newly added neighbors. Need to work on them in the future. + + + +--- + +**32. [Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)** Level: Hard Tags: [BFS] + + + + +--- + +**33. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + + +--- + +**34. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**35. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**36. [The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)** Level: Medium Tags: [BFS, DFS] + + +#### BFS +- BFS on coordinates +- always attempt to move to end of border +- use boolean[][] visited to alingn with BFS solution in Maze II, III, where it uses Node[][] to store state on each item. + + + +--- + +**37. [The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)** Level: Medium Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- if already found a good/shorter route, skip +- `if (distMap[node.x][node.y] <= node.dist) continue;` +- This always terminates the possibility to go return to original route, because the dist will be double/higher + + + +--- + +**38. [The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)** Level: Hard Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- 跟 Maze I, II 类似, 用一个 Node[][] 来存每一个(x,y)的state. +- Different from traditional BFS(shortest path): `it terminates BFS when good solution exists (distance), but will finish all possible routes` +- 1. `Termination condition`: if we already have a good/better solution on nodeMap[x][y], no need to add a new one +- 2. Always cache the node if passed the test in step1 +- 3. Always offer the moved position as a new node to queue (as long as it fits condition) +- 4. Finally the item at nodeMap[target.x][target.y] will have the best solution. + + + +--- + + + + + + + +## Segment Tree (12) +**0. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**1. [Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**2. [Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 + + + +--- + +**3. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**4. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**5. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**6. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**7. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**8. [Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的sum. + +#### Segment Tree + Binary Search +- 其实是segment tree 每个node上面加个sum +- 记得Segment Tree methods: Build, Query +- Note: 存在SegmentTreeNode里面的是sum. 其他题目可能是min,max,count ... or something else. + + + +--- + +**9. [Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)** Level: Hard Tags: [Binary Search, Lint, Segment Tree] + + +SegmentTree大集合. Methods: `build, query, modify`. 不难。只是要都记得不犯错. + +#### Segment Tree +- build: recursively build children based on index-mid and link to curr node +- query: recursively try to find `node.start == targetStart && node.end == targetEnd`. Compare with node.mid +- modify: recursively try to find `node.start == targetStart && node.end == targetEnd`; modify and recursively assign upper interval with updated interval property. + + + +--- + +**10. [Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字, array size = n. 给一串query: 每个query是一个数, 目的找 count# items smaller than query element. + +#### Segment Tree +- 和平时的segment tree问题不同。 [0 ~ n] 代表实际数字: based on real value的segment tree. +- Modify时,把array里面的value带进去,找到特定的位子, 然后count + 1. +- 最终在SegmentTree leaf上面全是array里面实际的数字。 +- node.count: 在node range里面的有多少个数字 + +##### right use of modify() +- build() 只是 empty segment tree, 没有property +- modify() 需要: 1. 找到left, update count+=1; 2. aggregate all parent when after returning +- 所以每一个modify 都是在整个path上所有的node上 + count + +##### query trick +- 在query前,给进去的start和end是: 0 ~ value-1. +- `value-1`: 找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. + +##### About other basic segment tree setup +- [那么其他做过的SegmentTree是怎么样呢?] +- 那些构成好的SegmentTree(找min,max,sum)也有一个Array。但是构成Tree时候,随Array的index而构架。 +- 也就是说,假如有Array[x,y,....]:在leaf,会有[0,0] with value = x. [1,1] with value = y. +- [但是这题] +- 构成时,是用actual value.也就是比如Array[x,y,....]会产生leaf:[x,x]with value = ..; [y,y]with value =... +- 其实很容易看穿: +- 若给出一个固定的array构成 SegmentTree,那估计很简单:按照index从0~array.lengh,leaf上就是[0,0] with value = x. +- 若题目让构造一个空心SegmentTree, `based on value 0 ~ n-1 (n <= 10000)`, 然后把一个Array的value modify 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**11. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + + + + + + + +## DFS (95) +**0. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**1. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**2. [Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS] + + +给一串integers, list里面可能有nest list. 算总的sum. 规则, 如果是nested list, 每深一个depth, sum要乘以depth. + +#### DFS +- New interface to understand: object contains integer or object +- Visit all && sum, consider dfs. +- bottom->up is easier: pick nested object and execute dfs, which returns sum of it, add with (level value * weight). +- 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) +- Note1: not multiplying on overall level sum. Only multiply level with single value at this level. +- Note2:top->bottom is not necessary: there is not need of passing added object into next level. + +#### BFS +- bfs, queue, 处理queue.size(). +- use a level variable to track levels + + + +--- + +**3. [Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Same%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### BFS +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**4. [Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +如题, build balanced BST from sorted array + +#### DFS +- Binary Search Tree特点: 左边的node都比右边的node小. +- height balance, subtree height 相差<1, 必须左右sub tree均分. 做DFS(num, start, end) +- 在每一个level, 找到中间点, 然后分割2半, 继续dfs +- Divide and Conquer +- time/space: O(n), visit all nodes, no redundant visits. + + + +--- + +**5. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**6. [Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Balanced%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个binary tree, 看是否是height-balanced + +#### DFS +- DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. +- 一旦有 <0 或者差值大于1, 就全部返回Integer.MIN_VALUE. Integer.MIN_VALUE比较极端, 确保结果的正确性。 +- 最后比较返回结果是不是<0. 是<0,那就false. +- Traverse 整个tree, O(n) + + +#### DFS, maxDepth function +- Same concept as in 1, but cost more traversal efforts. + + + +--- + +**7. [Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +给一个特殊的binary tree, treeNode里面有一个 next pointer. + +写一个function, 把所有node都更同level的node 连在一起. 最右边的node.next = NULL + +#### DFS + Divide and Conquer +- 题目要求DFS. 想清楚了如何在DFS level把几种情况都考虑了, 写起来很简单. NOT BFS, because requires O(1) space +- 对于一个root来说, 只有几个点可以顾忌到: root.left, root.right, root.next. +- 想办法把这三个方向的点, 能连起来的都连起来: +- 1. `node.left.next = node.right` +- 2. If `node.next != null`, link `node.right.next = node.next.left`; +- 然后在dfs(root.left), dfs(root.right) +- Time: visit && connect all nodes, O(n) + +#### BFS +- 不和题意,用了queue space,与Input成正比。太大。 +- BFS over Tree。 用Queue 和 queue.size(),老规矩。 +- process每层queue时, 注意把next pointer加上去就好. + + + +--- + +**8. [Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +如题, 验证是否是BST. + +#### DFS +- 查看每个parent-child关系: leftchild < root < rightChild; +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest +- imagine we know the two extreme border: Integer.MIN_VALUE, Integer.MAX_VALUE; pass node around and compare node vs. node.parent. +- 方法: 把root.val 传下来作为 max 或者 min, 然后检查children +- + +##### Note: +- min/max需要时long type. +- 如果题目真的给node.val = Integer.MAX_VALUE, 我们需要能够与之比较, long就可以. + + + +--- + +**9. [Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List] + + +如题, 把一个sorted singly linked list 转换成一个 height balanced BST + +#### DFS +- Divide and Conquer +- 找到mid node +- 然后分割两半, 分别dfs做各自两个subtree: node.left,node.right +- 用长度来定位mid, 每次找中间点做root, 然后前半段, 后半段分别dfs with length. +- 用快慢pointer 找到mid. Better: 不用traverse entire linked list + +#### Details +- slowPointer = node; +- fastPointer = node.next; +- 然后把root = mid.next +- 然后开始sortedListToBST(mid.next.next); //后半段 +- mid.next = null;//非常重要,要把后面拍过序的断掉 +- sortedListToBST(head); //从头开始的前半段 +- 最后root.left, root.right merge一下。 + + + +--- + +**10. [Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### DFS +- 分析题意后, 按照题意: Flatten the tree, no extra space. +- 1. reserve right child: `reservedRightNode` +- 2. Connect `root.right = root.left`, DFS flatten(root.right) +- 3. 移花接木, coneect end of list -> reservedRightNode +- 4. flatten 剩下的. root.right... + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**11. [Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)** Level: Easy Tags: [Backtracking, Binary Tree, DFS] + + +给一个binary tree, 返回所有root-to-leaf path + +#### DFS, backtracking +- Find all paths, bfs/dfs all works. dfs will be simplier to write +- Recursive:分叉. dfs. +- top->bottom: enumerate current node into the list, carry to next level, and backtrack +- top->bottom is trivial to consider: path flows from top->bottom + +#### DFS, bottom->up +- We can also take current node.left or node.right to generate list of results from the subproblem +- let dfs return list of string candidates, and we can run pair the list with currenet node, once they come back. +- TODO: can write code to practice + +#### Iterative +- Iterative, 非递归练习了一下 +- 因为要每次切短list, 所以再加了一个Stack 来存level +- 单这道题用dfs更简单, 因为找的就是从头到尾的path, 是dfs的pattern + + + + +--- + +**12. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**13. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + +**14. [Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)** Level: Hard Tags: [Backtracking, DFS, Trie] + + +给一串words, 还有一个2D character matrix. 找到所有可以形成的words. 条件: 2D matrix 只可以相邻走位. + +#### Trie, DFS +- 相比之前的implementation, 有一些地方可以优化: +- 1. Backtracking时候, 在board[][] 上面mark就可以, 不需要开一个visited[][] +- 2. 不需要implement trie的所有方程, 用不到: 这里只需要insert. +- 普通的trie题目会让你search a word, 但是这里是用一个board, 看board的每一个字母能不能走出个Word. +- 也就是: 这里的search是自己手动写, 不是传统的trie search() funcombination +- 3. TrieNode里面存在 end的时候存string word, 表示到底. 用完了 word = null, 刚好截断重复查找的问题. + +##### 关于Trie +- Build Trie with target words: insert, search, startWith. Sometimes, just: `buildTree(words)` and return root. +- 依然要对board matrix做DFS。 +- no for loop on words. 直接对board DFS: +- 每一层,都会有个up-to-this-point的string. 在Trie里面check它是不是存在。以此判断。 +- 若不存在,就不必继续DFS下去了。 +- Trie solution time complexity, much better: +- build Trie: n * wordMaxLength +- search: boardWidth * boardHeight * (4^wordMaxLength + wordMaxLength[Trie Search]) + + +#### Regular DFS +- for loop on words: inside, do board DFS based on each word. +- Time cpmplexity: word[].length * boardWidth * boardHeight * (4^wordMaxLength) + +#### Previous Notes +- Big improvement: use boolean visited on TrieNode! +- 不要用rst.contains(...), 因为这个是O(n) 在leetcode还是会timeout(lintcode竟然可以pass)! +- 在Trie search() method 里面,凡是visit过的,mark一下。 + + + + +--- + +**15. [Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)** Level: Medium Tags: [Array, Backtracking, DFS] + + +#### DFS, Backtracking: +- 找到开头的字母, 然后recursively DFS 去把word串到底: +- 每到一个字母, 朝四个方向走, 之中一个true就可以. +- Note:每次到一个字母,mark一下'#'. 4个path recurse回来后,mark it back. + +#### Note: other ways of marking visited: +- 用一个boolean visited[][] +- Use hash map, key = x@y + + + + +--- + +**16. [Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)** Level: Medium Tags: [DFS, Divide and Conquer, Stack] + + +给一个expression string. 里面包括数字, 字母, 括号. 其中数字代表括号里面的内容重复几次. + +括号里面可以是String, 也可能是expression. + +目的: 把expression展开成一个正常的String. + + +#### Stack, Iteratively +- Process inner item first: last come, first serve, use stack. +- Record number globally and only use it when '[' is met. +- Stack存 [ ] 里面的内容, detect 括号开头结尾: 结尾时process inner string +- 有很多需要注意的细节才能做对: +- Stack 也可以用, 每个地方要注意 cast. 存进去的需要是Object: String, Integer +- 几个 type check: instanceof String, Character.isDigit(x), Integer.valueOf(int num) +- 出结果时候: `sb.insert(0, stack.pop())` + + +#### DFS +- Bottom->up: find deepest inner string first and expand from inside of `[ ]` +- 与Stack时需要考虑的一些function类似. 特别之处: **检查`[ ]`的结尾** +- 因为DFS时候, 括号里的substring会被保留着进入下一个level, 所以我们在base level要keep track of substring. +- 用int paren 来track 括号的开合, 当paren再次==0的时候 找到closure ']' +- 其他时候, 都要继续 append to substring + + + + +--- + +**17. [Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)** Level: Hard Tags: [Binary Search, DFS, Divide and Conquer] + + +2Dmatrix, 里面的value有一些递增, 递减的特点(细节比较长, 看原题). 目标是找到peak element + +peak: 比周围4个方向的点value大 + +#### DFS + +##### 基本原理 +- 我们不可能一口气准确定位(x,y), 但是我们可以再一个row/col里面, 找到1D array的 peak. +- 根据这个点, 再往剩下两个方向移动 +- 1. 在中间的一行i=midX, 找到peak所在的y. +- 2. 在中间的一列j=midY, 找到peak所在的x. (有可能强势override之前找到的y, 也就是放弃那一行的peak, 在midY上找peak) +- 3. 根据 (x,y) 的4个neighbor check (x,y)是不是 peak, 如果不是, 像更高的位置移动一格 +- 4. 根据之前算的 midX, midY 把board分成4个象限, 在每一份里面再继续找 +- 这个题目LintCode不给做了, 所以思路对的, 但是解答还没有再次验证. + +##### 剪枝/切分象限 +- 每次只是找到一个row/col里面的peak而已! +- 找到这个点, 就等于把board切成了两半. +- 然后, 再跟剩下的相邻的两个位置比较, 就知道了哪里更大, 就去哪里找peak, 也就是又切了第二刀. +- 切第二刀的时候, 也要把(x, y) 移到需要取的象限. 进行DFS +- 根据mid row 切割: +- http://www.jiuzhang.com/solution/find-peak-element-ii/#tag-highlight-lang-java +- http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf + +##### 时间复杂度 +- 每一个level都减一半 +- T(n) = n + T(n/2) = n + n/2 + n/4 + ... + 1 = n(1 + 1/2 + .... + 1/n) = 2n = O(n) + +#### Binary Search +- TODO +- O(nLogN) + + + +--- + +**18. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**19. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**20. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**21. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**22. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**23. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**24. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**25. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**26. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**27. [Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### DFS +- 简单处理swap +- recursively swap children + +#### BFS +- BFS with Queue +- 每次process一个node, swap children; 然后把child加进queue里面 +- 直到queue process完 + + + +--- + +**28. [Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree, 找最深depth + +#### DFS +- 这里要走过所有的node, 所以dfs非常合适 +- Divide and conquer. +- 维持一个最大值: Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +- 注意check root == null + +#### Note +- BFS is doable as well, but a bit more code to write: tracks largest level we reach + + + +--- + +**29. [Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### BFS +- Shortest path; minimum depth: 想到BFS, check level by level, BFS更能确保更快找到结果 +- depth definition: reach to a leaf node, where node.left == null && node.right == null +- BFS using queue, track level. + + +#### DFS +- Divide and Conquery一个最小值. +- 注意处理Leaf的null: null leaf 出现的时候, 就忽略这个leaf, 直接return算有leaf +- 另一种count的方法: 用Integer.MAX_VALUE代替 null leaf,这样可以避免错误counting. (不能直接recursive) +- 这个无论如何都要走所有node, 所以dfs应该比较适合. + + + + +--- + +**30. [Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**31. [Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +检查binary tree是否 identical. + +特点: subtree如果是有旋转的, 只要tree node value相等, 就可以算是identical + +#### DFS +- 在DFS的基础上, 比对左左,左右,右左,右右 + + + +--- + +**32. [Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)** Level: Easy Tags: [DFS, Tree] + + +#### DFS +- 基础binary tree traversal. 注意对null child的判断 + + + +--- + +**33. [Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree s, 和一个binary tree t, 检查t是不是s的subtree. + +#### DFS +- 跟 identical binary tree的写法很像 +- 只有 current s.val = t.val 的时候才需要compare same tree. +- 其他情况, 继续recursively isSubtree +- 注意:即使找到T1 == T2, 但很可能只是数字相同(这里不是binary search tree!!), 而children不同 +- 所以同时要继续recursively isSubtree(T1.left, T2) ...etc. + + + +--- + +**34. [Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个Binary Tree root, 以及两个node p, q. 找 p 和 q 的 lowest common ancestor + +#### DFS +- 因为是 binary tree, 所以直接盲目搜索搜索path不efficient, use extra space and waste time +- 巧用DFS来找每一个node的common ancestor. +- Need the assumption: 1. unique nodes across tree; 2. must have a solution +- 当root == null或者 p q 任何一个在findLCA底部被找到了(root== A || root == B),那么就return 这个root. +- 三种情况: +- 1. A,B都找到,那么这个level的node就是其中一层的ancestor: 其实,最先recursively return到的那个,就是最底的LCA parent. +- 2. A 或者 B 找到,那就还没有公共parent, return 非null得那个。 +- 3. A B 都null, 那就找错了没有呗, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time/space O(n) + + + +--- + +**35. [Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的list +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Brutly寻找p和q的common ancestor, 然后recursively drive left/right +- 非常巧妙, 但是也比较局限; 稍微变条件, 就很难recursive. +- 几种情况: +- 1. one of p, q 在leaf, 那么此时的root其实就是lowest common ancestor +- 2. 如果p, q 在root的左右两边, 这就是分叉口, 那么root就是lowest common ancestor +- 3. 如果p,q 在root的同一边 (左,右), 那么继续dfs +- O(1) extra space, O(logn) time + + + +--- + +**36. [Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**37. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**38. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + +**39. [Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum.java)** Level: Easy Tags: [DFS, Tree] + + +给一个inputSum, 然后dfs, 找到是否有一条path, 得出的path sum 跟 inputSum 一样. + +#### DFS +- 确定好结尾的条件: is leaf && val == sum +- 每一层减掉node.val, 然后dfs. +- 写一写: root == null => false 对parent nodes的影响. 这里发现没影响, 所以可以简化成用1个functionDFS. + + + + +--- + +**40. [Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)** Level: Easy Tags: [Backtracking, DFS, Tree] + + +给一个inputSum, 然后dfs, 找到所有path, 满足: path sum 跟 inputSum 一样. + +#### DFS, Backtracking +- 用remaining sum 来检测是否满足 input path sum 条件 +- 满足的时候add to result list +- 两种backtracking: +- 1. backtrack 当下node, 加进list, 然后dfs. dfs结束后删掉之前加进去的元素. 非常clean. +- 2. backtrack 下一个dfs level增加的value. dfs return 之后, 删掉list里面的末尾元素: 但是删掉的dfs余下的value. +- 第一种backtrack更加好掌握. + +#### Previous Notes: +- Binary Tree的一个基本题: 找到所有满足条件的path +- 遍历到底,比较sum vs. target +- 注意divide的情况。要把遍历的例子写写 + + + +--- + +**41. [Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)** Level: Easy Tags: [DFS, Double Recursive, Tree] + + +count所有存在的 path sum == target sum. 可以从任意点开始. 但是只能parent -> child . + +#### DFS +- 对所给的input sum 做减法, 知道 sum 达到一个目标值截止 +- 因为可以从任意点开始, 所以当sum达标时候, 需要继续recursive, 从而找到所有情况 (有正负数, sum可能继续增加/减少) +- 经典的 helper dfs recursive + self recursive +- 1. helper dfs recursive 处理包括root的情况 +- 2. self recursive 来引领 skip root的情况. + +#### 特点 +- 与 `Binary Tree Longest Consecutive Sequence II` 在recursive的做法上很相似: +- 利用dfs做包括root的recursive computation +- 利用这个function自己, 做`不包括root的recursive computation` + + + +--- + +**42. [Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)** Level: Medium Tags: [Backtracking, Combination, DFS] + + +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +#### DFS, Backtracking +- for loop, recursive (dfs). +- 每个item用一次, 下一个level dfs(index + 1) +- Combination DFS. 画个图想想. 每次从1~n里面pick一个数字i +- 因为下一层不能重新回去 [0~i]选,所以下一层recursive要从i+1开始选。 + + + +--- + +**43. [Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右:即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list + +#### DFS +- Use Map 来存每一个level的结果 +- dfs function 里, 如果 input depth 不存在, 就add to map. +- dfs function 里面先: dfs(node.right), 然后 dfs(node.left) +- 由于always depth search on right side, 所以map会被right branch populate; 然后才是 leftChild.right + + + + +--- + +**44. [Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)** Level: Medium Tags: [DFS, Tree] + + +找到从max path sum from root. 条件: 至少有一个node. + +#### DFS +- 比Binary Tree Maximum Path Sum I 简单许多. 因为条件给的更多:at least 1 node + have to start from root +- root一定用到 +- 3种情况: curr node, curr+left, curr+right +- 因为一定包括root, 说以从 `dfs(root, sum=0)` 开始, 每个level先加root, sum += root.val + + + +--- + +**45. [Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +找到binary tree 里的最长 consecutive sequence. + +#### DFS +- Divide and Conquer. dfs +- 分开 看左边/右边 +- 如果左边满足连续递增的规则, dfs (depth + 1), 如果不满足规则, dfs(depth = 1) +- 右边也是一样 +- 对结果跟max作比较, return + + + +--- + +**46. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**47. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**48. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**49. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**50. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**51. [Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)** Level: Medium Tags: [DFS, Divide and Conquer] + + +如题: Calculate the a^n % b where a, b and n are all 32bit integers. + +#### Divide and Conquer +- a^n可以被拆解成(a*a*a*a....*a), 是乘机形式,而%是可以把每一项都mod一下的。所以就拆开来take mod. +- 这里用个二分的方法,recursively二分下去,直到n/2为0或者1,然后分别对待. +- 注意1: 二分后要conquer,乘积可能大于Integer.MAX_VALUE, 所以用个long. +- 注意2: 要处理n%2==1的情况,二分时候自动省掉了一份 a,要乘一下。 + + + + +--- + +**52. [Find the Connected Component in the Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Connected%20Component%20in%20the%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS] + + +给一个undirected graph, return 所有的component. (这道题找不到了) + +#### BFS +- BFS遍历,把每个node的neighbor都加进来. +- 一定注意要把visit过的node Mark一下。因为curr node也会是别人的neighbor,会无限循环。 +- Component的定义:所有Component内的node必须被串联起来via path (反正这里是undirected, 只要链接上就好) +- 这道题:其实component在input里面都已经给好了,所有能一口气visit到的,全部加进queue里面,他们就是一个component里面的了。 +- 而我们这里不需要判断他们是不是Component + +#### DFS +- DFS 应该也可以 visit all nodes, mark visited. + + + +--- + +**53. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**54. [Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)** Level: Medium Tags: [Backtracking, DFS, String] + + +给一串数字, 检查是否是valid IP, 如果合理, 给出所有valid 的IP组合方式. + +#### Backtracking +- 递归的终点:list.zie() == 3, 解决最后一段 +- 递归在一个index上面, pass s.toCharArray() +- validate string要注意leading '0' +- 注意: 递归的时候可以用一个start/level/index来跑路 +- 但是尽量不要去改变Input source, 会变得非常confusing. +- note: code有点messy, 因为要考虑IP的valid情况 +- 那个'remainValid', 其实是一个对于remain substring的判断优化, 不成立的就不dfs了 + + + +--- + +**55. [Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Topological Sort] + + +#### Topological Sort BFS +- indegree tracking: Track all neighbors/childrens. 把所有的children都存在 inDegree里面 +- Process with a queue: 先把所有的root加一遍(indegree == 0),可能多个root。并且全部加到queue里面。 +- BFS with Queue: +- Only when map.get(label) == 0, add into queue && rst. (indegree剪完了, 就是root啦) +- inDegree在这里就 count down indegree, 确保在后面出现的node, 一定最后process. + + +#### Basics about graph +- 几个graph的condition: +- 1. 可能有多个root +- 2. directed node, 可以direct backwards. + +TODO: +- build`Map inDegree = new HashMap<>();` and include the root itself +- that is more traditional indegree building + + + +--- + +**56. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**57. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**58. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**59. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + +**60. [Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)** Level: Medium Tags: [Backtracking, DFS, DP] + + +String 只包含 + , - 两个符号. 两个人轮流把consecutive连续的`++`, 翻转成 `--`. + +如果其中一个人再无法翻转了, 另一个人就赢. 求: 给出string, 先手是否能赢. + +#### Backtracking +- curr player 每走一步, 就生成一种新的局面, dfs on this +- 等到dfs结束, 不论成功与否, 都要backtracking +- curr level: 把"++" 改成 "--"; backtrack的时候, 改回 '--' +- 换成boolean[] 比 string/stringBuilder要快很多, 因为不需要重新生成string. +- ++ 可以走 (n - 1)个位置: +- T(N) = (N - 2) * T(N - 2) = (N - 4) * (N - 2) * T(N - 4) ... = O(N!) + +##### iterate based on "++" +- 做一个String s的 replica: string or stringBuilder +- 每次dfs后, 然后更替里面的字符 "+" => "-" +- 目的只是Mark已经用过的index +- 真正的dfs 还是在 original input string s 身上展开 +- 每次都重新生成substring, 并不是很efficient + +##### Game theory +- 保证p1能胜利,就必须保持所有p2的move都不能赢 +- 或者说, 在知道棋的所有情况时, 只要p2有一种路子会输, p1就一定能走对路能赢. +- 同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +- p1: player1, p2: player2 + +#### O(N^2) 的 DP +- 需要Game Theory的功底, Nim game. https://www.jiuzhang.com/qa/941/ +- http://www.1point3acres.com/bbs/thread-137953-1-1.html +- TODO: https://leetcode.com/problems/flip-game-ii/discuss/73954/Theory-matters-from-Backtracking(128ms)-to-DP-(0ms) + + + +--- + +**61. [Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)** Level: Easy Tags: [Array, DFS] + + +#### DFS +- 虽然Easy, 但用到DFS最基本的想法. +- 1. dive deep +- 2. mark VISITED +- 3. sum it up +- Time: worst O(mn), traverse all possible nodes + +- 更要注意, 要从符合条件value==1的地方开始dfs. +- 对于什么island都没有的情况,area应该位0, 而不是Integer.MIN_VALUE, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**62. [Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning.java)** Level: Medium Tags: [Backtracking, DFS] + + +给个string s, partition(分段)后, 要确保每个partition都是palindrome. + +求所有partition palindrome组合. `list>` + +#### DFS +- 可以top->bottom: 遍历str, validate substring(start, i); if valid, add as candidate, and dfs; backtrack by remove candidate. +- 也可以bottom->up: 遍历str, validate substring(start, i); if valid, dfs(remaining str), return list of suffix; cross match with curr candidate. + +#### DFS Top->Bottom +- 在遍历str的时候,考虑从每个curr spot 到 str 结尾,是能有多少种palindorme? +- 那就从curr spot当个字符开始算,开始backtracking. +- 如果所选不是palindrome, 那move on. +- 若所选的确是palindrome, 加到path里面,DFS去下个level,等遍历到了结尾,这就产生了一种分割成palindrome的串。 +- 每次DFS结尾,要把这一层加的所选palindrome删掉,backtracking嘛 + +#### Optimization +- 可以再每一个dfs level 算 isPalindrome(S), 但是可以先把 boolean[][] isPalin 算出来, 每次O(1) 来用 +- 注意: isPalin[i][j] 是 inclusive的, 所以用的时候要认准坐标 +- Calculate isPalin[i][j]: pick mid point [0 ~ n] +- expand and validate palindrome at these indexes: `[mid, mid+1]` or `[mid-1][mid+1]` + +#### Complexity +- Overall Space O(n^2): 存 isPlain[][] +- Time O(2^n), 每一层都在做 pick/not pick index i 的选择, 所以worst case 2^n. +- 因为我们的isPalin[][]优化了palindrome的判断O(1), 所以overall Time: O(2^n) + + + +--- + +**63. [Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST, DFS, Tree] + + +BST里面有2个node misplace, 要归为. 要求: O(1) extra space + +#### Observation +- BST inorder traversal should give small -> large sequence +- misplaced means: a **large**->small item would occur, and later a large>**small** would occur. +- The first large && second small item are the 2 candidates. Example +- [1, 5, 7, 10, 12, 15, 18] +- [1, 5, `15, 10`, `12, 7`, 18] + +#### dfs, O(1) extra space +- traverse, and take note of the candidate +- at the end, swap value of the 2 candidates + +#### O(n) space +- inorder traversal the nodes and save in array, find the 2 items misplanced and swap them +- But O(n) space should not be allowed + + + + +--- + +**64. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + +**65. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**66. [Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)** Level: Review Tags: [BFS, DFS, DP] + + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- in dfs: remove the incorrect parentheses one at a time +- detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` +- once detected, remove the char from middle of s, and dfs on the rest of the s that has not been tested yet. + +##### Core concept: reverse test +- `if a parenthese string is valid, the reverse of it should also be valid` +- Test s with open='(', close=')' first; **reverse s**, and test it with open=')', close='(' + +##### Minor details +- only procceed to remove invalid parenthese when `count<0`, and also break && return dfs after the recursive calls. +- The above 2 facts eliminates all the redundant results. +- Reverse string before alternating open and close parentheses, so when returning final result, it will return the correct order. +- Open questions: how does it guarantee minimum removals? + +##### Backtracking +- 如果用stringbuffer, 那么久不会每次create new string, 但是需要maintain这个string buffer, 就会backtracking + +##### Complexity +- Seems to be O(n), but need to derive + +#### BFS +TODO + +#### DP + + + +--- + +**67. [Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)** Level: Medium Tags: [DFS, Hash Table, Tree] + + +给一串3-digit 的数组. 每个数字的表达一个TreeNode, 3 digit分别代表: depth.position.value + +这串数字已经从小到大排列. 求: 所有可能的 root->leaf path 的所有可能的 path sum 总和. + +#### DFS, Hash Table +- 因为`前两个digit可以uniquely identify`一个node, 所以可以把前两个digit作为key, 定位node. +- 特点: 比如考虑root, 有 n 个leaf, 就会加 n 遍root, 因为有 n 个 unique path嘛. +- 实现: 每个node, 上来先把curr value加进sum; 只要有child, 到这个node位置的以上path sum 就要被重加一次. +- format: depth.position.value. (on same level, position may not be continuous) +- approach: map each number into: , and dfs. +- Start from dfs(map, rootKey, sum): +- 1. add node value to sum +- 2. compute potential child. +- 3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +- 4. also, if child exist, dfs into next level +- Space, time O(n) + + + +--- + +**68. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**69. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**70. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**71. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**72. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + +**73. [Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + +time: O(n) +space: O(n) + +给一个tree, 按照题意找最一个node满足: +1. 这个node的subtree涵盖最深level的所有leaves. +2. 这个node必须是能找到的最deep那个 + +条件2的需求是因为: root本身就是满足条件1的node, 还有很多Higher-level node也是如此, 所以要找那个deepest. + + +#### DFS on tree +- 分析题目, 思想是: 看到tree里面所有的leaves, 找到他们最deep的 common ancestor +- Maintain a map +- Recursively dfs: return deepest node that has all leaves by these comparisons: +- 1. If left,right child same depth, return root: they need common ancestor +- 2. If not same depth, return the one with larger depth +- 被传送去上一个level的, 永远都是subtree里面符合题意的: the node containing all leaf nodes +- Visit all nodes once O(n), space O(n) + +#### BFS +- Find all leaves at deepest level +- Use map to track each node-parent +- Backtrack all nodes to find common ancestor + + + +--- + +**74. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + + +// 如何想到从中间initialize + + + +--- + +**75. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**76. [Populating Next Right Pointers in Each Node II.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node%20II.java)** Level: Medium Tags: [DFS, Tree] + +time: O(n) +space: O(1) + +给一个binary tree, 用constant space link 所有所有node.next to same level next node. + +#### DFS +- 用constant space 也就是不可以BFS, 但是mention了用dfs stack space没问题 (提示啊!) +- 1. link leftChild -> rightChild +- 2. resolve root.rightMost child -> first possible root.next.left/right child +- 3. dfs connect(rightChild), connect(leftChild) +- Each level should be fully linked from left side, so every reach to parent will have valid path or end. + +#### Trick +- 1. 处理 nextNode -> next -> next ...的case: 找到第一个有child的next node才可以罢休. 这个case很容易miss +- 2. 我们的假设是, 上一个level的所有node都应该是linked, 那么在dfs时候, 就应该先connect(root.right). 右孩子的全处理完毕, 那么trick1才可以施行. + + + +--- + +**77. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**78. [Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +#### Tree +- Traverse tree: left, right +- Concept of partial compare vs. whole compare + + + +--- + +**79. [Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)** Level: Medium Tags: [BFS, DFS] + + +给一个room 2D grid. 里面有墙-1, 门0, 还有empty space INF(Math.MAX_VALUE). + +对每个empty space而言, fill it with dist to nearest gate. + +#### DFS +- Form empty room: it can reach different gate, but each shortest length will be determined by the 4 directions. +- Option1(NOT applicable). DFS on INF, mark visited, summerize results of 4 directions. +- hard to resue: we do not know the direction in cached result dist[i][j] +- Option2. DFS on gate, and each step taken to each direction will +1 on the spot: distance from one '0'; +- Through dfs from all zeros, update each spot with shorter dist +- Worst time: O(mn), where entre rooms[][] are gates. It takes O(mn) to complete the iteration. Other gates be skipped by `if (rooms[x][y] <= dist) return;` + +#### BFS +- Exact same concept. Init with `Queue queue = new LinkedList()` + + + +--- + +**80. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**81. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + +**82. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**83. [Flood Fill.java](https://github.com/awangdev/LintCode/blob/master/Java/Flood%20Fill.java)** Level: Easy Tags: [DFS] + + +Same as MS Paint + +#### DFS +- track `boolean[][] visited`, validate before dfs + + + +--- + +**84. [Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph] + + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes. +- 先能复制多少Node复制多少. 然后把neighbor 加上 +- Use `map` to mark visited + +#### DFS +- Given graph node obj `{val, list of neighbor}`: copy the node and all neighbors +- Mark visited using map +- for loop on the each one of the neighbors: map copy, record in map, and further dfs +- once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +- 主要思想是: 一旦复制过了, 不必要重新复制 + +#### BFS +- Copy the root node, then copy all the neighbors. +- Mark copied node in map. +- Use queue to contain the newly added neighbors. Need to work on them in the future. + + + +--- + +**85. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + +**86. [Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)** Level: Medium Tags: [Backtracking, DFS, Permutation] + + +#### Recursive: Backtracking +- Given a remaining list: 取, 或者不取 +- always iterate over full `nums[]`, use list.contains() to check if item has been added. +- Improvement: maintain list (add/remove elements) instead of 'list.contains' +- time O(n!): visit all possible outcome +- T(n) = n * T(n-1) + O(1) + +#### Iterative: Insertion +- 插入法: +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- 但是比dfs要快, 因该是因为 # of checks 少: 不需要check list.size(), 不需要maintain remaining list. + +#### Previous Notes +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- A bit slower, possibly because of the polling and saving the entire list every time + + + + +--- + +**87. [Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)** Level: Hard Tags: [DFS, Greedy, Math] + + +#### Greedy, Iterative +- For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +- We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +- Track the newly formed string; if new, add the new char to overall result +- Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] +- Same concept as dfs + +#### DFS +- Same concept: use window to cut out tail, and append with new candidate +- do this for k^n = Math.pow(k, n) times + + + +--- + +**88. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**89. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**90. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + +**91. [The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)** Level: Medium Tags: [BFS, DFS] + + +#### BFS +- BFS on coordinates +- always attempt to move to end of border +- use boolean[][] visited to alingn with BFS solution in Maze II, III, where it uses Node[][] to store state on each item. + + + +--- + +**92. [The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)** Level: Medium Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- if already found a good/shorter route, skip +- `if (distMap[node.x][node.y] <= node.dist) continue;` +- This always terminates the possibility to go return to original route, because the dist will be double/higher + + + +--- + +**93. [The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)** Level: Hard Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- 跟 Maze I, II 类似, 用一个 Node[][] 来存每一个(x,y)的state. +- Different from traditional BFS(shortest path): `it terminates BFS when good solution exists (distance), but will finish all possible routes` +- 1. `Termination condition`: if we already have a good/better solution on nodeMap[x][y], no need to add a new one +- 2. Always cache the node if passed the test in step1 +- 3. Always offer the moved position as a new node to queue (as long as it fits condition) +- 4. Finally the item at nodeMap[target.x][target.y] will have the best solution. + + + +--- + +**94. [Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)** Level: Hard Tags: [Backtracking, DFS] + + +#### DFS +- Different from regular dfs to visit all, the robot move() function need to be called, backtrack needs to move() manually and backtracking path shold not be blocked by visited positions +- IMPORTANT: Mark on the way in using set, but `backtrack directly without re-check against set` +- Mark coordinate 'x@y' +- Backtrack: turn 2 times to revert, move 1 step, and turn 2 times to revert back. +- Direction has to be up, right, down, left. +- `int [] dx = {-1, 0, 1, 0};`, `int[] dy = {0, 1, 0, -1};` + + + +--- + + + + + + + +## Design (21) +**0. [LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)** Level: Hard Tags: [Design, Hash Table] + + +#### Hash Table +- 具体看thoughts, 几种不同的方式使用map +- `regular object map`: map of , where `item : {int val; int count}` +- Use a Map to track the frequency +- Track constant capacity, and minimum frequency +- Every get(): update all frequency map as well +- Every put(): update all frequency map as well, with optional removal (if over capacity) + +- Original post: http://www.cnblogs.com/grandyang/p/6258459.html +- TODO: one doubly linked list might be good enough to replace below: +- `frequency list map`: map of >, where the list preserves `recency` +- `item location in frequency map`: map of : +- index relative to the item in a particular list, not tracking which list here + + + +--- + +**1. [Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)** Level: Medium Tags: [BST, Design, Stack, Tree] + + +画一下, BST in order traversal. 用stack记录最小值, 放在top. O(h) space. +每次消耗TreeNode, 都看看rightNode(其实就是下一个最小的candidate), 并且一条龙stack叠上rightNode所有的left子孙. + +Previous Notes: +用O(h)空间的做法: + +理解binary search tree inorder traversal的规律: + 先找left.left.left ....left 到底,这里是加进stack. + 然后考虑parent,然后再right. + +例如这题: + stack里面top,也就是tree最左下角的node先考虑,取名rst. + 其实这个rst拿出来以后, 它也同时是最底层left null的parent,算考虑过了最底层的parent。 + 最后就考虑最底层的parent.right, 也就是rst.right. + +注意: + next()其实有个while loop, 很可能是O(h).题目要求average O(1),所以也是okay的. + + +用O(1)空间的做法:不存stack, 时刻update current为最小值。 + +找下一个最小值,如果current有right child: + 和用stack时的iteration类似,那么再找一遍current.right的left-most child,就是最小值了。 + +如果current没有right child: + 那么就要找current node的右上parent, search in BinarySearchTree from root. + +注意: + 一定要确保找到的parent满足parent.left == current. + 反而言之,如果current是parent的 right child, 那么下一轮就会重新process parent。 + 但是有错:binary search tree里面parent是小于right child的,也就是在之前一步肯定visit过,如此便会死循环。 + + + + +--- + +**2. [Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, Stack] + + +方法1: 用queue, 把需要的item全部打出来 +方法2: 用stack, 把需要的item先存一行, 每次打开子序列时候, 全部加回stack. + + + +--- + +**3. [Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- HashMap构建Trie. Trie三个Method: +- 1. Inset: 加 word +- 2. Search: 找word +- 3. StartWith: 找prefix + +##### 特点 +- 只有两条children的是binary tree. 那么多个children就是Trie +- 那么没有left/right pointer怎么找孩子? +- 用HashMap,以child的label为Key,value就是child node。 HashMap走位 + +##### 其他 +- node里的char在这是optional. 只要在每个TrieNode里面用map存储向下分布的children就好了. +- 另外有种题目,比如是跟其他种类的search相关,在结尾要return whole string,就可以在node里存一个up-to-this-point的String。 + +##### Previous Note +- 如果是遇到一个一个字查询的题,可以考虑一下。 +- 构建TrieNode的时候要注意:如何找孩子?如果是个map的话,其实就挺好走位的。 +- 而且,每个node里面的 char 或者string有时候用处不大, +- 可以为空。但是有些题目,比如在结尾要return一些什么String,就可以在end string那边存一个真的String。 + + + + + +--- + +**4. [Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +Trie结构, prefix tree的变形: '.'可以代替任何字符,那么就要iterate这个node所有的children. + +节点里面有char, isEnd, HashMap +Build trie = Insert word:没node就加,有node就移动。 +Search word:没有node就报错. 到结尾return true + +这题因为'.'可以代替任何possible的字符,没一种都是一个新的path,所以recursive做比较好些。 +(iterative就要queue了,麻烦点) + + + +--- + +**5. [Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)** Level: Easy Tags: [Design, Stack] + + +双Stack:一个正常stack,另一个minStack存当下level最小值. 注意维护minStack的变化 + +另外. 如果要maxStack,也是类似做法 + + + +--- + +**6. [Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)** Level: Easy Tags: [Design, Stack] + + +#### 双Stack +画图, 知道最后maintain的stack是那个 reverseStack: pop(), peek(), empty() 都在这个stack上, 无需变换. +push()里面做stack和reverseStack的来回倾倒. +相比老的code, 在PUSH里面做倾倒, 更容易读. + +#### Previous notes +双Stack. 一个是等于是queue,一个是backfillStack. +Tricky: 是在pop()和peek()的时候backfill, 并且要等到stack用完再backfill. +写一下例子就知道,如果提早backfill,stack.peek()就不是queue的head了. + + + + +--- + +**7. [Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)** Level: Easy Tags: [Design] + + +让一个class 是 singleton + + + +--- + +**8. [Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)** Level: Easy Tags: [Design, Stack] + + +如题. + +#### Queue, 倒水 +- 两个Queue,交互倒水 +- 用一个Temp做swap + +##### 做法1 +- 逻辑在push里面: +- 1. x 放q2。 +- 2. q1全部offer/append到q2. +- 3. 用一个Temp做swap q1, q2. +- q1的头,就一直是最后加进去的值. + + +##### 做法2 +- 逻辑在top()/pop()里, 每次换水,查看末尾项. + + + + +--- + +**9. [LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)** Level: Hard Tags: [Design, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度。 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) + +##### 巧妙点 +- 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +- 2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +- 直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法: +- moveToHead() +- insertHead() +- remove() + +#### O(n) 检查重复 +- timeout method, 天真的来了一个O(n) 的解法,结果果然timeout. +- 一个map存数值。一个queue来存排位。 +- 每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + + + + +--- + +**10. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**11. [Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)** Level: Medium Tags: [Design, Hash Table] + + + +给一个string[] dict, 和一个word. + +每个word都可以缩写成固定的abbreviation ``(详细看原题) + +检查input word是否满足unique + +#### HashMap +- 简单算出abbreviatioin +- 检查abbr是否存在; 如果存在, 是不是input word本身. + + + +--- + +**12. [ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)** Level: Medium Tags: [Design, Hash Table] + + +#### basic implementation +- 用HashMap, 理解题目规律,因为重复的计算可以被覆盖,所以是个优化题。没有什么太大的悬念和意义. +- 消灭重合点: +- 如果process当下col, 其实要减去过去所有加过的row的交接点。。。 +- 再分析,就是每次碰到row 取一个单点, sumRow += xxx。 +- 然后process当下col时候, sum += colValue * N - sumRow. 就等于把交叉所有row(曾经Process过的row)的点减去了。很方便。 +- 最后read in 是O(P), process也是O(P). + + + + +--- + +**13. [Peeking Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Peeking%20Iterator.java)** Level: Medium Tags: [Design] + + +#### Use concept pre cache +- 找一个cache来存next()的值, 也就是: next value的值提前存在cache里面 +- 因此peek()的时候, 就可以直接return cache, 而不用做 itt.next() +- 然后每次真的next()的时候, 里取下一个itt.next()维护这个cache + +#### Previous notes +- 再一次理解错题意. peek() 就是头顶,但是不一定是最大值啊。 +- 总是把PEEK想成了最大值,然后用2 STACK做了最大值的cache,练的一手好双stack,可惜错了。 + + + + +--- + +**14. [Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)** Level: Hard Tags: [Design, Geometry, Hash Table] + + +看的list of coordinates 是否能组成perfect rectangle, 并且不允许overlap area. + +#### 画图发现特点 +- 特点1: 所有给出的点(再找出没有specify的对角线点), 如果最后组成perfect rectangle, 都应该互相消除, 最后剩下4个corner +- 特点2: 找到所有点里面的min/max (x,y), 最后组成的 maxArea, 应该跟过程中accumulate的area相等 +- 特点1确保中间没有空心的部分, 保证所有的重合点都会互相消除, 最后剩下4个顶点 +- 特点2确保没有重合: 重合的area会最终超出maxArea + + + +--- + +**15. [Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + +time: O(1) avg +space: O(n) + +#### Hash Table +- 用`map 来track value->index`, 用`list track index->value` +- map查看value是否存在 +- list maintain 用来 insert/remove/random operations. +- 特点: 一旦remove, 换到list结尾然后 `list.remove(list.size() - 1)`, 这样remove的cost更低. +- list.remove(object) 应该是要O(logn) 做一个search的. + + + +--- + +**16. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**17. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**18. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + +**19. [Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison + + + +--- + +**20. [Flatten 2D Vector.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%202D%20Vector.java)** Level: Medium Tags: [Design] + + +Implement an iterator to flatten a 2d vector. + +Just move pointers carefully with next(), hashNext() + +#### Basic Implementation using x, y corrdinate +- 就是把2D list里面的element全部遍历一遍。 +- 跟一个nxn的matrix遍历,是没区别的拉; 所有来个x,y,把2d list跑一变。 + +#### Always return item at index 0, and remove from list? +- list 方便remove, 考虑吧reduce input vector (就像给的是linked list 一样) + + + +--- + + + + + + + +## Game Theory (4) +**0. [Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)** Level: Medium Tags: [DP, Game Theory, Greedy] + + +拿棋子游戏, 每个人可以拿1个或者2个, 拿走最后一个子儿的输. 问: 根据给的棋子输, 是否能确定先手的输赢? + +Game Theory: 如果我要赢, 后手得到的局面一定要'有输的可能'. + +#### DP, Game Theory +- 要赢, 必须保证对手拿到棋盘时, 在所有他可走的情况中, '有可能败', 那就足够. +- 设计dp[i]:表示我面对i个coins的局面时是否能赢, 取决于我拿掉1个,或者2个时, 对手是不是会可能输? +- dp[i] = !dp[i - 1] || !dp[i-2] +- 时间: O(n), 空间O(n) +- 博弈问题, 常从'我的第一步'角度分析, 因为此时局面最简单. + +#### Rolling Array +空间优化O(1). Rolling array, %2 + + + +--- + +**1. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**2. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**3. [Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)** Level: Easy Tags: [Brainteaser, DP, Game Theory] + + +#### Brainteaser +- 著名Nim游戏 +- 写一些,发现n=4,5,6,7,8...etc之后的情况有规律性: 谁先手拿到4就输了. +- 最终很简单n%4!=0就可以了, time, space O(1) + +#### DP +- 正规地找规律做, 就跟 coins in a line 一样, 按照先手后手来做 +- 可以rolling array 优化空间 +- Time O(n), 当然啦, 这个题目这样会timeout, 可以使用brainteaser的做法写出结果. + + + +--- + + + + + + + +## HashHeap (1) +**0. [HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/HashHeap.java)** Level: Hard Tags: [HashHeap, Heap] + + +非题.是从九章找来的HashHeap implementation. + + + +--- + + + + + + + +## Hash Table (69) +**0. [Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)** Level: Medium Tags: [Hash Table, Math] + + +TODO: no need of hashMap, just use set to store the existing + +不难想到处理除法:考虑正负,考虑小数点前后。主要是小数点以后的要着重考虑。 +很容易忽略的是integer的益处。 + + +--- + +**1. [Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/Jewels%20and%20Stones.java)** Level: Easy Tags: [Hash Table] + +1524017454 + +给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 + +#### Basic HashSet + + + +--- + +**2. [LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)** Level: Hard Tags: [Design, Hash Table] + + +#### Hash Table +- 具体看thoughts, 几种不同的方式使用map +- `regular object map`: map of , where `item : {int val; int count}` +- Use a Map to track the frequency +- Track constant capacity, and minimum frequency +- Every get(): update all frequency map as well +- Every put(): update all frequency map as well, with optional removal (if over capacity) + +- Original post: http://www.cnblogs.com/grandyang/p/6258459.html +- TODO: one doubly linked list might be good enough to replace below: +- `frequency list map`: map of >, where the list preserves `recency` +- `item location in frequency map`: map of : +- index relative to the item in a particular list, not tracking which list here + + + +--- + +**3. [Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)** Level: Medium Tags: [Hash Table, Linked List] + + +TODO: +1. hash table solution not passing +2. Find O(n) solution + +#### Hash Table +- 与其他Majority Number一样。 +- 出现次数多余1/k,就要分成k份count occurance.用HashMap。 存在的+1;不存在map里的,分情况: +- 若map.size() == k,说明candidate都满了,要在map里把所有现存的都-1; +- 若map.size() < k, 说明该加新candidate,那么map.put(xxx, 1); +- 最后在HashMap里找出所留下的occurance最大的那个数。 +- 但这样的worst case是 O(nk) + + + +--- + +**4. [Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited value. +- 在nest for loop里面validate row,col,and block. +- validate block要利用i 和 j 增长的规律。 +- 说白了,i && j是按照0~n增长的index, 具体怎么用是可以flexible的。这个方法在同一个nest for loop解决所有运算。 +- `int c = 3 * (i % 3) + j % 3;` //make use of how i and j increases +- `int r = 3 * (i / 3) + j / 3;` + +#### A bit Slower approach +- 单独做block validation: validate block的时候虽然看到了4层for. 其实也就是n^2 +- 可能代码稍微复杂一点 + + + +--- + +**5. [Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Anagram%20Mappings.java)** Level: Easy Tags: [Hash Table] + + +比较简单, 用HashMap 存index list. 最后再遍历一遍数组A, 列举出所有元素. +O(n) + + + +--- + +**6. [Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/Island%20Perimeter.java)** Level: Easy Tags: [Hash Table] + + +#### Brutle +- 每个格子4个墙;每个shared的墙要-2 (墙是两面, -1 * 2) +- 最后合计结果就好. + +#### Hash Table +- 不必想太多用HashMap做.但是也可以思考一下: +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么久可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不打,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + + + +--- + +**7. [Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)** Level: Medium Tags: [Hash Table, Math] + + +其实想到了切入点, 是个可难可简单的题目. 这里的encode就是想办法把url存起来, 然后给个 key. +那么具体怎么做这个key, 简单就可以用一个map, 然后counting. 复杂一点就可以做random letter/number组成key. + + + +--- + +**8. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**9. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**10. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**11. [Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +HashMap + + + +--- + +**12. [Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)** Level: Medium Tags: [Hash Table, String, Two Pointers] + + +方法1: +Two Pointers +双指针: 从start开始遍历, 但是第一步是while loop来推进end; 直到推不动end, 然后start++ +巧妙点: 因为end是外围variable, 在start的loop上, end不会重置;[star ~ end] 中间不需要重复计算. +最终可以O(n); + +Previous verison of two pointers: +用两个pointer, head和i. +注意:head很可能被退回到很早的地方,比如abbbbbba,当遇到第二个a,head竟然变成了 head = 0+1 = 1. +当然这是不对的,所以head要确保一直增长,不回溯 + +方法2: + HashMap: + 一旦有重复, rest map. + 没有重复时候, 不断map.put(), 然后求max值 + +问题: 每次reset map之后就开始从新从一个最早的index计算, 最坏情况是O(n^2): +'abcdef....xyza' + + + + +--- + +**13. [Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, String, Two Pointers] + + +基本思想: 用个char[]存string的frequency. 然后2pointer, end走到底, 不断validate. +符合的就process as result candidate. + +HashMap的做法比char[]写起来要复杂一点, 但是更generic + + + +--- + +**14. [Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String] + + +大清洗 O(nk) +map.size一旦>k,要把longest string最开头(marked by pointer:start)的那个char抹掉 +一旦某一个char要被清除,所以在这个char 的1st and last appearance之间的char都要被清洗from map + + + + +--- + +**15. [Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)** Level: Hard Tags: [Hash Table, String, Trie] + + +Obvious的做法是全部试一遍, 判断, 变成 O(n^2) * O(m) = O(mn^2). O(m): isPalindrome() time. + +当然不行了, 那就看是O(nlogN), 还是O(n)? + +#### 方法1: Hash Table + Palindrome的性质. 复合型. +O(mn) + +##### 思路 +- 每一个word, 都可以拆分成 front + mid + end. 如果这个word + 其他word可以组成palindrome,那就是说 +- 砍掉 (mid+end), front.reverse() 应该存在 words[] 里面. +- 砍掉 (front+mid), end.reverse() 应该存在 words[] 里面. +- 我们用HashMap存所有的, 然后reverse, 找配对就好. + +##### Corner case +- 如果有 empty string "", 那么它跟任何一个palindrome word, 都可以配对, 并且根据位置前后变换, 凑成2份 distinct indexes. +- 这样就有了那个 `if (reverseEnd.equals("")) {...}` 的logic. +- 注意: 虽然在处理砍头/砍尾的两个 for loop里面都在根据 empty string 重复记录, + 但因为 "" 自己本身不能作为起点, 所以overall只会在被其他palindrome配对时记录一次. + + +#### 方法2: Trie +还要做一下那. + + + +--- + +**16. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**17. [Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Recursive +- 在自己的基础上recursive, 不用helper function +- Divide and Conquer, with helper(dfs) method +- O(n) time, no extra space + +#### Iterative: Stack +- Add left nodes all the way +- Print curr +- Move to right, add right if possible +- O(n) time, O(h) space + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. + +若不右移, 很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + +#### HashMap +? How? + + + +--- + +**18. [Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate.java)** Level: Easy Tags: [Array, Hash Table] + + +无序数组, 找是否有重复element, return true/false. + +#### HashSet +- No brain: HashSet. +- Time O(n), Space O(n) + +#### Sort, Binary Search +- Arrays.sort(x): Time O(nLogN), Space O(1) +- 排序后, 重复数会排在一起, 然后 binary search + + + +--- + +**19. [Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20II.java)** Level: Easy Tags: [Array, Hash Table] + + +Unsorted array, 找出是否有duplicate elemenets: 必要条件是, 这两个element的index i,j 的大小最多相差k. + +#### HashSet +- 很巧妙地根据k range地条件, 把HashSet里面的值控制在[i - k, i] +- 每次不断地往set里面加新元素, 从set里减去末尾index的元素 +- 而set.add(x)如果遇到重复, 会return false. +- 一旦在这个length k 的 range里面, 有重复, 就符合条件. +- Time O(n) + +#### HashTable +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k +- Time O(nm), m = # of duplicates + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**20. [Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Anagrams%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, Sliding Window] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### HashTable +- count character apperance 就想到hashtable +- 注意countS, countP 的技巧: 作比较只需要O(26) +- Overall timeO(n) +- 小心不要用一个int[] count 来技术 查0, 复杂度是O(n) + + + +--- + +**21. [Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Anagrams.java)** Level: Medium Tags: [Hash Table, String] + + +给一串string, return list of list, 把anagram 放在一起. + +#### Hash Table, key 是 character frequency +- 存anagram +- 用 character frequency 来做unique key +- 用固定长度的char[26] arr 存每个字母的frequency; 然后再 new string(arr). +- 因为每个位子上的frequency的变化,就能构建一个unique的string +- O(nk), k = max word length + + +#### Hash Table, key 是 sorted string +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**22. [Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Primes.java)** Level: Easy Tags: [Hash Table, Math] + + +计数: 所有小于n的prime number. + +#### prime number定义 +- >=2的没有除自己和1以外公约数的数。 +- 还有另外一个定义方法: 这个n,有没有小于n的一个i, 而达到: i * i + # of i = n. 如果有,那就不是 prime + +#### Steps +- 一个boolean长条,存isPrime[]。 然后从i=2, 全部变true. +- hash key: the number itself +- 然后利用这个因子的性质,非prime满足条件: self*self, self * self + self ... etc. +- 所以就check每一个j, j+i, j+i+i, 然后把所有non-prime全部mark成false. +- 最后,数一遍还剩下的true个数就好了 + + + +--- + +**23. [Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation.java)** Level: Easy Tags: [Hash Table] + + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count occurrance +- 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assum lower case letter. 应该至少是所有ASCII code + + + +--- + +**24. [Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Tree] + + +给一个Binary Tree root, 以及两个node A, B. 特点: node里面存了parent pointer. 找 lowest common ancestor + + +#### Hash Set +- 这个题有个奇葩的地方, 每个node还有一个parent, 所以可以自底向上. +- save visited in hashset. 第一个duplicate, 就是A B 的 lowest common ancestor + +#### Save in lists +- 自底向上。利用parent往root方向返回 +- 把所有parent存下来, 然后在两个list里面找最后一个 common node + +#### 注意 +- 无法从root去直接搜target node 而做成两个list. 因为根本不是Binary Search Tree! + + + + +--- + +**25. [Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/Hash%20Function.java)** Level: Easy Tags: [Hash Table] + + +#### Hash Function +- 解释Hash怎么做. +- Hash function例子: +- hashcode("abcd") = (ascii(a) * 33^3 + ascii(b) * 33^2 + ascii(c) *33^1 + ascii(d)*33^0) % HASH_SIZE +- 用到的参数比如: magic number 33, HASH_SIZE. + +- Hash的意义是:给一个string key, 转换成数字,从而把size变得更小。 +- 真实的implementation还要处理collision, 可能需要design hash function 等等。 + + +##### 每一步都%HASH_SIZE的原因 +- hashRst = hashRst * 33 + (int)(key[i]); +- hashRst = hashRst % HASH_SIZE; +- 原因是,hashRst会变得太大,所以不能算完和 再 %... + + + +--- + +**26. [LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)** Level: Hard Tags: [Design, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度。 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) + +##### 巧妙点 +- 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +- 2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +- 直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法: +- moveToHead() +- insertHead() +- remove() + +#### O(n) 检查重复 +- timeout method, 天真的来了一个O(n) 的解法,结果果然timeout. +- 一个map存数值。一个queue来存排位。 +- 每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + + + + +--- + +**27. [Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序, 排序以后才能逐个看是否partial string已经存在 +- 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 +- 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: 1. 长 string 排在前; 2. 相等length, 按照dictionary order 排序 +- 全部放入Trie. Trie.insert() +- 针对sorted words array, 从最长的开始找 Trie.startWith. +- 一旦找到, 就是符合题意的, 直接return. +- 注意: startWith 必须每一个node都是 isEnd, 才满足'逐个字母拼出' 的条件. +- Time: build Trie O(mn) + sort:O(nlogn) => O(nlogn) +- Space: O(mn) + +#### +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**28. [Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)** Level: Hard Tags: [Array, Hash Table, Union Find] + + +给一串数字, unsorted, 找这串数字里面的连续元素序列长度 (consecutive序列, 是数字连续, 并不是说要按照原order) + +#### HashSet +- 要想看连续元素, 必须要num++, num--这样搜索 +- 1. 需要O(1)找到元素 +- 2. 需要简单快速找到 num - 1, num + 1. +- 如果用min,max开array, 耗费空间 +- 用HashSet来存, 用set.contains() 来查找 num - 1, num + 1 存在与否 +- for loop. O(n) +- 里面的while loop 一般不会有O(n); 一旦O(n), 也意味着set 清零, for loop也不会有更多 inner while 的衍生. +- overall O(n) 时间复杂度 + + +#### Union Find +- 最终是要把相连的元素算一下总长, 其实也就是把元素group起来, 相连的group在一起, 于是想到UnionFind +- 这里用到了一个`int[] size` 来帮助处理 `合并的时候parent是哪个`的问题: 永远往group大的union里去 +- main function 里面, 有一个map来track, 每个元素, 只处理1遍. +- union的内容: current number - 1, current number + 1 +- https://www.jianshu.com/p/e6b955ca208f + +##### 特点 +- Union Find 在index上做好像更加容易 +- 其他union find function: `boolean connected(a,b){return find(a) == find(b)}` + + + +--- + +**29. [Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)** Level: Medium Tags: [Design, Hash Table] + + + +给一个string[] dict, 和一个word. + +每个word都可以缩写成固定的abbreviation ``(详细看原题) + +检查input word是否满足unique + +#### HashMap +- 简单算出abbreviatioin +- 检查abbr是否存在; 如果存在, 是不是input word本身. + + + +--- + +**30. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**31. [ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)** Level: Medium Tags: [Design, Hash Table] + + +#### basic implementation +- 用HashMap, 理解题目规律,因为重复的计算可以被覆盖,所以是个优化题。没有什么太大的悬念和意义. +- 消灭重合点: +- 如果process当下col, 其实要减去过去所有加过的row的交接点。。。 +- 再分析,就是每次碰到row 取一个单点, sumRow += xxx。 +- 然后process当下col时候, sum += colValue * N - sumRow. 就等于把交叉所有row(曾经Process过的row)的点减去了。很方便。 +- 最后read in 是O(P), process也是O(P). + + + + +--- + +**32. [Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + +time: O(n) +space: O(1) + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List +- Basic Implementation of copy linked list: +- use node and dummy to hold new list, 遍历head.next .... null. +- Map 在这里用来: 1. avoid creating same node; 2. return the item if existing +- map 的 key全部是old object, 新的key全部是 newly created object +- 每一步都check map里面有没有head. 没有? 加上 +- 每一步都check map里面有没有head.random. 没有? 加上 + + + +--- + +**33. [HashWithCustomizedClass(LinkedList).java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithCustomizedClass(LinkedList).java)** Level: Medium Tags: [Hash Table] + + +练习HashMap with customized class. functions: get(), put(), getRandom() + +#### Hash Table +- store map as array: `Entry[] table;` +- store entry as linked list: `public Entry(K key, V value, Entry next)` +- compute hashKey: `Math.abs(key.hashCode()) % this.capacity` +- Handle collision: +- 1. Check if duplicate (matching key), if so, replace and return +- 2. Check through the linked list, find find duplicate (matching key), replace and return. +- 3. If no duplicate, add the entry to the tail +- Find item: compute hashKey -> find linked list -> iterate over list to find a matching key. + + + +--- + +**34. [Rehashing.java](https://github.com/awangdev/LintCode/blob/master/Java/Rehashing.java)** Level: Medium Tags: [Hash Table] + + +给一个Hash Table, 是用 linked list 做的. 问题是: capacity太小, collision太多的情况下, 需要double capacity 然后rehash. + +#### Hash Table +- 明白hashCode() function的意义: 拿到hashKey的时候, 用hashKey%capacity 来做hash code +- hashcode就是hash map里面的index +- 明白collision handling 的方式, 和如何double capacity而rehashing +- 都是基本操作, 概念实现 + + + +--- + +**35. [Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)** Level: Hard Tags: [Greedy, Hash Table, Stack] + + +#### Hash Table, Greedy +- count[] = int[256], 不需要 `c-'a'` +- boolean visited[]: 一旦一个字母固定了位置后, 再次遇到时候, 直接跳过用过的character +- 如果tail字母可以变小, 那就delete掉tail, 重新接上新字母 (前提条件: 去掉的字母后面还会再出现, set visited[tail] = false) +- Space: O(1) count[], visited[]. +- Time: Go through all letters O(n) + +#### Stack +- Use stack instead of stringBuffer: keep append/remove last added item +- However, stringBuffer appears to be faster than stack. + + + +--- + +**36. [Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum.java)** Level: Easy Tags: [Array, Hash Table] + + +#### HashMap +- 相对暴力简洁: 找到一个value, 存一个index +- 若在HashMap里面 match 到结果, 就return HashMap里存的index. +- O(n) space && time. + +#### Sort array, two pointer +- 前后++, --搜索. Sort 用时O(nlogn). +- 1. 第一步 two pointer 找 value. +- 2. 注意,要利用额外的空间保留original array, 用来时候找index. (此处不能用HashMap,因为以value 为key,但value可能重复) +- O(n) space, O(nlogn) time. + + + + +--- + +**37. [Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)** Level: Hard Tags: [Design, Geometry, Hash Table] + + +看的list of coordinates 是否能组成perfect rectangle, 并且不允许overlap area. + +#### 画图发现特点 +- 特点1: 所有给出的点(再找出没有specify的对角线点), 如果最后组成perfect rectangle, 都应该互相消除, 最后剩下4个corner +- 特点2: 找到所有点里面的min/max (x,y), 最后组成的 maxArea, 应该跟过程中accumulate的area相等 +- 特点1确保中间没有空心的部分, 保证所有的重合点都会互相消除, 最后剩下4个顶点 +- 特点2确保没有重合: 重合的area会最终超出maxArea + + + +--- + +**38. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + +**39. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**40. [Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)** Level: Medium Tags: [Array, Hash Table, PreSum] + + +给一个int[][] matrix, 找一个sub matrix, where the sum == 0. + +#### PreSum的思想 +- 算出一个右下角点(i,j)到(0,0)的大小: 上一块 + 左一块 + curr node - overlap area +- preSum[i][j]: sum from (0,0) to (i-1,j-1) +- same approach as `subarray sum`: use hashmap to store diff->index; if diff re-appears, that means sum of 0 has occurred +- sequence of calculation: 1. iterate over start row. 2. iterate over end row. 3. iterate over col number (this is where hashmap is stored based on) +- the iteration over col is like a screening: find previous sum and determine result +- Note: 其实并没有真的去找 `== 0` 的解答,而是根据特性来判断 `剩下的/后来加上的一定是0` + + + +--- + +**41. [H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)** Level: Medium Tags: [Bucket Sort, Hash Table, Sort] + + +找到h-index, 给的citation int[] 并不是sorted. h-index 的definition 具体看题目. + +#### Sort, find h from end +- 例子写出来,发现可以sort以后按照定义搜索一遍。 nlogn. +- 搜索一遍时候可以优化,用binary search. 但是没意义,因为array.sort已经用了nlogn +- 题目给的规则, 从小到大排序后: 剩下的 paper `n-h`, 全部要 <= h 个 citation. +- time O(nlogn), search O(n) + +##### 正向思考 +- 从i = 0 开始找第一个 `citations[i] >= h`, 就是第一个符合 h-index 规则的paper, return h + +##### 反向思考 +- 如果从 h = n, 每次h--; 那么 `x = n - h` 就是从 `[0 ~ n)` 开始找第一个 `dictations[x] >= h`, 就是结果 +- 同时, `dictations[x-1]` 就是最后一个(dictation最多的)其余的paper. + +#### Bucket count / Bucket Sort +- O(n) +- Bucket sort的思想(更像是counting sort?): 过一遍 input, 把dictation value 作为 index, 分布在bucket[index]上++ +- bucket[x] 是 count when # of citation == x. +- 如果 x 大于 n的时候, 就超出了index范围, 但是刚好这个问题可以包容, 把这样的情况记位在bucket[n]就可以 +- 巧妙: `sum += bucket[h]` where `h = [n ~ 0]` 利用了h-index的definition: +- #of papers (sum of bucket[n]...bucket[0]) has more than h cidations +- 这里运用到了bucket sort的思想, 但是并不是sorting, 而h-index的定义运用的很巧妙. +- Read more about actual bucket sort: https://en.wikipedia.org/wiki/Bucket_sort + + + +--- + +**42. [Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)** Level: Hard Tags: [Greedy, Hash Table, Heap] + + +给一个string, 全是lowercase letter, 要求重新排列: 然后每个unique的character要有k distance apart. + +跟Task Scheduler有点像, 只不过Task那道题里面还可以用其他方法求count, 这道题要求出排列结果 + +#### PriorityQueue + HashTable +- PriorityQueue排序+分布排列的一个经典用法. +- Count frequency and store in pq. +- Consume element of pq for k rounds, each time pick one element from queue. +- Exception: if k still has content but queue is consumed: cannot complete valid string, return ""; +- space, O(n) extra space in sb, O(26) constant space with pq. +- time: O(n) to add all items + + + +--- + +**43. [Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Anagrams.java)** Level: Medium Tags: [Array, Hash Table] + + +把anagram找到并output + +#### HashMap +- 存在int[26], Arrays.toString(arr) 就是 string key: character frequency map +- anagram都有一样的key, 存进hashmap +- output anagrams + +#### HashMap + Sort +- HashMap 的做法. sort每个string, 存进HashMap, 重复的就是anagrams,最后输出。 +- toCharArray +- Arrays.sort +- Stirng.valueOf(char[]) +- 时间n*L*O(logL),L是最长string的长度。 + +#### Previous Notes +- Arrays.toString(arr)的做法。arr是int[26], assuming only have 26 lowercase letters. +- Count occurrance, 然后convert to String,作为map的key. +- Time complexity: nO(L) +- 另一种做法:http://www.jiuzhang.com/solutions/anagrams/ +- 1. take each string, count the occurrance of the 26 letters. save in int[]count. +- 2. hash the int[] count and output a unique hash value; hash = hash * a + num; a = a * b. +- 3. save to hashmap in the same way as we do. +- 这一步把for s: strs 里面的时间复杂度降到了O(L). L = s.length(). +- Need to work on the getHash() function. +- 时间变成n*O(L). Better. + + + + +--- + +**44. [Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)** Level: Medium Tags: [DFS, Hash Table, Tree] + + +给一串3-digit 的数组. 每个数字的表达一个TreeNode, 3 digit分别代表: depth.position.value + +这串数字已经从小到大排列. 求: 所有可能的 root->leaf path 的所有可能的 path sum 总和. + +#### DFS, Hash Table +- 因为`前两个digit可以uniquely identify`一个node, 所以可以把前两个digit作为key, 定位node. +- 特点: 比如考虑root, 有 n 个leaf, 就会加 n 遍root, 因为有 n 个 unique path嘛. +- 实现: 每个node, 上来先把curr value加进sum; 只要有child, 到这个node位置的以上path sum 就要被重加一次. +- format: depth.position.value. (on same level, position may not be continuous) +- approach: map each number into: , and dfs. +- Start from dfs(map, rootKey, sum): +- 1. add node value to sum +- 2. compute potential child. +- 3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +- 4. also, if child exist, dfs into next level +- Space, time O(n) + + + +--- + +**45. [Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Words.java)** Level: Easy Tags: [Hash Table, String] + + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**46. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**47. [Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- true/false problem, think about dp +- 子问题: 前i个字母, 是否可以有valid break +- 检查dp[j] && `if substring(j, i) valid`, for all j = [0 ~ i] +- dp = new boolean[n + 1]; dp[0] = true; +- goal: if there is a j, `dp[j] == true && word[j, n] in dict`. Need iterate over i = [0 ~ n], also j = [0, i] +- 注意, 用set代替list, 因为要用 contains(). + +#### Previous notes +##### 方法2(attempt4 code) +- 与Word BreakII用同样的DP。 +- valid[i]: 记录从i到valid array末尾是否valid. + +##### 方法1:(attempt3 code) +- state,rst[i]: 从[0~i] inclusive的string是否可以在dict中break开来找到? +- function: rst[i] = true if (rst[i - j] && set.contains(s.substring(i - j, i))); j in[0~i] +- 1. rst[i - j] 记录的是[0, i-j]这一段是否可以break后在dict找到。 +- 2. 若true,再加上剩下所有[i-j, i]都能在dict找到,那么rst[i] = rst[0, i - j] && rst[i-j, i] == true +- 优化:找dict里面最长string, 限制j的增大。 + + + + +--- + +**48. [Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/Sparse%20Matrix%20Multiplication.java)** Level: Medium Tags: [Hash Table] + +time: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` +space: O(1) extra + +给两个matrics, 做乘积. 注意, 是sparse matrix (特点: 很多0). + +#### Hash Table +- Recall matric multiplication rules: result[i][j] = sum(A-row[i] * B-col[j]) +- `sparse matric: lots positions are zero` +- 平白地写matric multiplication 没有意义, 重点就是optimization: +- `optimization`: for A-zero-row, and B-zero-col, there is no need to calculate, just return 0. +- 1. Find A-zero-rows and store in setA, same for setB +- 2. during multiplication, reduce time complexity. +- Base: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` + +#### Matrices +- 乘法规则: result[i][j] = sum(A-row[i] * B-col[j]) +- A column size == B row size. 并且: 计算顺序是iterate over A column size + + + +--- + +**49. [Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)** Level: Medium Tags: [Hash Table] + +time: O(mn) +space: O(X), X = max wall width + +给一面墙, 每一行是一行bricks. 用一条vertical line 扫描, 会vertically割开brink. 找到割开最少brick的那条线的x index. + +#### Hash Table +- Find the vertical line (x-coordinate of the grid), where most gaps are found. +- Each gap has (x,y) coordinate +- Create `map`, and maintain a max occurance. +- 计算: x-coordinate: `x = 0; x += brick[i] width` +- Eventually: min-crossed bricks = wall.lenght - maxOccurrance + +##### 思想 +- 分析题意, 找到题目的目标 +- 虽然Map自己不能有sort的规律, 但是可以maintain global variable + + + +--- + +**50. [Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)** Level: Medium Tags: [Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +#### Map +- use `Map` to store inline preSum and its index. +- 1. Build presum incline +- 2. Use map to cache current preSum value and its index: `Map` +- 3. Each iteration: calculate possible preSum candidate that prior target sequence. ex: `(preSum - k)` +- 4. Use the calculated preSum candidate to find index +- 5. Use found index to calculate for result. ex: calculate range. + + + +--- + +**51. [Contiguous Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Contiguous%20Array.java)** Level: Medium Tags: [Hash Table] + + +TODO: how aout without chaning the input nums? + + + +--- + +**52. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + +time: O(n) +space: O(n) + +给一串点, 找是否有一个所有点中间的, 跟y-axis平行的中线. + +#### Hash Table +- 1. store in `Map>`, 2. iterate over map, check head,tail against the mid point +- 很好的细节题目: +- 1. 除以2, 需要存double +- 2. (问面试官)可以有重复的点! 所以track `set` +- 3. 处理 left==right时候, 就当做两个点来处理. +- 4. 存进set里面没有sort, 但是最后做check的时候, 需要sort list +- 时间: visit all nodes 两遍, O(n) + + + +--- + +**53. [Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + +time: O(1) avg +space: O(n) + +#### Hash Table +- 用`map 来track value->index`, 用`list track index->value` +- map查看value是否存在 +- list maintain 用来 insert/remove/random operations. +- 特点: 一旦remove, 换到list结尾然后 `list.remove(list.size() - 1)`, 这样remove的cost更低. +- list.remove(object) 应该是要O(logn) 做一个search的. + + + +--- + +**54. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**55. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**56. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**57. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**58. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + +**59. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**60. [Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)** Level: Hard Tags: [DP, Hash Table] + + +Frog jump 的题目稍微需要理解: 每个格子可以 jump k-1, k, k+1 steps, 而k取决于上一步所跳的步数. 默认 0->1 一定是跳了1步. + +注意: int[] stones 里面是stone所在的unit (不是可以跳的步数, 不要理解错). + +#### DP +- 原本想按照corrdiante dp 来做, 但是发现很多问题, 需要track 不同的 possible previous starting spot. +- 根据jiuzhang答案: 按照定义, 用一个 map of > +- 每次在处理一个stone的时候, 都根据他自己的 set of , 来走下三步: k-1, k, or k+1 steps. +- 每次走一步, 查看 stone + step 是否存在; 如果存在, 就加进 next position: `stone+step`的 hash set 里面 + +##### 注意init +- `dp.put(stone, new HashSet<>())` mark 每个stone的存在 +- `dp.get(0).add(0)` init condition, 用来做 dp.put(1, 1) + +##### 思想 +- 最终做下来思考模式, 更像是BFS的模式: starting from (0,0), add all possible ways +- 然后again, try next stone with all possible future ways ... etc + + + +--- + +**61. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**62. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**63. [Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math] + + +根据题意枚举, 再根据规则basic implementation + +#### Alter input + +#### HashTable + Two Pointer + + + +--- + +**64. [First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +方法1: 按照题意, 找到第一个 first index == last index的字母. + +方法2: 用hashmap存字母的index, 有些重复字母的index就会是个list. 找到单一index, 结合成list, sort, return list.get(0) + + + +--- + +**65. [4Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/4Sum.java)** Level: Medium Tags: [Hash Table] + + +#### Based on 2sum +- 1. 利用2Sum的原理,把4Sum分为连个2Sum。左一个pair,右一个pair,每个pair里面放2个数字。 +- 2. 以一个点,i,作为分界口,也要列举出所有i之前的pair,作为基础。 +- 3. 再尝试从所有i+1后面,找合适的2nd pair。 +- Time: O(n^2 * x), where x = # of candidates, still slow +- 可以用HashSet, 可以直接比较list里面每一个元素, 保证set不重复. +- Previous Notes: 在造class Pair时候,要做@override的function: hashCode(), equals(Object d). 平时不太想得起来用。 +- 参见 http://lifexplorer.me/leetcode-3sum-4sum-and-k-sum/ + +#### Based on 3Sum +- 3Sum外面再加一层. 参考3Sum. 时间O(n^3)。 但此方法在k-sum时候,无疑过于费时间. O(n^k) + + + +--- + +**66. [Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Isomorphic%20Strings.java)** Level: Easy Tags: [Hash Table] + + +#### HashMap +- two failture cases: +- same key, value not matching +- two key maps to same value + +#### Previous note +1. Match. 就是map.containsKey, map.containsValue, and char1 == char2. Perfect. +2. Either Key not exist, or Value not exit. False; +3. Both key and Value exist, but map.get(char1) != char2. Miss-match. False. +4. None of Key or Value exist in HashMap. Then add the match. + + + +--- + +**67. [Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack? + + + +--- + +**68. [Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)** Level: Medium Tags: [Hash Table, String] + + + +#### Convert to orginal string +- shit by offset. `int offset = s.charAt(0) - 'a';` +- increase if less than 'a': `if (newChar < 'a') newChar += 26;` + +#### Previous notes +- 相同shift规则的string, 能被推算到同一个零起始点,就是共同减去一个char,最后就相等。以此作为key,用HashMap。一目了然。 +- 记得根据题目意思,一开始要String[] sort一下。 + + + +--- + + + + + + + +## Backtracking (32) +**0. [Gray Code.java](https://github.com/awangdev/LintCode/blob/master/Java/Gray%20Code.java)** Level: Medium Tags: [Backtracking] + + +TODO: +1. backtracking, using set to perform contains() +2. BFS: use queue to keep the mutations + +题目蛋疼,目前只接受一种结果。 + +BackTracking + DFS: + Recursive helper里每次flip一个 自己/左边/右边. Flip过后还要恢复原样.遍历所有. + +曾用法(未仔细验证): +基本想法就是从一个点开始往一个方向走,每次flip一个bit, 碰壁的时候就回头走。 + + + +--- + +**1. [Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)** Level: Medium Tags: [Backtracking, Permutation] + + +TODO: need to review permutation + +permutation的综合题: +1. validate Input 是不是可以做palindromic permutation. 这个就是(Palindrome Permutation I) +2. 顺便存一下permutation string的前半部分和中间的single character(if any) +3. DFS 做unique permutation: given input有duplicate characters。 + + + +--- + +**2. [Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)** Level: Medium Tags: [Backtracking, Math] + + +TODO: what about regular DFS/backtracking to compute the kth? dfs(rst, list, candiate list, k) + +k是permutation的一个数位。而permutation是有规律的。 + +也就是说,可以根据k的大小来判断每一个数位的字符(从最大数位开始,因为默认factorio从最大数位开始变化)。 + +于是先求出n!, 然后 k/n!就可以推算出当下这一个数位的字符。然后分别把factorio 和 k减小。 + +另外, 用一个boolean[] visited来确保每个数字只出现一次。 + +这个方法比计算出每个permutation要efficient许多。 + + + + +--- + +**3. [Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)** Level: Medium Tags: [Backtracking, String] + + +方法1: Iterative with BFS using queue. + +方法2: Recursively adding chars per digit + + + +--- + +**4. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**5. [Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)** Level: Easy Tags: [Backtracking, Binary Tree, DFS] + + +给一个binary tree, 返回所有root-to-leaf path + +#### DFS, backtracking +- Find all paths, bfs/dfs all works. dfs will be simplier to write +- Recursive:分叉. dfs. +- top->bottom: enumerate current node into the list, carry to next level, and backtrack +- top->bottom is trivial to consider: path flows from top->bottom + +#### DFS, bottom->up +- We can also take current node.left or node.right to generate list of results from the subproblem +- let dfs return list of string candidates, and we can run pair the list with currenet node, once they come back. +- TODO: can write code to practice + +#### Iterative +- Iterative, 非递归练习了一下 +- 因为要每次切短list, 所以再加了一个Stack 来存level +- 单这道题用dfs更简单, 因为找的就是从头到尾的path, 是dfs的pattern + + + + +--- + +**6. [Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +Trie结构, prefix tree的变形: '.'可以代替任何字符,那么就要iterate这个node所有的children. + +节点里面有char, isEnd, HashMap +Build trie = Insert word:没node就加,有node就移动。 +Search word:没有node就报错. 到结尾return true + +这题因为'.'可以代替任何possible的字符,没一种都是一个新的path,所以recursive做比较好些。 +(iterative就要queue了,麻烦点) + + + +--- + +**7. [Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)** Level: Hard Tags: [Backtracking, DFS, Trie] + + +给一串words, 还有一个2D character matrix. 找到所有可以形成的words. 条件: 2D matrix 只可以相邻走位. + +#### Trie, DFS +- 相比之前的implementation, 有一些地方可以优化: +- 1. Backtracking时候, 在board[][] 上面mark就可以, 不需要开一个visited[][] +- 2. 不需要implement trie的所有方程, 用不到: 这里只需要insert. +- 普通的trie题目会让你search a word, 但是这里是用一个board, 看board的每一个字母能不能走出个Word. +- 也就是: 这里的search是自己手动写, 不是传统的trie search() funcombination +- 3. TrieNode里面存在 end的时候存string word, 表示到底. 用完了 word = null, 刚好截断重复查找的问题. + +##### 关于Trie +- Build Trie with target words: insert, search, startWith. Sometimes, just: `buildTree(words)` and return root. +- 依然要对board matrix做DFS。 +- no for loop on words. 直接对board DFS: +- 每一层,都会有个up-to-this-point的string. 在Trie里面check它是不是存在。以此判断。 +- 若不存在,就不必继续DFS下去了。 +- Trie solution time complexity, much better: +- build Trie: n * wordMaxLength +- search: boardWidth * boardHeight * (4^wordMaxLength + wordMaxLength[Trie Search]) + + +#### Regular DFS +- for loop on words: inside, do board DFS based on each word. +- Time cpmplexity: word[].length * boardWidth * boardHeight * (4^wordMaxLength) + +#### Previous Notes +- Big improvement: use boolean visited on TrieNode! +- 不要用rst.contains(...), 因为这个是O(n) 在leetcode还是会timeout(lintcode竟然可以pass)! +- 在Trie search() method 里面,凡是visit过的,mark一下。 + + + + +--- + +**8. [Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)** Level: Medium Tags: [Array, Backtracking, DFS] + + +#### DFS, Backtracking: +- 找到开头的字母, 然后recursively DFS 去把word串到底: +- 每到一个字母, 朝四个方向走, 之中一个true就可以. +- Note:每次到一个字母,mark一下'#'. 4个path recurse回来后,mark it back. + +#### Note: other ways of marking visited: +- 用一个boolean visited[][] +- Use hash map, key = x@y + + + + +--- + +**9. [Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)** Level: Hard Tags: [Backtracking, Trie] + + +可以开Trie class, 里面用到TrieNode. 开Trie(words) 可以直接initalize with for loop +TrieNode 里面可以有一个 List startWith: 记录可以到达这个点的所有string: 有点像树形, ancestor形状的存储. + +神操作: +根据square的性质, 如果选中了list of words, 设定 int prefixIndex = list.size(). +取出list里面的所有word[prefixedIndex], 并且加在一起, 就是下一个word candidate的 prefix. + +形象一点: +list = ["ball", "area"]; +prefixIndex = list.size(); ball[prefixIndex] = 'l'; area[prefixIndex] = 'e'; +//then +candidatePrefix = ball[prefixIndex] + area[prefixIndex] = "le"; +这里就可以用到Trie的那个 findByPrefix function, 在每个点, 都存有所有这个点能产生的candidate. +这时, 试一试所有candidate: dfs + +能想到这种倒转的结构来存prefix candidates 在 Trie里面, 这个想法非常值得思考. + + + +--- + +**10. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**11. [Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations%20II.java)** Level: Medium Tags: [Backtracking] + + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +#### Backtracking +- 排序, +- Mark visited. 通过permutation规律查看是否排出了重复结果 +- 并且要检查上一层recursive时有没有略过重复element +- time O(n!) + +##### 背景1 +- 在recursive call里面有for loop, 每次从i=0开始, 试着在当下list上加上nums里面的每一个。 +- 从i=0开始,所以会依次recursive每一个nums: +- 因此,例如i=2,肯定比i=3先被访问。也就是:取i=2的那个list permutation肯定先排出来。 + +##### 背景2 +- 重复的例子:给出Input[x, y1, y2], 假设y的值是一样的。那么,{x,y1,y2}和{x,y2,y1}是相同结果。 + +##### Note +- 综上,y1肯定比y2先被访问,{x,y1,y2}先出。 紧随其后,在另一个recursive循环里,{x,y2...}y2被先访问,跳过了y1。 +- 重点:规律在此,如果跳过y1,也就是visited[y1] == false, 而num[y2] == num[y1],那么这就是一个重复的结果,没必要做,越过。 +- 结果:那么,我们需要input像{x,y1,y2}这样数值放一起,那么必须排序。 + +#### Non-recursive, manuall swap +- Idea from: https://www.sigmainfy.com/blog/leetcode-permutations-i-and-ii.html +- 用到 sublist sort +- 用 swap function, 在原数组上调节出来新的permutation +- 注意: 每次拿到新的candidate, 都要把没有permutate的数位sort, 然后再开始swap. +- 这是为了确保, [j]和[j-1]在重复时候, 不用重新记录. + +#### Queue +- 给一个visited queue +- 和queue在所有的地方一同populate. +- 然后visited里面存得时visited indexes。 (Not efficient code. check again) + + + +--- + +**12. [N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens.java)** Level: Hard Tags: [Backtracking] + + +N-Queen 问题, 给数字n, 和 nxn board, 找到所有N-queens的答案. + +#### Backtracking +- 用dfs找所有情况, 每一个iteration, 从找一行里挑合适的点, dfs +- 选中的点加进candidate list 里面, 记得要backtracking. +- 每一个candidate都需要validation, 检查 row, col, 2 diagnal 有没有queen + +#### validate n queue at certain (x, y) +- 1. array 里面不能有 target row# +- 2. diagnal. 记得公式: +- row1 - row2 == col1 - col2. Diagnal elelment.fail +- row1 - row2 == - (col1 - col2). Diagnal element. fail +- Draw a 3x3 board to test the 2 scanarios: +- (0,0) and (3,3) are diagnal +- (0,2) and (2,0) are diagnal + + + + +--- + +**13. [N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens%20II.java)** Level: Hard Tags: [Backtracking] + + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + + + +--- + +**14. [Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)** Level: Easy Tags: [Backtracking, DFS, Tree] + + +给一个inputSum, 然后dfs, 找到所有path, 满足: path sum 跟 inputSum 一样. + +#### DFS, Backtracking +- 用remaining sum 来检测是否满足 input path sum 条件 +- 满足的时候add to result list +- 两种backtracking: +- 1. backtrack 当下node, 加进list, 然后dfs. dfs结束后删掉之前加进去的元素. 非常clean. +- 2. backtrack 下一个dfs level增加的value. dfs return 之后, 删掉list里面的末尾元素: 但是删掉的dfs余下的value. +- 第一种backtrack更加好掌握. + +#### Previous Notes: +- Binary Tree的一个基本题: 找到所有满足条件的path +- 遍历到底,比较sum vs. target +- 注意divide的情况。要把遍历的例子写写 + + + +--- + +**15. [Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)** Level: Medium Tags: [Backtracking, Combination, DFS] + + +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +#### DFS, Backtracking +- for loop, recursive (dfs). +- 每个item用一次, 下一个level dfs(index + 1) +- Combination DFS. 画个图想想. 每次从1~n里面pick一个数字i +- 因为下一层不能重新回去 [0~i]选,所以下一层recursive要从i+1开始选。 + + + +--- + +**16. [Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)** Level: Medium Tags: [Backtracking, DFS, String] + + +给一串数字, 检查是否是valid IP, 如果合理, 给出所有valid 的IP组合方式. + +#### Backtracking +- 递归的终点:list.zie() == 3, 解决最后一段 +- 递归在一个index上面, pass s.toCharArray() +- validate string要注意leading '0' +- 注意: 递归的时候可以用一个start/level/index来跑路 +- 但是尽量不要去改变Input source, 会变得非常confusing. +- note: code有点messy, 因为要考虑IP的valid情况 +- 那个'remainValid', 其实是一个对于remain substring的判断优化, 不成立的就不dfs了 + + + +--- + +**17. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**18. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**19. [Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)** Level: Medium Tags: [Backtracking, DFS, DP] + + +String 只包含 + , - 两个符号. 两个人轮流把consecutive连续的`++`, 翻转成 `--`. + +如果其中一个人再无法翻转了, 另一个人就赢. 求: 给出string, 先手是否能赢. + +#### Backtracking +- curr player 每走一步, 就生成一种新的局面, dfs on this +- 等到dfs结束, 不论成功与否, 都要backtracking +- curr level: 把"++" 改成 "--"; backtrack的时候, 改回 '--' +- 换成boolean[] 比 string/stringBuilder要快很多, 因为不需要重新生成string. +- ++ 可以走 (n - 1)个位置: +- T(N) = (N - 2) * T(N - 2) = (N - 4) * (N - 2) * T(N - 4) ... = O(N!) + +##### iterate based on "++" +- 做一个String s的 replica: string or stringBuilder +- 每次dfs后, 然后更替里面的字符 "+" => "-" +- 目的只是Mark已经用过的index +- 真正的dfs 还是在 original input string s 身上展开 +- 每次都重新生成substring, 并不是很efficient + +##### Game theory +- 保证p1能胜利,就必须保持所有p2的move都不能赢 +- 或者说, 在知道棋的所有情况时, 只要p2有一种路子会输, p1就一定能走对路能赢. +- 同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +- p1: player1, p2: player2 + +#### O(N^2) 的 DP +- 需要Game Theory的功底, Nim game. https://www.jiuzhang.com/qa/941/ +- http://www.1point3acres.com/bbs/thread-137953-1-1.html +- TODO: https://leetcode.com/problems/flip-game-ii/discuss/73954/Theory-matters-from-Backtracking(128ms)-to-DP-(0ms) + + + +--- + +**20. [Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning.java)** Level: Medium Tags: [Backtracking, DFS] + + +给个string s, partition(分段)后, 要确保每个partition都是palindrome. + +求所有partition palindrome组合. `list>` + +#### DFS +- 可以top->bottom: 遍历str, validate substring(start, i); if valid, add as candidate, and dfs; backtrack by remove candidate. +- 也可以bottom->up: 遍历str, validate substring(start, i); if valid, dfs(remaining str), return list of suffix; cross match with curr candidate. + +#### DFS Top->Bottom +- 在遍历str的时候,考虑从每个curr spot 到 str 结尾,是能有多少种palindorme? +- 那就从curr spot当个字符开始算,开始backtracking. +- 如果所选不是palindrome, 那move on. +- 若所选的确是palindrome, 加到path里面,DFS去下个level,等遍历到了结尾,这就产生了一种分割成palindrome的串。 +- 每次DFS结尾,要把这一层加的所选palindrome删掉,backtracking嘛 + +#### Optimization +- 可以再每一个dfs level 算 isPalindrome(S), 但是可以先把 boolean[][] isPalin 算出来, 每次O(1) 来用 +- 注意: isPalin[i][j] 是 inclusive的, 所以用的时候要认准坐标 +- Calculate isPalin[i][j]: pick mid point [0 ~ n] +- expand and validate palindrome at these indexes: `[mid, mid+1]` or `[mid-1][mid+1]` + +#### Complexity +- Overall Space O(n^2): 存 isPlain[][] +- Time O(2^n), 每一层都在做 pick/not pick index i 的选择, 所以worst case 2^n. +- 因为我们的isPalin[][]优化了palindrome的判断O(1), 所以overall Time: O(2^n) + + + +--- + +**21. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**22. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**23. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**24. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**25. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + +**26. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**27. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**28. [Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)** Level: Medium Tags: [Backtracking, DFS, Permutation] + + +#### Recursive: Backtracking +- Given a remaining list: 取, 或者不取 +- always iterate over full `nums[]`, use list.contains() to check if item has been added. +- Improvement: maintain list (add/remove elements) instead of 'list.contains' +- time O(n!): visit all possible outcome +- T(n) = n * T(n-1) + O(1) + +#### Iterative: Insertion +- 插入法: +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- 但是比dfs要快, 因该是因为 # of checks 少: 不需要check list.size(), 不需要maintain remaining list. + +#### Previous Notes +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- A bit slower, possibly because of the polling and saving the entire list every time + + + + +--- + +**29. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**30. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + +**31. [Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)** Level: Hard Tags: [Backtracking, DFS] + + +#### DFS +- Different from regular dfs to visit all, the robot move() function need to be called, backtrack needs to move() manually and backtracking path shold not be blocked by visited positions +- IMPORTANT: Mark on the way in using set, but `backtrack directly without re-check against set` +- Mark coordinate 'x@y' +- Backtrack: turn 2 times to revert, move 1 step, and turn 2 times to revert back. +- Direction has to be up, right, down, left. +- `int [] dx = {-1, 0, 1, 0};`, `int[] dy = {0, 1, 0, -1};` + + + +--- + + + + + + + +## Double Recursive (2) +**0. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**1. [Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)** Level: Easy Tags: [DFS, Double Recursive, Tree] + + +count所有存在的 path sum == target sum. 可以从任意点开始. 但是只能parent -> child . + +#### DFS +- 对所给的input sum 做减法, 知道 sum 达到一个目标值截止 +- 因为可以从任意点开始, 所以当sum达标时候, 需要继续recursive, 从而找到所有情况 (有正负数, sum可能继续增加/减少) +- 经典的 helper dfs recursive + self recursive +- 1. helper dfs recursive 处理包括root的情况 +- 2. self recursive 来引领 skip root的情况. + +#### 特点 +- 与 `Binary Tree Longest Consecutive Sequence II` 在recursive的做法上很相似: +- 利用dfs做包括root的recursive computation +- 利用这个function自己, 做`不包括root的recursive computation` + + + +--- + + + + + + + +## Combination (4) +**0. [Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)** Level: Medium Tags: [Backtracking, Combination, DFS] + + +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +#### DFS, Backtracking +- for loop, recursive (dfs). +- 每个item用一次, 下一个level dfs(index + 1) +- Combination DFS. 画个图想想. 每次从1~n里面pick一个数字i +- 因为下一层不能重新回去 [0~i]选,所以下一层recursive要从i+1开始选。 + + + +--- + +**1. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**2. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**3. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + + + + + + + +## TreeSet (2) +**0. [K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)** Level: Hard Tags: [Array, BST, TreeSet] + + +题目解析后: find 2 number, that: 1. k slots between the 2 number, 2. no slots taken between the two number. + +#### BST +- BST structure not given, use TreeSet to build BST with each node +- Every time find last/next inorder element +- `treeSet.lower(x)`, `treeSet.higher(x)` +- 一旦位置相隔(k + 1), 就满足题目条件 +- O(nlogn), good enough + +#### Track slots of days +- Reverse the array, save days index into days[], where the new index is slot. +- days[i]: at slot i, which day a flower will be planted +- O(n) +- Needs to understand: http://www.cnblogs.com/grandyang/p/8415880.html + + + +--- + +**1. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + + + + + + + +## Sort (22) +**0. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + + +--- + +**1. [Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)** Level: Medium Tags: [Array, Sort] + + +方法1: +排序, nLog(n). 然后把直线上坡变成层叠山峰, 需要每隔几个(题目中是每隔2位)就做个swap 造成高低不平. +Note: 每隔山峰之间是相互没有关系的, 所以每次只要操心 [i], [i-1]两个位置就好了. + +方法2: +O(n) +看好奇数偶数位的规律, 然后根据题目给出的规律, 跑一遍, 每次只关注两个位置: 把不合适的[i], [i-1]调换位置就好了. + +方法3: +跟法2一样, 只是更巧妙一点罢了: +第一遍想太多. 其实做一个fall-through就能把问题解决,原因是因为: +这样的fall-through每次在乎两个element,可以一口气搞定,无关乎再之前的elements。 +特别的一点:flag来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**2. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**3. [Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +HashMap + + + +--- + +**4. [Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### 方法1: +找是否有overlap. priorityQueue 按照start time排序好以后, 比较current和peek: current.end > peek.start? + +#### 方法2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 + + + +--- + +**5. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**6. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**7. [Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)** Level: Medium Tags: [Linked List, Sort] + + +input一串数字, 需要出sorted output. 每次insert一个数字时, 都要放到正确的sorted的位置 + +每次insertion的时候, 都从input里面减掉这个数字 + +#### Linked List +- 把list里面每个元素都拿出来,scan and insert一遍 +- Time O(n^2), worst case, 每次放入n个数字里面的element, 刚好都是最大的 +- 所以每次要traverse n nodes, 然后走n次 + +##### 思考方法 +- 如果已经有个sorted list, insert一个element进去。怎么做? +- while 里面每个元素都小于 curr, keep going +- 一旦curr在某个点小了,加进去当下这个空隙。 + + + +--- + +**8. [Largest Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number.java)** Level: Medium Tags: [Sort] + + +给一串数字, 非负数, 把所有数字串联起来, 组成最大数字. + +因为结果很大, 所以用string表示 + +#### Sort, Comparator +- 考虑 more significant spot 应该拿到更大的值 +- 如果sort number, comparator 会比较难写: 每个digit的weight不同, 要分别讨论个位数和多位数. +- goal: 让较大的组合数排在前面, 让较小的组合数排在后面 +- 不如: 组合两种情况, 用String比较一下大小 (也可以用 integer来比较组合数, 但是为保险不超Integer.MAX_VALUE, 这里比较String) +- String.compareTo() 是按照 lexicographically, 字典顺序排列的 +- 利用compareTo, 来倒序排列 string, 刚好就得到我们要的结果. +- O(nlogn), 排序 + + + +--- + +**9. [QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)** Level: Medium Tags: [Quick Sort, Sort] + + +implement quick sort. + +#### Quick Sort +- 首先partition. 返还一个partition的那个中间点的位置: 这个时候, 所有小于nums[partitionIndex] 都应该在 partitionIndex左边 +- 然后劈开两半 +- 前后各自 quick sort, recursively +- 注意:在partition里面, 比较的时候nums[start] < pivot, nums[end]>pivot, 如果写成了 <= 会 stack overflow. +- Time O(nlogn), Space: O(1) + + + +--- + +**10. [MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/MergeSort.java)** Level: Medium Tags: [Merge Sort, Sort] + + +#### Merge Sort +- Divide and conquer, recursively +- 先从中间分段, merge sort 左边 (dfs), merge sort 右边 +- 最后merge起来 +- merge的时候因为是做int[], 所以没办法必须要O(n) space +- Time O(nlogn), Space O(n) + + + +--- + +**11. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**12. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**13. [Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort] + + +#### Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn) + + +#### Basic Implementation +- 这里已经给了 **sorted** intervals by start point. +- 直接找到可以insert newInterval的位子. Insert +- 然后loop to merge entire interval array +- 因为给的是个list, 所以方便`intervals.remove(i)` +- remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture +- O(n) + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**14. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + +**15. [H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)** Level: Medium Tags: [Bucket Sort, Hash Table, Sort] + + +找到h-index, 给的citation int[] 并不是sorted. h-index 的definition 具体看题目. + +#### Sort, find h from end +- 例子写出来,发现可以sort以后按照定义搜索一遍。 nlogn. +- 搜索一遍时候可以优化,用binary search. 但是没意义,因为array.sort已经用了nlogn +- 题目给的规则, 从小到大排序后: 剩下的 paper `n-h`, 全部要 <= h 个 citation. +- time O(nlogn), search O(n) + +##### 正向思考 +- 从i = 0 开始找第一个 `citations[i] >= h`, 就是第一个符合 h-index 规则的paper, return h + +##### 反向思考 +- 如果从 h = n, 每次h--; 那么 `x = n - h` 就是从 `[0 ~ n)` 开始找第一个 `dictations[x] >= h`, 就是结果 +- 同时, `dictations[x-1]` 就是最后一个(dictation最多的)其余的paper. + +#### Bucket count / Bucket Sort +- O(n) +- Bucket sort的思想(更像是counting sort?): 过一遍 input, 把dictation value 作为 index, 分布在bucket[index]上++ +- bucket[x] 是 count when # of citation == x. +- 如果 x 大于 n的时候, 就超出了index范围, 但是刚好这个问题可以包容, 把这样的情况记位在bucket[n]就可以 +- 巧妙: `sum += bucket[h]` where `h = [n ~ 0]` 利用了h-index的definition: +- #of papers (sum of bucket[n]...bucket[0]) has more than h cidations +- 这里运用到了bucket sort的思想, 但是并不是sorting, 而h-index的定义运用的很巧妙. +- Read more about actual bucket sort: https://en.wikipedia.org/wiki/Bucket_sort + + + +--- + +**16. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**17. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**18. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + +**19. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**20. [Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort] + + +#### PriorityQueue +- Use priority queue to sort by customized class interval{int dist; int x, y;}. +- Sort by larger distance and then sort by start index +- seat(): pq.poll() to find interval of largest distance. Split and add new intervals back to queue. +- leave(x): one seat will be in 2 intervals: remove both from pq, and merge to a new interval. +- 主方程写出来其实很好写, 就是 split + add interval, 然后 find + delete interval 而已. 最难的是构建data structure +- seat(): O(logn), leave(): O(n) + +##### Trick: 构建虚拟 boundary +- 如果是开头的seat, 或者是结尾的seat, 比较难handle: 一开始坐在seat=0的时候, 没有interval啊! +- Trick就是, 我们自己定义个虚拟的座位 `seat=-1`, `seat=N` +- 一开始有一个 interval[-1, N] 然后就建立了boundary. +- 从此以后, 每次split成小interval的时候: +- 遇到 `interval[-1, y]`, distance就是 `(y - 0)` +- 遇到 `interval[x, N]`, distance就是 `(N - 1 - x)` +- 当然正常的interval dist 就是 `(y - x) / 2` + +##### distance 中间点 +- Interval.dist 我们其实做的是 distance的中间点 `(y - x) / 2` +- 这里的dist是 `距离两边的距离` 而不是 x, y 之间的距离. 这里要特别注意. + +#### TreeSet +- https://leetcode.com/problems/exam-room/discuss/139885/Java-Solution-based-on-treeset/153588 + +#### Map +- how? +- TODO, not sure. + + + +--- + +**21. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + + + + + + + +## Tree (55) +**0. [Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DP, Tree] + + +Not quite clear. +根据左右分割而总结出了原理, 每次分割, 左右两边都会有一定数量的permutation, 总体上的情况数量当然是相乘. +然后每一个不同的分割点都加一遍: +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +然后把数学公式转换成DP的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**1. [Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, Tree] + + +方法1: +适合复习BST. 用DFS对待每个node. 注意BST的特征: 所有left nodes都小于当下node, 所有right nodes都大于当下node. + +根据题意用[L,R]切割.如果node.val index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**6. [Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Balanced%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个binary tree, 看是否是height-balanced + +#### DFS +- DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. +- 一旦有 <0 或者差值大于1, 就全部返回Integer.MIN_VALUE. Integer.MIN_VALUE比较极端, 确保结果的正确性。 +- 最后比较返回结果是不是<0. 是<0,那就false. +- Traverse 整个tree, O(n) + + +#### DFS, maxDepth function +- Same concept as in 1, but cost more traversal efforts. + + + +--- + +**7. [Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +给一个特殊的binary tree, treeNode里面有一个 next pointer. + +写一个function, 把所有node都更同level的node 连在一起. 最右边的node.next = NULL + +#### DFS + Divide and Conquer +- 题目要求DFS. 想清楚了如何在DFS level把几种情况都考虑了, 写起来很简单. NOT BFS, because requires O(1) space +- 对于一个root来说, 只有几个点可以顾忌到: root.left, root.right, root.next. +- 想办法把这三个方向的点, 能连起来的都连起来: +- 1. `node.left.next = node.right` +- 2. If `node.next != null`, link `node.right.next = node.next.left`; +- 然后在dfs(root.left), dfs(root.right) +- Time: visit && connect all nodes, O(n) + +#### BFS +- 不和题意,用了queue space,与Input成正比。太大。 +- BFS over Tree。 用Queue 和 queue.size(),老规矩。 +- process每层queue时, 注意把next pointer加上去就好. + + + +--- + +**8. [Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +如题, 验证是否是BST. + +#### DFS +- 查看每个parent-child关系: leftchild < root < rightChild; +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest +- imagine we know the two extreme border: Integer.MIN_VALUE, Integer.MAX_VALUE; pass node around and compare node vs. node.parent. +- 方法: 把root.val 传下来作为 max 或者 min, 然后检查children +- + +##### Note: +- min/max需要时long type. +- 如果题目真的给node.val = Integer.MAX_VALUE, 我们需要能够与之比较, long就可以. + + + +--- + +**9. [Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)** Level: Medium Tags: [Stack, Tree] + + +给一串数字, 做一个 maximum binary tree: 最顶上的root最大; 左child也是一个max tree, 右child也必须是max tree. + +#### Monotonous Stack +- 用到bottom->top递减的stack: 最底下的root维持成最大的element. +- 过程当中, 一旦遇到currNode.val > stack.peek(), 就意味着需要把这个currNode放在 stack的底层位置. +- 也就是说, 遇到这个条件, process, pop()所有 currNode.val > stack.peek(), 最后把currNode加进去. + +- maxTree题目本身的要求是: 大的在最中间, 左右两边的subTree也要是maxTree: +- Monotonous Stack在这里帮助 keep/track of max value, 但是left/right tree的logic是MaxTree独有的. +- left/right node的assignment是根据题目要求: 中间最大值分开后, 左边的是左边subTree, 右边的作为右边subTree. + +#### Previous notes +- Should memorize MaxTree. 依次类推,会做Min-Tree, Expression Tree +- Stack里,最大的值在下面。利用此性质,有这样几个step: + +##### Step1 +- 把所有小于curr node的,全Pop出来, while loop, keep it going. +- 最后pop出的这个小于Curr的node:它同时也是stack里面pop出来小于curr的最大的一个,最接近curr大小。(因为这个stack最大值靠下面) +- 把这个最大的小于curr的node放在curr.left. + +##### Step2 +- 那么,接下去stack里面的一定是大于curr: +- 那就变成curr的left parent. set stack.peek().right = curr. + +##### Step3 +- 结尾:stack底部一定是最大的那个,也就是max tree的头。 + + + + + +--- + +**10. [Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Recursive +- 在自己的基础上recursive, 不用helper function +- Divide and Conquer, with helper(dfs) method +- O(n) time, no extra space + +#### Iterative: Stack +- Add left nodes all the way +- Print curr +- Move to right, add right if possible +- O(n) time, O(h) space + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. + +若不右移, 很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + +#### HashMap +? How? + + + +--- + +**11. [Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Recursive +trivial, 先加left recursively, 再加right recursively, 然后组成头部. + +#### Stack +- 双stack的思想, 需要在图纸上画一画 +- 原本需要的顺序是: 先leftChild, rightChild, currNode. +- 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild +- 这样出来的结果是reverse的, 那么翻转一下就可以了. +- v1做的时候用了stack1, stack2, 因为根据这个双stack的思想而来 +- v2简化, 可以放在一个stack里面, 每次record result 的时候: rst.add(0, item); + +##### 利用stack的特点 +- 每次加element进stack的时候, 想要在 bottom/后process的, 先加 +- 想要下一轮立刻process的, 最后push进stack. + +##### 注意 +这些binary tree traversal的题目.常常有多个做法:recursive or iterative + + + +--- + +**12. [Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +#### Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, +- 找到 node.val == target, 或者走位走完, return closest + + + +--- + +**13. [Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样, 如果一样, 直接 2 ^ h - 1就好 +- 不一样的话, 再DFS + +##### Trick +- 直接DFS会timeout, O(n), 其实可以optimize +- to pass the test with O(h^2), 位运算: Math.pow(2, h) = 2 << (h - 1). 神奇! +- 2 << 1就是把所有bits往左移动一位, 也就是 * 2 + +#### Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + + + +--- + +**14. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**15. [Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +A complete binary tree is a binary tree in which every level, except possibly the last, + +is completely filled, and all nodes are as far left as possible + +#### BFS +- 当出现了第一次有 null children的node的时候, 说明到了leaf level, mark flag = true; +- 自此以后,queue再不该有node再有child; queue后面出现的node的left/right child应该都是null +- 否则就是有问题, return false; + + + + +--- + +**16. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**17. [Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### DFS +- 简单处理swap +- recursively swap children + +#### BFS +- BFS with Queue +- 每次process一个node, swap children; 然后把child加进queue里面 +- 直到queue process完 + + + +--- + +**18. [Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree, 找最深depth + +#### DFS +- 这里要走过所有的node, 所以dfs非常合适 +- Divide and conquer. +- 维持一个最大值: Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +- 注意check root == null + +#### Note +- BFS is doable as well, but a bit more code to write: tracks largest level we reach + + + +--- + +**19. [Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### BFS +- Shortest path; minimum depth: 想到BFS, check level by level, BFS更能确保更快找到结果 +- depth definition: reach to a leaf node, where node.left == null && node.right == null +- BFS using queue, track level. + + +#### DFS +- Divide and Conquery一个最小值. +- 注意处理Leaf的null: null leaf 出现的时候, 就忽略这个leaf, 直接return算有leaf +- 另一种count的方法: 用Integer.MAX_VALUE代替 null leaf,这样可以避免错误counting. (不能直接recursive) +- 这个无论如何都要走所有node, 所以dfs应该比较适合. + + + + +--- + +**20. [Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**21. [Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +检查binary tree是否 identical. + +特点: subtree如果是有旋转的, 只要tree node value相等, 就可以算是identical + +#### DFS +- 在DFS的基础上, 比对左左,左右,右左,右右 + + + +--- + +**22. [Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)** Level: Easy Tags: [DFS, Tree] + + +#### DFS +- 基础binary tree traversal. 注意对null child的判断 + + + +--- + +**23. [Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree s, 和一个binary tree t, 检查t是不是s的subtree. + +#### DFS +- 跟 identical binary tree的写法很像 +- 只有 current s.val = t.val 的时候才需要compare same tree. +- 其他情况, 继续recursively isSubtree +- 注意:即使找到T1 == T2, 但很可能只是数字相同(这里不是binary search tree!!), 而children不同 +- 所以同时要继续recursively isSubtree(T1.left, T2) ...etc. + + + +--- + +**24. [Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个Binary Tree root, 以及两个node p, q. 找 p 和 q 的 lowest common ancestor + +#### DFS +- 因为是 binary tree, 所以直接盲目搜索搜索path不efficient, use extra space and waste time +- 巧用DFS来找每一个node的common ancestor. +- Need the assumption: 1. unique nodes across tree; 2. must have a solution +- 当root == null或者 p q 任何一个在findLCA底部被找到了(root== A || root == B),那么就return 这个root. +- 三种情况: +- 1. A,B都找到,那么这个level的node就是其中一层的ancestor: 其实,最先recursively return到的那个,就是最底的LCA parent. +- 2. A 或者 B 找到,那就还没有公共parent, return 非null得那个。 +- 3. A B 都null, 那就找错了没有呗, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time/space O(n) + + + +--- + +**25. [Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Tree] + + +给一个Binary Tree root, 以及两个node A, B. 特点: node里面存了parent pointer. 找 lowest common ancestor + + +#### Hash Set +- 这个题有个奇葩的地方, 每个node还有一个parent, 所以可以自底向上. +- save visited in hashset. 第一个duplicate, 就是A B 的 lowest common ancestor + +#### Save in lists +- 自底向上。利用parent往root方向返回 +- 把所有parent存下来, 然后在两个list里面找最后一个 common node + +#### 注意 +- 无法从root去直接搜target node 而做成两个list. 因为根本不是Binary Search Tree! + + + + +--- + +**26. [Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的list +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Brutly寻找p和q的common ancestor, 然后recursively drive left/right +- 非常巧妙, 但是也比较局限; 稍微变条件, 就很难recursive. +- 几种情况: +- 1. one of p, q 在leaf, 那么此时的root其实就是lowest common ancestor +- 2. 如果p, q 在root的左右两边, 这就是分叉口, 那么root就是lowest common ancestor +- 3. 如果p,q 在root的同一边 (左,右), 那么继续dfs +- O(1) extra space, O(logn) time + + + +--- + +**27. [Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**28. [Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)** Level: Medium Tags: [BFS, Tree] + + +如题, 但是output要倒序. + +#### BFS +- 跟Binary Tree Level Order Traversal一样,只不过存result一直存在存在0位. + + +#### DFS +- 根据level来append每个list +- rst里面add(0,...)每次都add在list开头 + + + +--- + +**29. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**30. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + +**31. [Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum.java)** Level: Easy Tags: [DFS, Tree] + + +给一个inputSum, 然后dfs, 找到是否有一条path, 得出的path sum 跟 inputSum 一样. + +#### DFS +- 确定好结尾的条件: is leaf && val == sum +- 每一层减掉node.val, 然后dfs. +- 写一写: root == null => false 对parent nodes的影响. 这里发现没影响, 所以可以简化成用1个functionDFS. + + + + +--- + +**32. [Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)** Level: Easy Tags: [Backtracking, DFS, Tree] + + +给一个inputSum, 然后dfs, 找到所有path, 满足: path sum 跟 inputSum 一样. + +#### DFS, Backtracking +- 用remaining sum 来检测是否满足 input path sum 条件 +- 满足的时候add to result list +- 两种backtracking: +- 1. backtrack 当下node, 加进list, 然后dfs. dfs结束后删掉之前加进去的元素. 非常clean. +- 2. backtrack 下一个dfs level增加的value. dfs return 之后, 删掉list里面的末尾元素: 但是删掉的dfs余下的value. +- 第一种backtrack更加好掌握. + +#### Previous Notes: +- Binary Tree的一个基本题: 找到所有满足条件的path +- 遍历到底,比较sum vs. target +- 注意divide的情况。要把遍历的例子写写 + + + +--- + +**33. [Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)** Level: Easy Tags: [DFS, Double Recursive, Tree] + + +count所有存在的 path sum == target sum. 可以从任意点开始. 但是只能parent -> child . + +#### DFS +- 对所给的input sum 做减法, 知道 sum 达到一个目标值截止 +- 因为可以从任意点开始, 所以当sum达标时候, 需要继续recursive, 从而找到所有情况 (有正负数, sum可能继续增加/减少) +- 经典的 helper dfs recursive + self recursive +- 1. helper dfs recursive 处理包括root的情况 +- 2. self recursive 来引领 skip root的情况. + +#### 特点 +- 与 `Binary Tree Longest Consecutive Sequence II` 在recursive的做法上很相似: +- 利用dfs做包括root的recursive computation +- 利用这个function自己, 做`不包括root的recursive computation` + + + +--- + +**34. [Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右:即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list + +#### DFS +- Use Map 来存每一个level的结果 +- dfs function 里, 如果 input depth 不存在, 就add to map. +- dfs function 里面先: dfs(node.right), 然后 dfs(node.left) +- 由于always depth search on right side, 所以map会被right branch populate; 然后才是 leftChild.right + + + + +--- + +**35. [Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)** Level: Medium Tags: [DFS, Tree] + + +找到从max path sum from root. 条件: 至少有一个node. + +#### DFS +- 比Binary Tree Maximum Path Sum I 简单许多. 因为条件给的更多:at least 1 node + have to start from root +- root一定用到 +- 3种情况: curr node, curr+left, curr+right +- 因为一定包括root, 说以从 `dfs(root, sum=0)` 开始, 每个level先加root, sum += root.val + + + +--- + +**36. [Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +找到binary tree 里的最长 consecutive sequence. + +#### DFS +- Divide and Conquer. dfs +- 分开 看左边/右边 +- 如果左边满足连续递增的规则, dfs (depth + 1), 如果不满足规则, dfs(depth = 1) +- 右边也是一样 +- 对结果跟max作比较, return + + + +--- + +**37. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**38. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**39. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**40. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**41. [Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST, DFS, Tree] + + +BST里面有2个node misplace, 要归为. 要求: O(1) extra space + +#### Observation +- BST inorder traversal should give small -> large sequence +- misplaced means: a **large**->small item would occur, and later a large>**small** would occur. +- The first large && second small item are the 2 candidates. Example +- [1, 5, 7, 10, 12, 15, 18] +- [1, 5, `15, 10`, `12, 7`, 18] + +#### dfs, O(1) extra space +- traverse, and take note of the candidate +- at the end, swap value of the 2 candidates + +#### O(n) space +- inorder traversal the nodes and save in array, find the 2 items misplanced and swap them +- But O(n) space should not be allowed + + + + +--- + +**42. [Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)** Level: Medium Tags: [DFS, Hash Table, Tree] + + +给一串3-digit 的数组. 每个数字的表达一个TreeNode, 3 digit分别代表: depth.position.value + +这串数字已经从小到大排列. 求: 所有可能的 root->leaf path 的所有可能的 path sum 总和. + +#### DFS, Hash Table +- 因为`前两个digit可以uniquely identify`一个node, 所以可以把前两个digit作为key, 定位node. +- 特点: 比如考虑root, 有 n 个leaf, 就会加 n 遍root, 因为有 n 个 unique path嘛. +- 实现: 每个node, 上来先把curr value加进sum; 只要有child, 到这个node位置的以上path sum 就要被重加一次. +- format: depth.position.value. (on same level, position may not be continuous) +- approach: map each number into: , and dfs. +- Start from dfs(map, rootKey, sum): +- 1. add node value to sum +- 2. compute potential child. +- 3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +- 4. also, if child exist, dfs into next level +- Space, time O(n) + + + +--- + +**43. [Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + +time: O(n) +space: O(n) + +给一个tree, 按照题意找最一个node满足: +1. 这个node的subtree涵盖最深level的所有leaves. +2. 这个node必须是能找到的最deep那个 + +条件2的需求是因为: root本身就是满足条件1的node, 还有很多Higher-level node也是如此, 所以要找那个deepest. + + +#### DFS on tree +- 分析题目, 思想是: 看到tree里面所有的leaves, 找到他们最deep的 common ancestor +- Maintain a map +- Recursively dfs: return deepest node that has all leaves by these comparisons: +- 1. If left,right child same depth, return root: they need common ancestor +- 2. If not same depth, return the one with larger depth +- 被传送去上一个level的, 永远都是subtree里面符合题意的: the node containing all leaf nodes +- Visit all nodes once O(n), space O(n) + +#### BFS +- Find all leaves at deepest level +- Use map to track each node-parent +- Backtrack all nodes to find common ancestor + + + +--- + +**44. [Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)** Level: Medium Tags: [Linked List, Stack, Tree] + +time: O(n) +space: O(n) + +给一个BST, convert成 sorted doubly DoublyListNode. + +#### Inorder Traversal, Linked List +- 会iterative traverse Binary Search Tree(Stack && handle left-dig-down) +- create Doubly-ListNode, 注意用一个dNode作为tail node of the list + +##### Iterative inorder traversal +- 在check right node的事后, +- 不论right == null or != null, 每次都要强行move to right. +- 如果不node = node.right, +- 很可能发生窘境: +- node always = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 + + + +--- + +**45. [Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + +time: O(n) +space: O(n) + +#### Queue +- 简单的level traversal.根据level奇数偶数而add到不同位子. +- Option1: based on level % 2, insert to front/end of list +- Option2: based on level, insert right/left of node into queue + + + +--- + +**46. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**47. [Populating Next Right Pointers in Each Node II.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node%20II.java)** Level: Medium Tags: [DFS, Tree] + +time: O(n) +space: O(1) + +给一个binary tree, 用constant space link 所有所有node.next to same level next node. + +#### DFS +- 用constant space 也就是不可以BFS, 但是mention了用dfs stack space没问题 (提示啊!) +- 1. link leftChild -> rightChild +- 2. resolve root.rightMost child -> first possible root.next.left/right child +- 3. dfs connect(rightChild), connect(leftChild) +- Each level should be fully linked from left side, so every reach to parent will have valid path or end. + +#### Trick +- 1. 处理 nextNode -> next -> next ...的case: 找到第一个有child的next node才可以罢休. 这个case很容易miss +- 2. 我们的假设是, 上一个level的所有node都应该是linked, 那么在dfs时候, 就应该先connect(root.right). 右孩子的全处理完毕, 那么trick1才可以施行. + + + +--- + +**48. [Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)** Level: Medium Tags: [BST, Tree] + + +找 Inorder traversal规则里的下一个. + +主要想法是考虑: + 1. 如果 node.right == null, 找上一个unprocessed node alone the inorder traversal path + 2. 如果 node.right != null, successor 一定在这个node.right那个subtree里面 +最后竟然可以简化成几行, 非常全面的BST问题: 有search, 有对inorder traversal的理解, 还有坑. + +#### Short Recursive and Iterative without Stack +- Previous solution, we use stack to hold previous cached/unprocessed items: but do we need use catch to hold them? +- If moving left: `p.val < root.val`, then root (parent of left child) is a successor candidate, so save `rst = root`. +- If moving right or equal: `p.val >= root.val`, the successor has nothing to do with curr node, so just directly dive into root.right. +- Both iterative and recursive solution can be simplified as such. + + +#### Previous Iterative + stack +- Iteratively search +- Still need stack to store previously unprocessed items along the path + +#### Previous Recursive + Stack +- 画inorder图,发现规律.每个node的后继node(successor)有几种情况: +- 1. node.right 是个leaf到底了。那么就return. +- 2. set rightNode = node.right, 但发现rightNode has a lot left children to leaf. +- 3. 比如, node.right == null, 也就是node自己是leaf,要回头看山顶找Inorder traversal规则里的下一个。 +- 发现:其实就是每层都把路过的curr node放在stack里,最上面的,就是当下改return的那个successor:) Done. + + + +--- + +**49. [Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +#### Tree +- Traverse tree: left, right +- Concept of partial compare vs. whole compare + + + +--- + +**50. [Two Sum IV - Input is a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20IV%20-%20Input%20is%20a%20BST.java)** Level: Easy Tags: [Tree] + + +HashSet to store visited items. Same old 2 sum trick. + + + +--- + +**51. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + +**52. [Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Diameter%20of%20Binary%20Tree.java)** Level: Easy Tags: [Tree] + + +找longest path (include or not include root) + +跟Binary Tree Maximum Path Sum 的想法一样: 处理single path, 或者combined path (do not include curr root) + +#### Singlepath, combined path +- `int[]{combinedPath, singlePath}`; +- pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; +- complete left/right child, or join curr root: `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`; + + + +--- + +**53. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**54. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + + + + + + + +## Trie (11) +**0. [Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Prefix%20and%20Suffix%20Search.java)** Level: Hard Tags: [Trie] + + + + +--- + +**1. [Maximum XOR of Two Numbers in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20XOR%20of%20Two%20Numbers%20in%20an%20Array.java)** Level: Medium Tags: [Bit Manipulation, Trie] + + +比较难想到. 利用到XOR性质A^B=C, then A=B^C. +1. 枚举可能的A, 2. 然后一个个猜. + +1. 枚举A: 因为求MAX肯定是找leading-1最多的数字, 那么枚举A从(1000000...000)开始, +每次多一位取1或者0 +2. 因为枚举A的时候是按照每个bit来, 那么B和C也要以同样数位出现. +这里吧B和C变成了prefix的形式, 放在了set里面. +跟2sum用hashmap的思想类似, 每次用枚举的 A^B=C, 看看结果C是否已经在set里面. +如果在, 证明枚举的A可能被B^C得出, 那么就找到了一种情况. + +还用到一些技巧: +mask = (1 << i); // i位mask +mask = mask | (1 << i); // prefix mask + + + +--- + +**2. [Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- HashMap构建Trie. Trie三个Method: +- 1. Inset: 加 word +- 2. Search: 找word +- 3. StartWith: 找prefix + +##### 特点 +- 只有两条children的是binary tree. 那么多个children就是Trie +- 那么没有left/right pointer怎么找孩子? +- 用HashMap,以child的label为Key,value就是child node。 HashMap走位 + +##### 其他 +- node里的char在这是optional. 只要在每个TrieNode里面用map存储向下分布的children就好了. +- 另外有种题目,比如是跟其他种类的search相关,在结尾要return whole string,就可以在node里存一个up-to-this-point的String。 + +##### Previous Note +- 如果是遇到一个一个字查询的题,可以考虑一下。 +- 构建TrieNode的时候要注意:如何找孩子?如果是个map的话,其实就挺好走位的。 +- 而且,每个node里面的 char 或者string有时候用处不大, +- 可以为空。但是有些题目,比如在结尾要return一些什么String,就可以在end string那边存一个真的String。 + + + + + +--- + +**3. [Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +Trie结构, prefix tree的变形: '.'可以代替任何字符,那么就要iterate这个node所有的children. + +节点里面有char, isEnd, HashMap +Build trie = Insert word:没node就加,有node就移动。 +Search word:没有node就报错. 到结尾return true + +这题因为'.'可以代替任何possible的字符,没一种都是一个新的path,所以recursive做比较好些。 +(iterative就要queue了,麻烦点) + + + +--- + +**4. [Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)** Level: Hard Tags: [Backtracking, DFS, Trie] + + +给一串words, 还有一个2D character matrix. 找到所有可以形成的words. 条件: 2D matrix 只可以相邻走位. + +#### Trie, DFS +- 相比之前的implementation, 有一些地方可以优化: +- 1. Backtracking时候, 在board[][] 上面mark就可以, 不需要开一个visited[][] +- 2. 不需要implement trie的所有方程, 用不到: 这里只需要insert. +- 普通的trie题目会让你search a word, 但是这里是用一个board, 看board的每一个字母能不能走出个Word. +- 也就是: 这里的search是自己手动写, 不是传统的trie search() funcombination +- 3. TrieNode里面存在 end的时候存string word, 表示到底. 用完了 word = null, 刚好截断重复查找的问题. + +##### 关于Trie +- Build Trie with target words: insert, search, startWith. Sometimes, just: `buildTree(words)` and return root. +- 依然要对board matrix做DFS。 +- no for loop on words. 直接对board DFS: +- 每一层,都会有个up-to-this-point的string. 在Trie里面check它是不是存在。以此判断。 +- 若不存在,就不必继续DFS下去了。 +- Trie solution time complexity, much better: +- build Trie: n * wordMaxLength +- search: boardWidth * boardHeight * (4^wordMaxLength + wordMaxLength[Trie Search]) + + +#### Regular DFS +- for loop on words: inside, do board DFS based on each word. +- Time cpmplexity: word[].length * boardWidth * boardHeight * (4^wordMaxLength) + +#### Previous Notes +- Big improvement: use boolean visited on TrieNode! +- 不要用rst.contains(...), 因为这个是O(n) 在leetcode还是会timeout(lintcode竟然可以pass)! +- 在Trie search() method 里面,凡是visit过的,mark一下。 + + + + +--- + +**5. [Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)** Level: Hard Tags: [Backtracking, Trie] + + +可以开Trie class, 里面用到TrieNode. 开Trie(words) 可以直接initalize with for loop +TrieNode 里面可以有一个 List startWith: 记录可以到达这个点的所有string: 有点像树形, ancestor形状的存储. + +神操作: +根据square的性质, 如果选中了list of words, 设定 int prefixIndex = list.size(). +取出list里面的所有word[prefixedIndex], 并且加在一起, 就是下一个word candidate的 prefix. + +形象一点: +list = ["ball", "area"]; +prefixIndex = list.size(); ball[prefixIndex] = 'l'; area[prefixIndex] = 'e'; +//then +candidatePrefix = ball[prefixIndex] + area[prefixIndex] = "le"; +这里就可以用到Trie的那个 findByPrefix function, 在每个点, 都存有所有这个点能产生的candidate. +这时, 试一试所有candidate: dfs + +能想到这种倒转的结构来存prefix candidates 在 Trie里面, 这个想法非常值得思考. + + + +--- + +**6. [Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)** Level: Hard Tags: [Hash Table, String, Trie] + + +Obvious的做法是全部试一遍, 判断, 变成 O(n^2) * O(m) = O(mn^2). O(m): isPalindrome() time. + +当然不行了, 那就看是O(nlogN), 还是O(n)? + +#### 方法1: Hash Table + Palindrome的性质. 复合型. +O(mn) + +##### 思路 +- 每一个word, 都可以拆分成 front + mid + end. 如果这个word + 其他word可以组成palindrome,那就是说 +- 砍掉 (mid+end), front.reverse() 应该存在 words[] 里面. +- 砍掉 (front+mid), end.reverse() 应该存在 words[] 里面. +- 我们用HashMap存所有的, 然后reverse, 找配对就好. + +##### Corner case +- 如果有 empty string "", 那么它跟任何一个palindrome word, 都可以配对, 并且根据位置前后变换, 凑成2份 distinct indexes. +- 这样就有了那个 `if (reverseEnd.equals("")) {...}` 的logic. +- 注意: 虽然在处理砍头/砍尾的两个 for loop里面都在根据 empty string 重复记录, + 但因为 "" 自己本身不能作为起点, 所以overall只会在被其他palindrome配对时记录一次. + + +#### 方法2: Trie +还要做一下那. + + + +--- + +**7. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**8. [Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序, 排序以后才能逐个看是否partial string已经存在 +- 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 +- 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: 1. 长 string 排在前; 2. 相等length, 按照dictionary order 排序 +- 全部放入Trie. Trie.insert() +- 针对sorted words array, 从最长的开始找 Trie.startWith. +- 一旦找到, 就是符合题意的, 直接return. +- 注意: startWith 必须每一个node都是 isEnd, 才满足'逐个字母拼出' 的条件. +- Time: build Trie O(mn) + sort:O(nlogn) => O(nlogn) +- Space: O(mn) + +#### +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**9. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**10. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + + + + + + + +## BST (22) +**0. [Insert Node in a Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Node%20in%20a%20Binary%20Search%20Tree%20.java)** Level: Easy Tags: [BST] + + +往Binary Search Tree里面加东西,一定会找到一个合适的leaf加上去。 + +那么:就是说someNode.left or someNode.right是null时,就是insert node的地方。 + +找到那个someNode就按照正常的Binary Search Tree规律。 + + + +--- + +**1. [Minimum Absolute Difference in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Absolute%20Difference%20in%20BST.java)** Level: Easy Tags: [BST] + + +BST: inorder-traversal: 先left node(adding to stack till left leav), 再process stack.peek (mid node), 再 add rightNode && dive to rightNode.left leaf + + + +--- + +**2. [Remove Node in Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Node%20in%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST] + + +方法1: Brutle一点。找到target和target的parent. +把target remove时,把target的children nodes 重新排列组成新的BST: inorder traversal, build tree based on inorder traversal list. + +方法2: 分析规律,先找到target和parent, 然后根据性质,把target remove时,移动children nodes, 保证还是BST。 + + + +--- + +**3. [Zigzag Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Zigzag%20Iterator.java)** Level: Medium Tags: [BST] + + +这个题目相对简单. 做的时候我先考虑起来k条怎么办. 那么用个map把index和每个listmark一下就好了。 +每次next(), 相应的list的头拿下来就好。 +然后就跑圈呗,每次刷一个list头。不难。只要把几个variable维护清楚就行。 + + +--- + +**4. [Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DP, Tree] + + +Not quite clear. +根据左右分割而总结出了原理, 每次分割, 左右两边都会有一定数量的permutation, 总体上的情况数量当然是相乘. +然后每一个不同的分割点都加一遍: +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +然后把数学公式转换成DP的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**5. [Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, Tree] + + +方法1: +适合复习BST. 用DFS对待每个node. 注意BST的特征: 所有left nodes都小于当下node, 所有right nodes都大于当下node. + +根据题意用[L,R]切割.如果node.val>>>> (-t <= A - B <= t) =>>>>>> A >= B - t, A <= B + t +- 也就是说, 如果对于 B = nums[i], 来说, 能找到一个target A, 满足上面的公式, 那么就可以 return true. +- Time O(nLogk), treeSet的大小不会超过k, 而 treeSet.ceiling(), treeSet.add(), treeSet.remove() 都是 O(logK) +- Space O(k) + +#### Note +- 与Contains Duplicate II 类似概念. TreeSet有BST 因此可以直接用, 而不用自己构建BST +- 简化题目里面的重要条件 Math.abs(A-B) <= t 而推断出 A >= B - t, A <= B + t +- 并且需要需要用 TreeSet.ceiling(x): return number greater or equal to x. 这个用法要记住吧, 没别的捷径. + + + +--- + +**11. [Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的list +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Brutly寻找p和q的common ancestor, 然后recursively drive left/right +- 非常巧妙, 但是也比较局限; 稍微变条件, 就很难recursive. +- 几种情况: +- 1. one of p, q 在leaf, 那么此时的root其实就是lowest common ancestor +- 2. 如果p, q 在root的左右两边, 这就是分叉口, 那么root就是lowest common ancestor +- 3. 如果p,q 在root的同一边 (左,右), 那么继续dfs +- O(1) extra space, O(logn) time + + + +--- + +**12. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**13. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**14. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**15. [Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)** Level: Medium Tags: [BST, Binary Tree] + + +给一个BST, integer range (k1, k2), 找range 里面所有的integer. + +#### BST +- 等于dfs遍历了所有k1<= x <= k2的x node。 +- dfs left, process root, then dfs right +- 这里, 把 left/right/match的情况全部cover了,然后把k1,k2的边框限制好,中间就全部遍历了。 + + + +--- + +**16. [K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)** Level: Hard Tags: [Array, BST, TreeSet] + + +题目解析后: find 2 number, that: 1. k slots between the 2 number, 2. no slots taken between the two number. + +#### BST +- BST structure not given, use TreeSet to build BST with each node +- Every time find last/next inorder element +- `treeSet.lower(x)`, `treeSet.higher(x)` +- 一旦位置相隔(k + 1), 就满足题目条件 +- O(nlogn), good enough + +#### Track slots of days +- Reverse the array, save days index into days[], where the new index is slot. +- days[i]: at slot i, which day a flower will be planted +- O(n) +- Needs to understand: http://www.cnblogs.com/grandyang/p/8415880.html + + + +--- + +**17. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**18. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**19. [Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST, DFS, Tree] + + +BST里面有2个node misplace, 要归为. 要求: O(1) extra space + +#### Observation +- BST inorder traversal should give small -> large sequence +- misplaced means: a **large**->small item would occur, and later a large>**small** would occur. +- The first large && second small item are the 2 candidates. Example +- [1, 5, 7, 10, 12, 15, 18] +- [1, 5, `15, 10`, `12, 7`, 18] + +#### dfs, O(1) extra space +- traverse, and take note of the candidate +- at the end, swap value of the 2 candidates + +#### O(n) space +- inorder traversal the nodes and save in array, find the 2 items misplanced and swap them +- But O(n) space should not be allowed + + + + +--- + +**20. [Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)** Level: Medium Tags: [BST, Tree] + + +找 Inorder traversal规则里的下一个. + +主要想法是考虑: + 1. 如果 node.right == null, 找上一个unprocessed node alone the inorder traversal path + 2. 如果 node.right != null, successor 一定在这个node.right那个subtree里面 +最后竟然可以简化成几行, 非常全面的BST问题: 有search, 有对inorder traversal的理解, 还有坑. + +#### Short Recursive and Iterative without Stack +- Previous solution, we use stack to hold previous cached/unprocessed items: but do we need use catch to hold them? +- If moving left: `p.val < root.val`, then root (parent of left child) is a successor candidate, so save `rst = root`. +- If moving right or equal: `p.val >= root.val`, the successor has nothing to do with curr node, so just directly dive into root.right. +- Both iterative and recursive solution can be simplified as such. + + +#### Previous Iterative + stack +- Iteratively search +- Still need stack to store previously unprocessed items along the path + +#### Previous Recursive + Stack +- 画inorder图,发现规律.每个node的后继node(successor)有几种情况: +- 1. node.right 是个leaf到底了。那么就return. +- 2. set rightNode = node.right, 但发现rightNode has a lot left children to leaf. +- 3. 比如, node.right == null, 也就是node自己是leaf,要回头看山顶找Inorder traversal规则里的下一个。 +- 发现:其实就是每层都把路过的curr node放在stack里,最上面的,就是当下改return的那个successor:) Done. + + + +--- + +**21. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + + + + + + + +## MinHeap (9) +**0. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**1. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**2. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high(Comparator.comparing(a -> a.val));` is slower + + + +--- + +**4. [Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/Heapify.java)** Level: Medium Tags: [Heap, MinHeap] + + +Turn unsorted array into a min-heap array, where for each A[i], + +A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i]. + +#### Heap +- Heap用的不多. 得用一下, 才好理解. 通常default 的PriorityQueue就是给了一个现成的min-heap: +- 所有后面的对应element都比curr element 小。 +- Heapify里面的**siftdown**的部分: +- 只能从for(i = n/2-1 ~ 0), 而不能从for(i = 0 ~ n/2 -1): 必须中间开花,向上跑的时候才能确保脚下是符合heap规则的 + +#### Heapify/SiftDown做了什么? +- 确保在heap datastructure里面curr node下面的两个孩子,以及下面所有的node都遵循一个规律 +- 比如在这里,若是min-heap,就是后面的两孩子都要比自己大。若不是,就要swap。 + +#### min-heap的判断规律: +- for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. +- siftdown时:在curr node和两个child里面小的比较。如果的确curr < child, 搞定,break while. +- 但若curr 并不比child小,那么就要换位子,而且继续从child的位子往下面盘查。 + + + +--- + +**5. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**6. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**7. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**8. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + + + + + + + +## Permutation (3) +**0. [Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)** Level: Medium Tags: [Backtracking, Permutation] + + +TODO: need to review permutation + +permutation的综合题: +1. validate Input 是不是可以做palindromic permutation. 这个就是(Palindrome Permutation I) +2. 顺便存一下permutation string的前半部分和中间的single character(if any) +3. DFS 做unique permutation: given input有duplicate characters。 + + + +--- + +**1. [Shuffle an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Shuffle%20an%20Array.java)** Level: Medium Tags: [Permutation] + + +像shuffle music 一样, 做一套shuffle array的functions: + +shuffle() 给出random的permutation + +reset() 给出最初的nums + +#### Permutation +- Permutation 实际上就是在list/array/... 上面给元素换位置 +- 硬换位置, 每次换的位置不同, 用random来找到要换的index +- 维持同一个random seed +- O(n) + +##### Note +- compute all permutations 太慢, 不可行. + + + +--- + +**2. [Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)** Level: Medium Tags: [Backtracking, DFS, Permutation] + + +#### Recursive: Backtracking +- Given a remaining list: 取, 或者不取 +- always iterate over full `nums[]`, use list.contains() to check if item has been added. +- Improvement: maintain list (add/remove elements) instead of 'list.contains' +- time O(n!): visit all possible outcome +- T(n) = n * T(n-1) + O(1) + +#### Iterative: Insertion +- 插入法: +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- 但是比dfs要快, 因该是因为 # of checks 少: 不需要check list.size(), 不需要maintain remaining list. + +#### Previous Notes +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- A bit slower, possibly because of the polling and saving the entire list every time + + + + +--- + + + + + + + +## Partition DP (5) +**0. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**1. [Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)** Level: Hard Tags: [Binary Search, DP, Partition DP] + + +给一串书pages[i], k个人, pages[i] 代表每本书的页数. k个人从不同的点同时开始抄书. + +问, 最快什么时候可以抄完? + +#### Partition DP +- 第一步, 理解题目要求的问题: 前k个人copy完n本书, 找到最少的用时; 也可以翻译成: `n本书, 让k个人来copy, 也就是分割成k段`. +- 最后需要求出 dp[n][k]. 开: int[n+1][k+1]. +- 原理: +- 1. 考虑最后一步: 在[0 ~ n - 1]本书里, 最后一个人可以选择copy 1 本, 2 本....n本, 每一种切割的方法的结果都不一样 +- 2. 讨论第k个人的情况, 在 j = [0 ~ i] 循环. 而循环j时候最慢的情况决定 第k个人的结果(木桶原理): `Math.max(dp[j][k - 1], sum)`. +- 3. 其中: `dp[j][k-1]` 是 [k-1]个人读完j本书的结果, 也就是著名的`上一步`. 这里循环考虑的是第k个人不同的j种上一步 : ) +- 4. 循环的结果, 是要存在 dp[i][k] = Math.min(Math.max(dp[j][k - 1], sum[j, i]), loop over i, k, j = [i ~ 0]) +- Time: O(kn^2), space O(nk) + +##### Init +- Init: dp[0][0] = 0, 0个人0本书 +- Integer.MAX_VALUE的运用: +- 当 i = 1, k = 1, 表达式: dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); +- 唯一可行的情况就只有一种: i=0, k=0, 刚好 0 个人 copy 0 本书, dp[0][0] = 0. +- 其他情况, i = 1, k = 0, 0 个人读 1本书, 不可能发生: 所以用Integer.MAX_VALUE来冲破 Math.max, 维持荒谬值. +- 当 i=0, k=0 的情况被讨论时候, 上面的方程式才会按照实际情况计算出 dp[i][k] +- 这道题的init是非常重要而tricky的 + +##### 计算顺序 +- k个人, 需要一个for loop; +- k个人, 从copy1本书开始, 2, 3, ... n-1,所以 i=[1, n], 需要第二个for loop +- 在每一个i上, 切割的方式可以有[0 ~ i] 中, 我们要计算每一种的worst time + +##### 滚动数组 +- [k] 只有和 [k - 1] 相关 +- Space: O(n) + +#### Binary Search +- 根据: 每个人花的多少时间(time)来做binary search: 每个人花多久时间, 可以在K个人之内, 用最少的时间完成? +- time variable的范围不是index, 也不是page大小. 而是[minPage, pageSum] +- validation 的时候注意3种情况: 人够用 k>=0, 人不够所以结尾减成k<0, 还有一种是time(每个人最多花的时间)小于当下的页面, return -1 +- O(nLogM). n = pages.length; m = sum of pages. + + + + +--- + +**2. [Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Partition DP +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- 确定末尾的2种状态: single letter or combos. 然后计算出单个letter的情况, 和双数的情况 +- 定义`dp[i] = 前i个digits最多有多少种decode的方法`. new dp[n + 1]. +- 加法原理: 把不同的情况, single-digit, double-digit 的情况加起来 +- dp[i] += dp[i - x], where x = 1, 2 +- note: calculate number from characters, need to - '0' to get the correct integer mapping. +- 注意: check value != '0', 因为'0' 不在条件之中(A-Z) +- Space, Time O(n) + +#### 引申 +- 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + + + +--- + +**3. [Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)** Level: Hard Tags: [DP, Enumeration, Partition DP] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +其中字符可能是 "*", 可以代表 [1 - 9] + +#### DP +- 乘法原理 +- 跟decode way I 一样, 加法原理, 切割点时: 当下是取了 1 digit 还是 2 digits 来decode +- 定义dp[i] = 前i个digits最多有多少种decode的方法. new dp[n + 1]. +- 不同的情况是: 每一个partition里面, 如果有"*", 就会在自身延伸出很多不同的可能 +- 那么: dp[i] = dp[i - 1] * (#variations of ss[i]) + dp[i - 2] * (#variations of ss[i,i+1]) + +##### 特点 +- 枚举的能力: 具体分析 '*' 出现的位置, 枚举出数字, 基本功. +- 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) +- 理解取MOD的原因: 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. +- 枚举好以后, 其实这个题目的写法和思考过程都不难 + + + + +--- + +**4. [Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)** Level: Hard Tags: [DP, Partition DP] + + +给一个String s, 找出最少用多少cut, 使致 切割出的每一个substring, 都是palindrome + +#### Partition DP +- Find minimum cut: 分割型DP +- dp[i]: 最少cut多少刀, 使得前 i 长度的string, 割出来都是palindrome +- 最终要得到 dp[n], 所以 int[n + 1] +- 移动切刀, 看在哪里切, index j in [0 ~ i] +- 考虑[j, i - 1] 是否是回文串, 如果是, 那么: dp[i]= min(dp[i], d[j] + 1). +- note: 估计遍历 j 的时候, 反过来遍历也可以. + +#### 计算Palindrome的优化 +- 利用palindrome的性质, 可以算出 boolean palindrome[i, j]的情况. +- 找一个任意mid point: +- 1. 假设palindrome是奇数长度, 那么 mid 是单独的字符, 而两边的字符 [mid-1], [mid+1] 应该完全相等. +- 2. 假设palindrome是偶数长度, 那么 [mid] 和 [mid + 1] 这样位置的字符应该相等. +- 这样做出来 palindrome[i, j]: 从字符 i 到 字符 j 的 substring 是否是 palindrome +- 这样就给我们的问题合理降维, 目前是time: O(n^2). +- 不然求一次palindrome, 就是n, 会变成O(n^3) + +#### Previous Notes +- Double for loop 检查每种substring string (i~j). 若i,j相邻或者同点,那么肯定isPal;否则,i,j之间的(i+1, j-1)一定得isPal。 +- 看上去,在检查i,j的时候,中间按的(i+1, j-1)怎么可能先知道? 其实不然..在j慢慢长大的时候,所有的0~j的substring都检查过。所以isPal[i+1][j-1]一定是已经知道结果的。 +- okay.那么假如以上任意一种情况成立,也就是说isPal[i][j] == true。那就要判断,切到第一层循环参数j的末尾点时,有多少种切法? +- 想法很顺:我们naturally会想到,把i之前的cut加上i~j之间发生的不就好了。 +- 反正现在j不变,现在就看吧i定在哪里,cut[i - 1]是否更小/最小; 再在cut[i-1]基础上+1就完了。 +- 当然,如果i==0, 而 i~j又是isPal,那没啥好谈的,不必切,0刀。 +- 最终,刷到cut[s.length() - 1] 也就是最后一点。 return的理所应当。 + + + + +--- + + + + + + + +## Binary Search Tree (1) +**0. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + + + + + + + +## PriorityQueue (19) +**0. [Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### 方法1: +找是否有overlap. priorityQueue 按照start time排序好以后, 比较current和peek: current.end > peek.start? + +#### 方法2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 + + + +--- + +**1. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**2. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**3. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**4. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**5. [Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort] + + +#### Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn) + + +#### Basic Implementation +- 这里已经给了 **sorted** intervals by start point. +- 直接找到可以insert newInterval的位子. Insert +- 然后loop to merge entire interval array +- 因为给的是个list, 所以方便`intervals.remove(i)` +- remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture +- O(n) + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**6. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + +**7. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**8. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**9. [Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort] + + +#### PriorityQueue +- Use priority queue to sort by customized class interval{int dist; int x, y;}. +- Sort by larger distance and then sort by start index +- seat(): pq.poll() to find interval of largest distance. Split and add new intervals back to queue. +- leave(x): one seat will be in 2 intervals: remove both from pq, and merge to a new interval. +- 主方程写出来其实很好写, 就是 split + add interval, 然后 find + delete interval 而已. 最难的是构建data structure +- seat(): O(logn), leave(): O(n) + +##### Trick: 构建虚拟 boundary +- 如果是开头的seat, 或者是结尾的seat, 比较难handle: 一开始坐在seat=0的时候, 没有interval啊! +- Trick就是, 我们自己定义个虚拟的座位 `seat=-1`, `seat=N` +- 一开始有一个 interval[-1, N] 然后就建立了boundary. +- 从此以后, 每次split成小interval的时候: +- 遇到 `interval[-1, y]`, distance就是 `(y - 0)` +- 遇到 `interval[x, N]`, distance就是 `(N - 1 - x)` +- 当然正常的interval dist 就是 `(y - x) / 2` + +##### distance 中间点 +- Interval.dist 我们其实做的是 distance的中间点 `(y - x) / 2` +- 这里的dist是 `距离两边的距离` 而不是 x, y 之间的距离. 这里要特别注意. + +#### TreeSet +- https://leetcode.com/problems/exam-room/discuss/139885/Java-Solution-based-on-treeset/153588 + +#### Map +- how? +- TODO, not sure. + + + +--- + +**10. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**11. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**13. [Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)** Level: Medium Tags: [Heap, MinHeap, PriorityQueue] + + +Same as merge k sorted list, use priorityQueue + +#### Priority Queue +- 由Merge k sorted list启发。用PriorityQueue,存那k个首发element +- PriorityQueue需要存储单位: 自己建一个Class Node 存val, x, y index. +- 因为array里没有 'next' pointer,只能存x,y来推next element +- Not sure why `new PriorityQueue<>(Comparator.comparing(a -> a.val));` is slower + + + +--- + +**14. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**15. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**16. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + +**17. [The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)** Level: Medium Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- if already found a good/shorter route, skip +- `if (distMap[node.x][node.y] <= node.dist) continue;` +- This always terminates the possibility to go return to original route, because the dist will be double/higher + + + +--- + +**18. [The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)** Level: Hard Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- 跟 Maze I, II 类似, 用一个 Node[][] 来存每一个(x,y)的state. +- Different from traditional BFS(shortest path): `it terminates BFS when good solution exists (distance), but will finish all possible routes` +- 1. `Termination condition`: if we already have a good/better solution on nodeMap[x][y], no need to add a new one +- 2. Always cache the node if passed the test in step1 +- 3. Always offer the moved position as a new node to queue (as long as it fits condition) +- 4. Finally the item at nodeMap[target.x][target.y] will have the best solution. + + + +--- + + + + + + + +## Interval DP (4) +**0. [Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)** Level: Hard Tags: [DP, Interval DP, String] + + +- 给两个string S, T. 检验他们是不是scramble string. +- scramble string 定义: string可以被分拆成binary tree的形式, 也就是切割成substring; +- 旋转了不是leaf的node之后, 形成新的substring, 这就是原来string的 scramble. + + +#### Interval DP 区间型 +- 降维打击, 分割, 按照长度来dp. +- dp[i][j][k]: 数组S从index i 开始, T从index j 开始, 长度为k的子串, 是否为scramble string + +##### Break down +- 一切两半以后, 看两种情况: , 或者不rotate这两半. 对于这些substring, 各自验证他们是否scramble. +- 不rotate分割的两半: S[part1] 对应 T[part1] && S[part2] 对应 T[part2]. +- rotate分割的两半: S[part1] 对应 T[part2] && S[part2] 对应 T[part1]. + +##### Initialization +- len == 1的时候, 其实无法旋转, 也就是看S,T的相对应的index是否字符相等. +- initialization非常非常重要. 很神奇, 这个initailization 打好了DP的基础, 后面一蹴而就, 用数学表达式就算出了结果. +- input s1, s2 在整个题目的主要内容里面, 几乎没有用到, 只是用在initialization时候. +- More details, 看解答 + + + +--- + +**1. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**2. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**3. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + + + + + + + +## Heap (16) +**0. [Kth Smallest Element in a Sorted Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20Sorted%20Matrix.java)** Level: Medium Tags: [Binary Search, Heap] + +time: O(n + klogn) +space: O(n) + +给一个sorted matrix, 找kth smallest number (not distinct) + +Related: `Kth Largest Element in an Array` + +#### PriorityQueue +- 和Merge K sorted Array/ List 类似:使用PriorityQueue。 +- 因为Array的element无法直接找到next,所以用一个class Node 存value, x,y positions. +- Initial O(n) time, also find k O(k), sort O(logn) => O(n + klogn) +- 变型: Kth Largest in N Arrays + +#### Binary Search +- we know where the boundary is start/end are the min/max value. +- locate the kth smallest item (x, y) by cutt off partition in binary fasion: +- find mid-value, and count # of items < mid-value based on the ascending matrix +- O(nlogn) + + + + +--- + +**1. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**2. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**3. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**4. [Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)** Level: Hard Tags: [Greedy, Hash Table, Heap] + + +给一个string, 全是lowercase letter, 要求重新排列: 然后每个unique的character要有k distance apart. + +跟Task Scheduler有点像, 只不过Task那道题里面还可以用其他方法求count, 这道题要求出排列结果 + +#### PriorityQueue + HashTable +- PriorityQueue排序+分布排列的一个经典用法. +- Count frequency and store in pq. +- Consume element of pq for k rounds, each time pick one element from queue. +- Exception: if k still has content but queue is consumed: cannot complete valid string, return ""; +- space, O(n) extra space in sb, O(26) constant space with pq. +- time: O(n) to add all items + + + +--- + +**5. [HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/HashHeap.java)** Level: Hard Tags: [HashHeap, Heap] + + +非题.是从九章找来的HashHeap implementation. + + + +--- + +**6. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**7. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**9. [Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)** Level: Medium Tags: [Heap, MinHeap, PriorityQueue] + + +Same as merge k sorted list, use priorityQueue + +#### Priority Queue +- 由Merge k sorted list启发。用PriorityQueue,存那k个首发element +- PriorityQueue需要存储单位: 自己建一个Class Node 存val, x, y index. +- 因为array里没有 'next' pointer,只能存x,y来推next element +- Not sure why `new PriorityQueue<>(Comparator.comparing(a -> a.val));` is slower + + + +--- + +**10. [Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/Heapify.java)** Level: Medium Tags: [Heap, MinHeap] + + +Turn unsorted array into a min-heap array, where for each A[i], + +A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i]. + +#### Heap +- Heap用的不多. 得用一下, 才好理解. 通常default 的PriorityQueue就是给了一个现成的min-heap: +- 所有后面的对应element都比curr element 小。 +- Heapify里面的**siftdown**的部分: +- 只能从for(i = n/2-1 ~ 0), 而不能从for(i = 0 ~ n/2 -1): 必须中间开花,向上跑的时候才能确保脚下是符合heap规则的 + +#### Heapify/SiftDown做了什么? +- 确保在heap datastructure里面curr node下面的两个孩子,以及下面所有的node都遵循一个规律 +- 比如在这里,若是min-heap,就是后面的两孩子都要比自己大。若不是,就要swap。 + +#### min-heap的判断规律: +- for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. +- siftdown时:在curr node和两个child里面小的比较。如果的确curr < child, 搞定,break while. +- 但若curr 并不比child小,那么就要换位子,而且继续从child的位子往下面盘查。 + + + +--- + +**11. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**12. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**13. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**14. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**15. [Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Deque, Monotonous queue +- 维持monotonuous queue: one end is always at max and the other end is min. Always need to return the max end of queue. +- when adding new elements x: start from small-end of the queue, drop all smaller elements and append to first element larger than x. +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`. +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! + + + +--- + + + + + + + +## Linked List (31) +**0. [Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)** Level: Medium Tags: [Hash Table, Linked List] + + +TODO: +1. hash table solution not passing +2. Find O(n) solution + +#### Hash Table +- 与其他Majority Number一样。 +- 出现次数多余1/k,就要分成k份count occurance.用HashMap。 存在的+1;不存在map里的,分情况: +- 若map.size() == k,说明candidate都满了,要在map里把所有现存的都-1; +- 若map.size() < k, 说明该加新candidate,那么map.put(xxx, 1); +- 最后在HashMap里找出所留下的occurance最大的那个数。 +- 但这样的worst case是 O(nk) + + + +--- + +**1. [Remove Duplicates from Unsorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Unsorted%20List.java)** Level: Medium Tags: [Linked List] + + +基本方法: O(n) sapce, time +遍历。 +遇到duplicate(可能多个), while直到node.next不是duplicate. +接下去,既然不是duplicate,那就add 进 set + + +如果不用extra memory, do it in place: +那就要sort linked list. 用merge sort. + +复习merge sort: +1. find middle. +2. recursively: right = sort(mid.next); left = sort(head). +3. within sort(), at the end call merge(left, right) + + +--- + +**2. [Two Lists Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Lists%20Sum.java)** Level: Medium Tags: [Linked List] + + +给两个Linked list, sum up and 合成新的list + + + +--- + +**3. [Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. +遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + +跟Add Binary的理解方式一模一样. + +注意: +Linked List 没有天然size. +用DummyNode(-1).next来hold住结果. + + + + +--- + +**4. [Add Two Numbers II.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers%20II.java)** Level: Medium Tags: [Linked List] + + +Singly-linked list需要reverse, 用stack. +最终结果要恢复成input list 那样的sequence方向, 用stack一个个pop()刚好就可以做到. + +加法都一样: + 1. sum = carry + 2. carry = sum / 10 + 3. sum = sum % 10; + + + +--- + +**5. [Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List] + + +如题, 把一个sorted singly linked list 转换成一个 height balanced BST + +#### DFS +- Divide and Conquer +- 找到mid node +- 然后分割两半, 分别dfs做各自两个subtree: node.left,node.right +- 用长度来定位mid, 每次找中间点做root, 然后前半段, 后半段分别dfs with length. +- 用快慢pointer 找到mid. Better: 不用traverse entire linked list + +#### Details +- slowPointer = node; +- fastPointer = node.next; +- 然后把root = mid.next +- 然后开始sortedListToBST(mid.next.next); //后半段 +- mid.next = null;//非常重要,要把后面拍过序的断掉 +- sortedListToBST(head); //从头开始的前半段 +- 最后root.left, root.right merge一下。 + + + +--- + +**6. [Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Two Pointer: Slow Fast Pointer +- O(1) sapce: 用快慢指针。一个跑.next, 一个跑.next.next。 总有一次,fast会因为cycle而追上slow。 +- 那个时候其实slow.val = fast.val. + +#### Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**7. [Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +O(n), one pace, no extra space +找到窗口, 然后平移, 最后pre 和 head之间 skip一个node就好. + + + +--- + +**8. [Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Linked List, Math, Two Pointers] + + +LinkedList 里面有 cycle, 找到cycle的起始点(第一个重复出现的element). + +#### Slow, fast Pointer +- 快慢指针, O(1)space. +- 1. 确认有cycle后 2. 数学问题:找到开头. +- 当head == slow.next时候, head就是cycle starting point. +- 也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +#### 证明 +- 1. 假设慢指针走t步, 快指针走快一倍, 也就是2t. +- 2. 我们假设cycle的长度是Y, 而进入cycle之前的长度为X. +- 3. 假设慢指针走了m圈cycle, 而快指针走了n圈cycle之后, 两个pointer相遇. +- 4. 最终在Y cycle里面的K点相遇, 也就是两个指针都在这最后一圈里面走了K 步. +- 那么: +- t = X + mY + K +- 2t = X + nY + K +- 整合公式: X + K = (n - 2m)Y +- 这里的m和n不过是整数的跑圈数, 也就是说X和K加在一起, 总归是结束cycle. X 和 K 互补 +- 结论: 当slow/fast 指针在K点相遇后, 再走X步, 就到了cycle的起点, 也就是题目要求的起点. + +#### Hash Table, O(n) space + + + + +--- + +**9. [Swap Nodes in Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Nodes%20in%20Pairs.java)** Level: Medium Tags: [Linked List] + + +#### enumurate +基本原理, 写出来, 然后连线: +pre -> A -> B -> C -> ... +需要一个虚拟 preNode做起始node, 不然无法把后面的node换到开头. + +#### 注意 +用dummy = pre作为head前一格. +用 `pre.next == null && pre.next.next` 来check是否为NULL. +pre.next.next 保证了至少有一次swap. + + + +--- + +**10. [Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Node%20in%20a%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +Given Singlely linked list, 删除一个任意node (不能是head node) + +#### Basic +- update node.val +- Link curr.next to curr.next.next + + + +--- + +**11. [Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)** Level: Medium Tags: [Linked List, Sort] + + +input一串数字, 需要出sorted output. 每次insert一个数字时, 都要放到正确的sorted的位置 + +每次insertion的时候, 都从input里面减掉这个数字 + +#### Linked List +- 把list里面每个元素都拿出来,scan and insert一遍 +- Time O(n^2), worst case, 每次放入n个数字里面的element, 刚好都是最大的 +- 所以每次要traverse n nodes, 然后走n次 + +##### 思考方法 +- 如果已经有个sorted list, insert一个element进去。怎么做? +- while 里面每个元素都小于 curr, keep going +- 一旦curr在某个点小了,加进去当下这个空隙。 + + + +--- + +**12. [Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Middle%20of%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +找Linked List的中间node + +- 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + + + +--- + +**13. [Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Linked%20List%20Elements.java)** Level: Easy Tags: [Linked List] + + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + + + +--- + +**14. [Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单, 但是要在Linkde List random access坐标, 是很难得: 所以需要把一半 ListNode 翻转 +- reverse linked list: 遍历接开头 +- 用快慢指正找到mid point +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + +#### Previous Note +- Palindrome都是要两边回溯相等 +- linkedlist不能reverse iterating, 那么就reverse the list, 从中间开花作比较。 + + + +--- + +**15. [Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +#### Reverse List +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + + + +--- + +**16. [Reverse Linked List II .java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List%20II%20.java)** Level: Medium Tags: [Linked List] + + +reverse 一个 linked list 中 [m ~ n] 的一部分. + +#### Reverse linked list +- 在基本的reverse linked list 上面 多了一层: 找到front node, 接下来的 [m ~ n] node 需要被reverse +- 只需要reverse中间的部分. +- Reverse的时候: 用一个dummyNode, 这道题里面, 其实就用 nodeFront, 那么 dummy.next 就是整个reversed list. + +##### 注意 +- 一定要Mark开头的那个mth node, 最后用它接上 剩下node tail. 不然后面的node会断掉 + +#### Previous notes +- 遍历到M前, +- 存一下那个点, +- 从M开始, for loop, reverse [m~n]。 然后把三段链接在一起。 + + + + +--- + +**17. [Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Linked%20Lists.java)** Level: Easy Tags: [Linked List] + + +给两个 linked list, 问从哪个node开始, 两个 linked list 开始有重复? + +#### Basics +- 长短list,找重合点 +- 长度不同的话,切掉长的list那个的extra length +- 那么起点一样后,重合点就会同时到达 +- Time O(n) * 2, constant space + + + +--- + +**18. [Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Sorted%20Lists.java)** Level: Easy Tags: [Linked List] + + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + + + +--- + +**19. [LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)** Level: Hard Tags: [Design, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度。 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) + +##### 巧妙点 +- 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +- 2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +- 直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法: +- moveToHead() +- insertHead() +- remove() + +#### O(n) 检查重复 +- timeout method, 天真的来了一个O(n) 的解法,结果果然timeout. +- 一个map存数值。一个queue来存排位。 +- 每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + + + + +--- + +**20. [Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List.java)** Level: Easy Tags: [Linked List] + + +从Linked list 里面摘掉重复元素, 只留下unique元素. + +#### Linked List +- sorted list, 重复元素都在一起 +- 知道如何构建Linked List. +- 一点遇到重复元素: node.val == node.next.val, 就去掉. +- 用一个dummy node 来跑路 +- 注意: +- 只有当没有重复的时候, 才node = node.next; +- 有重复的时候, 当后面第三个元素被提上来之后, 还是可能跟当下元素重复, 所以不能前移node. +- ex: A -> A -> A +- while loop 里面check node 和 node.next 比较好, 这样ending position会非常清晰 + + + +--- + +**21. [Remove Duplicates from Sorted List II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List%20II.java)** Level: Medium Tags: [Linked List] + + +从Linked list 里面摘掉重复元素: 只要重复过, 全部都删掉; 重复出现过得元素一个不留. + +#### Linked List +- sorted list, 重复元素都在一起 +- 运用 dummyHead: 如果要去掉所有重复元素, 就要有个dummyHead作为局外人在开头牵线 +- 只要发现一个 node.val == node.next.val, 就记下这个duplicated val, move forward, 过掉所有重复过的元素 +- 思想: +- 用第二个 inner while loop, 把所有的重复元素都处理干净, 然后再move forward +- 优点: outter while loop 不需要考虑太多case, 在inner loop 都把主要的business logic 解决了. + +##### 注意DummyHead 的使用 +- 当我们有了DummyHead 作为Linked List 的局外线头, 其实可以选择每次遇到duplicate, 就把更加后面的元素 强行assign 给 dummyHead.next +- 下面还尝试过一种做法: 但是需要考虑的edge case 太多了: 不断移动node, 知道不重复, assign prev.next = node. +- 这样的做法比较直白, 但是需要考虑很多edge case, 而且并没有很好利用到 dummy head, 注意规避. + +##### Previous Note +- 斩草除根。 +- 多个node,check node.next ?= node.next.next + + + + +--- + +**22. [Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +给一个single linked list, 右移k steps. k non-negative. + +#### Linked List basics +- 记得用dummy.next来存head. +- 特殊: 这里k可能大于list总长. 写一写linked node 移动的步数, 然后 k = k % n. +- 找到newTail, newHead, 然后利用dummy, 换位子 + + + +--- + +**23. [Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + +time: O(n) +space: O(1) + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List +- Basic Implementation of copy linked list: +- use node and dummy to hold new list, 遍历head.next .... null. +- Map 在这里用来: 1. avoid creating same node; 2. return the item if existing +- map 的 key全部是old object, 新的key全部是 newly created object +- 每一步都check map里面有没有head. 没有? 加上 +- 每一步都check map里面有没有head.random. 没有? 加上 + + + +--- + +**24. [Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Linked List +- linked list 不能像partitioin array一样从两边遍历 +- 把小于value的加在前半段, 把 >= value的加在后半段 +- 做法很普通: 建造两个list, midTail pointer, post pointer +- 把满足条件(=x)的数字分别放到两个list里面 +- 记得用dummyNode track head. +- 最终midTail.next = post链接起来。 + + + +--- + +**25. [Reorder List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reorder%20List.java)** Level: Medium Tags: [Linked List] + + +给一个Linked list, reorder: 从head/tail 两个方向 向中间进发, re-order like: one node at a time, + +#### Linked List 功能大集合 +- reverse list, find mid of list, merge two list +- 先find mid, 然后把 mid.next reverse了, 最后merge 两段. +- 注意, 用完mid.next之后, 一定要 mid.next = null, 不然merge会出问题 + + + +--- + +**26. [Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/Nth%20to%20Last%20Node%20in%20List.java)** Level: Easy Tags: [Linked List] + + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth + + + +--- + +**27. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**28. [Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)** Level: Medium Tags: [Linked List, Stack, Tree] + +time: O(n) +space: O(n) + +给一个BST, convert成 sorted doubly DoublyListNode. + +#### Inorder Traversal, Linked List +- 会iterative traverse Binary Search Tree(Stack && handle left-dig-down) +- create Doubly-ListNode, 注意用一个dNode作为tail node of the list + +##### Iterative inorder traversal +- 在check right node的事后, +- 不论right == null or != null, 每次都要强行move to right. +- 如果不node = node.right, +- 很可能发生窘境: +- node always = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 + + + +--- + +**29. [Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- 记得k lists 需要是已经sort好的 +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: +- 1. 不要忘记customized priority需要一个customized new Comparator() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**30. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + + + + + + + +## Stack (30) +**0. [Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)** Level: Medium Tags: [BST, Design, Stack, Tree] + + +画一下, BST in order traversal. 用stack记录最小值, 放在top. O(h) space. +每次消耗TreeNode, 都看看rightNode(其实就是下一个最小的candidate), 并且一条龙stack叠上rightNode所有的left子孙. + +Previous Notes: +用O(h)空间的做法: + +理解binary search tree inorder traversal的规律: + 先找left.left.left ....left 到底,这里是加进stack. + 然后考虑parent,然后再right. + +例如这题: + stack里面top,也就是tree最左下角的node先考虑,取名rst. + 其实这个rst拿出来以后, 它也同时是最底层left null的parent,算考虑过了最底层的parent。 + 最后就考虑最底层的parent.right, 也就是rst.right. + +注意: + next()其实有个while loop, 很可能是O(h).题目要求average O(1),所以也是okay的. + + +用O(1)空间的做法:不存stack, 时刻update current为最小值。 + +找下一个最小值,如果current有right child: + 和用stack时的iteration类似,那么再找一遍current.right的left-most child,就是最小值了。 + +如果current没有right child: + 那么就要找current node的右上parent, search in BinarySearchTree from root. + +注意: + 一定要确保找到的parent满足parent.left == current. + 反而言之,如果current是parent的 right child, 那么下一轮就会重新process parent。 + 但是有错:binary search tree里面parent是小于right child的,也就是在之前一步肯定visit过,如此便会死循环。 + + + + +--- + +**1. [Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, Stack] + + +方法1: 用queue, 把需要的item全部打出来 +方法2: 用stack, 把需要的item先存一行, 每次打开子序列时候, 全部加回stack. + + + +--- + +**2. [Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. +#### 方法1 +Array, 维持一个左手最高墙array, 右手最高强array. +对于每个index而言, vertically 能存放的最大水柱, 就是靠左右最高墙决定的: +min(leftHighestWall, rightHighestWall) - currHeight. + +#### 方法2 +方法1上面的优化, two pointer, 还是找左边最高和右边最高. O(1) space. +利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +左边按照maxLeft来计算, 右边按照maxRight来计算. + +#### 方法3 +2 Pointers, 双面夹击: +1. 找中间最高bar的index +2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条。 +3. 每次还要减去block自身的height + +#### 方法4 +主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. + + + + +--- + +**3. [Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)** Level: Easy Tags: [Design, Stack] + + +双Stack:一个正常stack,另一个minStack存当下level最小值. 注意维护minStack的变化 + +另外. 如果要maxStack,也是类似做法 + + + +--- + +**4. [Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)** Level: Easy Tags: [Design, Stack] + + +#### 双Stack +画图, 知道最后maintain的stack是那个 reverseStack: pop(), peek(), empty() 都在这个stack上, 无需变换. +push()里面做stack和reverseStack的来回倾倒. +相比老的code, 在PUSH里面做倾倒, 更容易读. + +#### Previous notes +双Stack. 一个是等于是queue,一个是backfillStack. +Tricky: 是在pop()和peek()的时候backfill, 并且要等到stack用完再backfill. +写一下例子就知道,如果提早backfill,stack.peek()就不是queue的head了. + + + + +--- + +**5. [Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)** Level: Medium Tags: [DFS, Divide and Conquer, Stack] + + +给一个expression string. 里面包括数字, 字母, 括号. 其中数字代表括号里面的内容重复几次. + +括号里面可以是String, 也可能是expression. + +目的: 把expression展开成一个正常的String. + + +#### Stack, Iteratively +- Process inner item first: last come, first serve, use stack. +- Record number globally and only use it when '[' is met. +- Stack存 [ ] 里面的内容, detect 括号开头结尾: 结尾时process inner string +- 有很多需要注意的细节才能做对: +- Stack 也可以用, 每个地方要注意 cast. 存进去的需要是Object: String, Integer +- 几个 type check: instanceof String, Character.isDigit(x), Integer.valueOf(int num) +- 出结果时候: `sb.insert(0, stack.pop())` + + +#### DFS +- Bottom->up: find deepest inner string first and expand from inside of `[ ]` +- 与Stack时需要考虑的一些function类似. 特别之处: **检查`[ ]`的结尾** +- 因为DFS时候, 括号里的substring会被保留着进入下一个level, 所以我们在base level要keep track of substring. +- 用int paren 来track 括号的开合, 当paren再次==0的时候 找到closure ']' +- 其他时候, 都要继续 append to substring + + + + +--- + +**6. [Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)** Level: Hard Tags: [Array, Monotonous Stack, Stack] + + +给n个bar,组成柱状图histogram. 求在这一排柱状图里面可以找到的面积最大的长方形. + +思考: 找长方形面积, 无非是找两个index, 然后底边长度 * height. + +#### Monotonous Stack +- 重点是根据找Histogram里面rectangle的性质, 维持一个单调递增的Stack +- 在loop over indexes的时候: +- 如果高度>= previous peek(), 那么对于那个peek, 就意味着, 往下走, 一直走高嘛, 之前的peek总可以继续抄底 +- 什么时候不能抄底了呢? 就是有一个下降趋势的时候 +- 这时候并不是calculate所有前面的peek, 而是考虑 大于 current height的之前所有的peek. +- 把这些peek到 current height 前一格的rectangle全部找出来: stack.pop() +- 这个stack.pop()的过程里面, 其实没有算上 current height, 因为需要留到下一轮, 把current index加进stack 再说 +- 为什么用stack? 因为需要知道连续递增的peek, stack.peek() O(1), 好用 + 而其实不用stack, 也可以用其他方式记录所有height, 只不过要 O(n)去找peek不方便 + +#### 知识点 +- 理解monotonous stack 是如何被维护的 +- 维护monotonous stack 是题目需要, 而不是stack本身性质, 是一种借助 stack.peek() O(1)的巧妙用法. + + + + +--- + +**7. [Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)** Level: Medium Tags: [Stack, Tree] + + +给一串数字, 做一个 maximum binary tree: 最顶上的root最大; 左child也是一个max tree, 右child也必须是max tree. + +#### Monotonous Stack +- 用到bottom->top递减的stack: 最底下的root维持成最大的element. +- 过程当中, 一旦遇到currNode.val > stack.peek(), 就意味着需要把这个currNode放在 stack的底层位置. +- 也就是说, 遇到这个条件, process, pop()所有 currNode.val > stack.peek(), 最后把currNode加进去. + +- maxTree题目本身的要求是: 大的在最中间, 左右两边的subTree也要是maxTree: +- Monotonous Stack在这里帮助 keep/track of max value, 但是left/right tree的logic是MaxTree独有的. +- left/right node的assignment是根据题目要求: 中间最大值分开后, 左边的是左边subTree, 右边的作为右边subTree. + +#### Previous notes +- Should memorize MaxTree. 依次类推,会做Min-Tree, Expression Tree +- Stack里,最大的值在下面。利用此性质,有这样几个step: + +##### Step1 +- 把所有小于curr node的,全Pop出来, while loop, keep it going. +- 最后pop出的这个小于Curr的node:它同时也是stack里面pop出来小于curr的最大的一个,最接近curr大小。(因为这个stack最大值靠下面) +- 把这个最大的小于curr的node放在curr.left. + +##### Step2 +- 那么,接下去stack里面的一定是大于curr: +- 那就变成curr的left parent. set stack.peek().right = curr. + +##### Step3 +- 结尾:stack底部一定是最大的那个,也就是max tree的头。 + + + + + +--- + +**8. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**9. [Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Recursive +- 在自己的基础上recursive, 不用helper function +- Divide and Conquer, with helper(dfs) method +- O(n) time, no extra space + +#### Iterative: Stack +- Add left nodes all the way +- Print curr +- Move to right, add right if possible +- O(n) time, O(h) space + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. + +若不右移, 很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + +#### HashMap +? How? + + + +--- + +**10. [Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Recursive +trivial, 先加left recursively, 再加right recursively, 然后组成头部. + +#### Stack +- 双stack的思想, 需要在图纸上画一画 +- 原本需要的顺序是: 先leftChild, rightChild, currNode. +- 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild +- 这样出来的结果是reverse的, 那么翻转一下就可以了. +- v1做的时候用了stack1, stack2, 因为根据这个双stack的思想而来 +- v2简化, 可以放在一个stack里面, 每次record result 的时候: rst.add(0, item); + +##### 利用stack的特点 +- 每次加element进stack的时候, 想要在 bottom/后process的, 先加 +- 想要下一轮立刻process的, 最后push进stack. + +##### 注意 +这些binary tree traversal的题目.常常有多个做法:recursive or iterative + + + +--- + +**11. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**12. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**13. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**14. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**15. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**16. [Evaluate Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Reverse%20Polish%20Notation.java)** Level: Medium Tags: [Stack] + + +给一个 RPN string list, 根据这个list, 计算结果. + +#### Stack +- stack 里面 存数字 +- 每次遇到operator, 都拿前2个数字计算 +- 计算结果存回到stack里面, 方便下一轮使用. +- Time,Space O(n) + + + + +--- + +**17. [Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)** Level: Easy Tags: [Design, Stack] + + +如题. + +#### Queue, 倒水 +- 两个Queue,交互倒水 +- 用一个Temp做swap + +##### 做法1 +- 逻辑在push里面: +- 1. x 放q2。 +- 2. q1全部offer/append到q2. +- 3. 用一个Temp做swap q1, q2. +- q1的头,就一直是最后加进去的值. + + +##### 做法2 +- 逻辑在top()/pop()里, 每次换水,查看末尾项. + + + + +--- + +**18. [Implement Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack.java)** Level: Easy Tags: [Stack] + + +随便用一个data structure, implement stack. + +#### Stack, 先入, 后出 +- ArrayList: return/remove ArrayList的末尾项。 +- 2 Queues + + + +--- + +**19. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + +**20. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**21. [Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)** Level: Hard Tags: [Greedy, Hash Table, Stack] + + +#### Hash Table, Greedy +- count[] = int[256], 不需要 `c-'a'` +- boolean visited[]: 一旦一个字母固定了位置后, 再次遇到时候, 直接跳过用过的character +- 如果tail字母可以变小, 那就delete掉tail, 重新接上新字母 (前提条件: 去掉的字母后面还会再出现, set visited[tail] = false) +- Space: O(1) count[], visited[]. +- Time: Go through all letters O(n) + +#### Stack +- Use stack instead of stringBuffer: keep append/remove last added item +- However, stringBuffer appears to be faster than stack. + + + +--- + +**22. [Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)** Level: Hard Tags: [Coordinate DP, Stack, String] + + +给一串string, 里面只有`(`, `)`. 找最长valid parentheses 的长度. + +#### 1D Coordinate DP +- use dp[i] track local max, maintain global max +- int[] dp. dp[i]: longest valid string that ends on i. +- 结尾是 ')', 2种情况: 1. 刚好s[i-1]是'('; 2. s[i]的')'更前面的一个起始'(' 对应 +- 注意, 结尾如果是'('属于不合理情况, 忽略. +- init: dp[0] = 0, 单个char不可能成型. +- 计算顺序: 从左到右, 找local max, maintain global max +- O(n) space, O(n) runtime + +#### Stack +- Stack 里面存所有的open/close parentheses. +- 如果遇到stack.top()刚好开合结掉, 就stack.pop(). +- 剩下的都是不合理的elements. +- 有点像negatively找 solution: `endIndex - 最后一个failedIndex(stack.pop()) - 1`, 应该就是最后一个succeeded string的长度 +- 每次更新 endIndex 为stack.top(), 然后从stack继续找下一个failedIndex +- 所有的length作比较, 就可以找出最长length +- O(n) stack space, O(n) runtime. 应该比dp慢一点, 因为做了2遍O(n) + + + + +--- + +**23. [Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + +time: O(n) +space: O(n) + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path 的情况, 不懂得要问: +- 1. `.` 代表current directory, 可以忽略. +- 2. `../` 表示previous level. +- 3. double slash 可以忽略. +- 4. empty string 要output `/` +- 最终就是用stack (`上一个加进去的item, 用来备选pop() out`), 遇到 `../` pop()掉上一个加上去的item, 其余加进stack +- 最终用 '/' 把所有item连接起来. + + + +--- + +**24. [Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)** Level: Medium Tags: [Linked List, Stack, Tree] + +time: O(n) +space: O(n) + +给一个BST, convert成 sorted doubly DoublyListNode. + +#### Inorder Traversal, Linked List +- 会iterative traverse Binary Search Tree(Stack && handle left-dig-down) +- create Doubly-ListNode, 注意用一个dNode作为tail node of the list + +##### Iterative inorder traversal +- 在check right node的事后, +- 不论right == null or != null, 每次都要强行move to right. +- 如果不node = node.right, +- 很可能发生窘境: +- node always = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 + + + +--- + +**25. [Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + +time: O(n) +space: O(n) + +#### Queue +- 简单的level traversal.根据level奇数偶数而add到不同位子. +- Option1: based on level % 2, insert to front/end of list +- Option2: based on level, insert right/left of node into queue + + + +--- + +**26. [Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/Exclusive%20Time%20of%20Functions.java)** Level: Medium Tags: [Stack] + + +#### Stack +- 1. later function always appears after prior fn: 1 is called by 0 +- 2. `Not mentione in the question`: a function can be started multiple times +- 3. `Not mentione in the question`: a fn cannot start if children fn starts +- 4. Use stack to keep id +- TODO: what leads to the choice of stack? stacking fn id + + + +--- + +**27. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + + +--- + +**28. [Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +剥皮过程。解铃还须系铃人 +左边的外皮'{['在stack底部 +右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**29. [Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack? + + + +--- + + + + + + + +## KMP (1) +**0. [Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)** Level: Hard Tags: [KMP, String] + + +#### Divide by mid point, Brutle +- check (mid, mid+1), or (mid-1, mid+1). +- If the two position matches, that is a palindrome candidate +- 比较front string 是否是 end string 的substring +- O(n^2) +- timeout on last case: ["aaaaaa....aaaacdaaa...aaaaaa"] + +#### KMP +- TODO + + + +--- + + + + + + + +## PreSum (10) +**0. [Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)** Level: Review Tags: [Array, Binary Search, PreSum] + + +给int[] nums 和 window min size k. window size可以大于K. 找最大的连续数列average value. + +- Binary Search的思想, 用在所要找的这个 average sum 上面. 大小是在[min, max]之中 +- 找k的时候, 是>=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + + + +--- + +**1. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**2. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**3. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**4. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**5. [Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)** Level: Medium Tags: [Array, Hash Table, PreSum] + + +给一个int[][] matrix, 找一个sub matrix, where the sum == 0. + +#### PreSum的思想 +- 算出一个右下角点(i,j)到(0,0)的大小: 上一块 + 左一块 + curr node - overlap area +- preSum[i][j]: sum from (0,0) to (i-1,j-1) +- same approach as `subarray sum`: use hashmap to store diff->index; if diff re-appears, that means sum of 0 has occurred +- sequence of calculation: 1. iterate over start row. 2. iterate over end row. 3. iterate over col number (this is where hashmap is stored based on) +- the iteration over col is like a screening: find previous sum and determine result +- Note: 其实并没有真的去找 `== 0` 的解答,而是根据特性来判断 `剩下的/后来加上的一定是0` + + + +--- + +**6. [Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- 就是pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**7. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**8. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**9. [Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)** Level: Medium Tags: [Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +#### Map +- use `Map` to store inline preSum and its index. +- 1. Build presum incline +- 2. Use map to cache current preSum value and its index: `Map` +- 3. Each iteration: calculate possible preSum candidate that prior target sequence. ex: `(preSum - k)` +- 4. Use the calculated preSum candidate to find index +- 5. Use found index to calculate for result. ex: calculate range. + + + +--- + + + + + + + +## Binary Indexed Tree (3) +**0. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**1. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**2. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + + + + + + + +## Graph (11) +**0. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**1. [Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)** Level: Medium Tags: [BFS, Graph] + + +#### Graph + BFS +- Build graph `map` +- BFS to find the shortest path: when the neibhbor has the curr node as the only one neighbor, it is leaf. +- record shortest path in Map> as result +- TODO: code it up. + +#### Previous Solution +- removing leaf && edge + + + +--- + +**2. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**3. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**4. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**5. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**6. [Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph] + + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes. +- 先能复制多少Node复制多少. 然后把neighbor 加上 +- Use `map` to mark visited + +#### DFS +- Given graph node obj `{val, list of neighbor}`: copy the node and all neighbors +- Mark visited using map +- for loop on the each one of the neighbors: map copy, record in map, and further dfs +- once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +- 主要思想是: 一旦复制过了, 不必要重新复制 + +#### BFS +- Copy the root node, then copy all the neighbors. +- Mark copied node in map. +- Use queue to contain the newly added neighbors. Need to work on them in the future. + + + +--- + +**7. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + + +--- + +**8. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**9. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**10. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + + + + + + + +## Brainteaser (1) +**0. [Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)** Level: Easy Tags: [Brainteaser, DP, Game Theory] + + +#### Brainteaser +- 著名Nim游戏 +- 写一些,发现n=4,5,6,7,8...etc之后的情况有规律性: 谁先手拿到4就输了. +- 最终很简单n%4!=0就可以了, time, space O(1) + +#### DP +- 正规地找规律做, 就跟 coins in a line 一样, 按照先手后手来做 +- 可以rolling array 优化空间 +- Time O(n), 当然啦, 这个题目这样会timeout, 可以使用brainteaser的做法写出结果. + + + +--- + + + + + + + +## Union Find (15) +**0. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**1. [Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)** Level: Medium Tags: [Union Find] + + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + + + +--- + +**2. [Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)** Level: Medium Tags: [Union Find] + + +Lint还不能跑, 全部按照题意和答案document的. + + + +--- + +**3. [Connecting Graph III.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20III.java)** Level: Medium Tags: [Union Find] + + +还是UnionFind的变形, 这次是算有剩下多少个union. 其实非常简单, 维持一个全局变量count: +一开始count=n, 因为全是散装elements; 每次union, count--. + + + +--- + +**4. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**5. [Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands%20II.java)** Level: Hard Tags: [Union Find] + + +给一个island grid[][], and list of operations to fill a particualr (x,y) position. + +count # of remaining island after each operation. + +#### Union Find, model with int[] +- 把board转换成1D array, 就可以用union-find来判断了. +- 用int[] father 的unionFind, 需要转换2D position into 1D index. 这样比较clean +- 判断时,是在四个方向各走一步,判断是否是同一个Land. +- 每走一次operator,都会count++. 若发现是同一个island, count-- +- count的加减, 都放在了UnionFind自己的function里面, 方便tracking, 给几个helper function就对了. +- Time: O(k * log(mn)) + +#### Union Find, model with Hashmap +- 用HashMap的Union-find. + + +#### Note: +- Proof of UnionFind log(n) time: https://en.wikipedia.org/wiki/Proof_of_O(log*n)_time_complexity_of_union%E2%80%93find + + + +--- + +**6. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + +**7. [Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)** Level: Hard Tags: [Array, Hash Table, Union Find] + + +给一串数字, unsorted, 找这串数字里面的连续元素序列长度 (consecutive序列, 是数字连续, 并不是说要按照原order) + +#### HashSet +- 要想看连续元素, 必须要num++, num--这样搜索 +- 1. 需要O(1)找到元素 +- 2. 需要简单快速找到 num - 1, num + 1. +- 如果用min,max开array, 耗费空间 +- 用HashSet来存, 用set.contains() 来查找 num - 1, num + 1 存在与否 +- for loop. O(n) +- 里面的while loop 一般不会有O(n); 一旦O(n), 也意味着set 清零, for loop也不会有更多 inner while 的衍生. +- overall O(n) 时间复杂度 + + +#### Union Find +- 最终是要把相连的元素算一下总长, 其实也就是把元素group起来, 相连的group在一起, 于是想到UnionFind +- 这里用到了一个`int[] size` 来帮助处理 `合并的时候parent是哪个`的问题: 永远往group大的union里去 +- main function 里面, 有一个map来track, 每个元素, 只处理1遍. +- union的内容: current number - 1, current number + 1 +- https://www.jianshu.com/p/e6b955ca208f + +##### 特点 +- Union Find 在index上做好像更加容易 +- 其他union find function: `boolean connected(a,b){return find(a) == find(b)}` + + + +--- + +**8. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**9. [Find the Weak Connected Component in the Directed Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Weak%20Connected%20Component%20in%20the%20Directed%20Graph.java)** Level: Medium Tags: [Union Find] + + +遍历 weak connected graph, 将结果存在 List>种. + +#### Union Find +- 跟传统的UnionFind有两点不同: +- 1. 用 Map 代替 int[], 因为没有给出 graph node label的 boundary. +- 2. find(x)时候, 没有去update `parent[x]/map.put(x, ..)`. 因为我们最终需要找到这个path. +- 无法用传统dfs: directed node 无法point到上一个点; 必须用`存parent的方式把所有node遍历掉` + +#### Identify这是个union-find问题 +- 看到了weak component的形式: 一个点指向所有,那么所有的点都有一个公共的parent,然后就是要找出这些点。 +- 为何不能从一个点出发,比如A,直接print它所有的neighbors呢: +- 如果轮到了B点,那因为是directed,它也不知道A的情况,也不知道改如何继续加,或者下手。 +- 所以,要把所有跟A有关系的点,或者接下去和A的neighbor有关系的点,都放进union-find里面,让这些点有Common parents. +- 最后output的想法: +- 做一个 map 。 +- 之前我们不是给每个num都存好了parent了嘛。 +- 每个num都有个parent, 然后不同的parent就创造一个不同的list。 +- 最后,把Map里面所有的list拿出来就好了。 + + + +--- + +**10. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**11. [Bricks Falling When Hit.java](https://github.com/awangdev/LintCode/blob/master/Java/Bricks%20Falling%20When%20Hit.java)** Level: Hard Tags: [Union Find] + + +给一个matrix of 1 and 0, `1` 代表brick. 连着ceiling的brick就不会drop. 给一串coordinate hits[][], 记录每次take down 1 brick 后, 会drop多少个. + +#### UnionFind +- 1. 我们知道大部分的brick可能都是连着ceiling, 所以每次正向检查都traverse all and timeout +- 2. 能否用union, 把connect都装在一起, 然后drop brick的时候把连着的都drop掉? 难: 因为还是要check所有brick当下的status. +- 受其他人的解答启发, 由于是计算count,我们可以`反向考虑`: +- 把hit-brick全部mark=2 (就当舍弃不算), 观察整个局面的最后一步, 先把所有还连着ceiling的brick算一下总数, 统计在unionFind的 全部统计在count[0] 里面. +- 剩下的不连着ceiling的也就是一个个isolated island +- 做法: 把hit-brick 一个个加回去, 然后再做一次union, 看看最终连到ceiling的有多少个. 增加的count, 就是正向思考时 dropped brick 数量! + +##### Union Find 变种 +- 还是用数字index做union find, 但是把每一个index都+1, 右移一位, 而[0]留下来做特殊用途: +- 用union at 0来 统计总共的remain count of ceiling-connected bricks, where `x = 0`. +- 如果在其他其他题目种, 条件可能就不是`x=0`, 但也可以用这个 union index = 0 来做一个root的统计 +- 关键: 把最后一个hit brick加回去, 然后再重新union一下这个hit-brick周围: count增加的变化, 不就是缺少hit-brick时候掉下去的数量. + + +#### DFS (timeout) +- 考虑每个hit的四周, 全部traverse, 没有连着ceiling就全部: +- 比如是 200 x 200 的 全部是1的matrix, 任何一次traverse都要到顶; 重复计算, 所以timeout +- 算法是没错, 但是不efficient. +- 想要减少重复计算, 但是又不能提前计算: grid在不断变化. 所以看能不能把连着ceiling的都group起来, 可以O(1)快速check? + + + + +--- + +**12. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**13. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**14. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + + + + + + + +## Matrix DFS (2) +**0. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**1. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + + + + + + + +## Sweep Line (5) +**0. [Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### 方法1: +找是否有overlap. priorityQueue 按照start time排序好以后, 比较current和peek: current.end > peek.start? + +#### 方法2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 + + + +--- + +**1. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**2. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**3. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**4. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + + + + + + + +## Subarray (11) +**0. [Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### DP +- 求最值, 想到DP. Time/Space O (n) +- 两个特别处: +- 1. 正负数情况, 需要用两个DP array. +- 2. continuous prodct 这个条件决定了在Math.min, Math.max的时候, +- 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. +- 这也就注定了需要一个global variable 来hold result. + +#### Space optimization, rolling array +- maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins +- Time: O(n), space: O(1) + + + +--- + +**1. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + +time: O(n) +space: O(1) + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + + + +--- + +**2. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**3. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**4. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**5. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**6. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**7. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**8. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**9. [Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)** Level: Medium Tags: [Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +#### Map +- use `Map` to store inline preSum and its index. +- 1. Build presum incline +- 2. Use map to cache current preSum value and its index: `Map` +- 3. Each iteration: calculate possible preSum candidate that prior target sequence. ex: `(preSum - k)` +- 4. Use the calculated preSum candidate to find index +- 5. Use found index to calculate for result. ex: calculate range. + + + +--- + +**10. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + + + + + + + +## Interval (1) +**0. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + + + + + + + +## Sequence DP (21) +**0. [Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP] + + +给两个string, A, B. 找这两个string里面的LCS: 最长公共字符长度 (不需要是continuous substring) + +#### Double Sequence DP +- 设定dp长度为(n+1), 因为dp[i]要用来表示前i个(ith)时候的状态, 所以长度需要时i+1才可以在i位置, hold住i. +- 双序列: 两个sequence之间的关系, 都是从末尾字符看起, 分析2种情况: +- 1. A最后字符不在common sequence 或者 B最后字符不在common sequence. +- 2. A/B最后字符都在common sequence. 总体count + 1. + + + +--- + +**1. [Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + +time: O(MN) +Space: O(N) + +两个字符串, A要变成B, 可以 insert/delete/replace, 找最小变化operation count + +#### Double Sequence +- 考虑两个字符串的末尾index s[i], t[j]: 如果需要让这两个字符一样, 可能使用题目给出的三种operation: insert/delete/replace? +- 先calculate最坏的情况, 3种operation count + 1; 然后在比较match的情况. +- 注意, 在i或者j为0的时候, 变成另外一个数字的steps只能是全变. +- 第一步, 空间时间都是O(MN), O(MN) +- 滚动数组优化, 空间O(N) + +##### Detail analysis +- insert: assume insert on s, `#ofOperation = (s[0 ~ i] to t[0 ~ j-1]) + 1;` +- delete: assume delete on t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j]) + 1;` +- replace: replace both s and t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j - 1]) + 1;` +- dp[i][j]代表了两个 sequence 互相之间的性质: s[0 ~ i] 转换成 s[0~j] 所需要的最少 operation count +- init: 当i==0, dp[0][j] = j; 每次都要 + j 个character; 同理, 当j==0, dp[i][0] = i; +- 而dp[i][j]有两种情况处理: `s[i] == t[j]` or `s[i] != t[j]` + +##### 何时initialize +- 这种判断取决于经验: 如果知道initialization可以再 double for loop 里面一起做, 那么可以留着那么做 +- 这样属于 `需要什么, initialize什么` +- 事后在做space optimization的时候, 可以轻易在 1st dimension 上做rolling array + +#### Search +- 可以做, 但是不建议:这道题需要找 min count, 而不是search/find all solutions, 所以search会写的比较复杂, 牛刀杀鸡. + + + +--- + +**2. [Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)** Level: Easy Tags: [DP, Memoization, Sequence DP] + + +每一步可以走1步或者2步, 求总共多少种方法爬完梯子. + +#### Recursive + Memoization +- 递归很好写, 但是重复计算, timeout. time: O(2^n) +- O(2^n): each n can spawn 2 dfs child, at next level, it will keep spawn. Total 2^n nodes will spawn. +- 用全局变量int[] memo 帮助减少重复计算 +- O(n) time, space + +#### DP +- 加法原理, 最后一步被前两种走法决定: dp[i] = dp[i - 1] + dp[i - 2] +- 基础sequence DP, int[] dp = int[n + 1]; +- DP[]存的是以 1-based index的状态 +- dp[i]: count # of ways to finish 前i个 台阶 +- 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1] +- dp[0]往往是有特殊状态的 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**3. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**4. [Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + +time: O(nm), m = # of colors +space: O(nm) + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, 但是不知道最后一个房子选什么颜色, 那么就遍历最后一个房子(i - 1)的颜色 +- 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- 考虑DP最后一个位置的情况(颜色选择):需要附带颜色status在DP[i]上: 定义二维数组, 其中一位是status +- dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. +- dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- 然后选ith 房子的 color, 再选(i-1)th 房子的color. Double for loop, skip same color + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 + + + +--- + +**5. [House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) or rolling array O(1) + +搜刮房子, 相邻的不能碰. 每个房子里有value, 求max. + +#### Sequence DP +- dp[i]: 前i个房子拿到的max gain +- 看最后结尾状态的前一个或前两个的情况,再综合考虑当下的 +- 搞清楚当下[i]的和之前[i-x]的情况的关系: 不可以连着house, 那么就直接考虑 dp[i-2]的情况 +- Sequence DP, new dp[n + 1]; + +#### Rolling Array +- [i]'只和前两个位子 [i-1], [i - 2]'相关 +- 用%2来标记 [i], [i - 1], [i - 2]三个位置. +- 其他滚动时惯用curr/prev来表示坐标, 这里%2虽然抽象, 但是更加实用. + + + + +--- + +**6. [House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)** Level: Medium Tags: [DP, Sequence DP, Status DP] + + +和House Robber I 类似, 搜刮房子, 相邻不能动. 特点是: 现在nums排成了圈, 首尾相连. + +#### Sequence DP +- dp[i][status]: 在 status=[0,1] 情况下, 前i个 房子拿到的 max rob gain. status=0, 1st house robbed; status=1, 1st house skipped +- 根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]); +- 特别的是,末尾的last house 和 first house相连. 这里就需要分别讨论两种情况: 第一个房子被搜刮, 或者第一个房子没被搜刮 +- be careful with edge case nums = [0], only with 1 element. +- Time,space: O(n) + +#### 两个状态 +- 是否搜刮了第一个房子, 分出两个branch, 可以看做两种状态. +- 可以考虑用两个DP array; 也可以加一dp维度, 补充这个状态. +- 连个维度表示的是2种状态(1st house being robbed or not); 这两种状态是平行世界的两种状态, 互不相关. + +#### Rolling array +- 与House Robber I一样, 可以用%2 来操作rolling array, space reduced to O(1) + + + +--- + +**7. [Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + +time: O(NK^2): +space: (NK) + +一排n个房子, 每个房子可涂成k种颜色, 涂每个房子的价钱不一样, 用costs[][]表示. + +costs[0][1]表示涂了index是0的房子, 用了color 1. + +规则: 相邻的两个房子不能使同一种颜色 + +求: 最少的cost + +#### DP +- 跟Paint House I 几乎一模一样, 只不过paint color更多了: k colors. +- 先考虑单纯地用dp[i]表示涂前 i 个房子的最小cost +- 但是 dp[i] 和 dp[i-1] 两个index选什么颜色会互相影响, 难讨论, 于是加状态: 序列DP被加了状态变成2D. +- 考虑最后位, 而前一位i-1又被i位的颜色限制, 于是在考虑 min dp[i] 时候, 又多了一层iteration. +- 做dp[i][j]: # cost for 前 i 个房子, 所以要先pick (i-1) 房子的cost, 然后在找出 (i-2)房子的cost +- K种颜色 => O(NK^2) +- 如果不优化, 跟Paint House I 几乎是一模一样的代码 +- Time O(NK^2), space(NK) +- Rolling array: reduce space to O(K) + +#### 注意 +- 序列型dp[i]表示'前i-1个'的结果. 所以dp最好设定为 int[n + 1] size. +- 然而, 颜色在这里是状态, 所以保留在 j: [ 0~k) +- [[8]] 这样的edge case. 跑不进for loop, 所以特殊handle. + +#### Optimization Solution +- Time: O(NK) +- 如果已知每次都要从cost里面选两个不同的最小cost,那么先把最小两个挑出来, 就不必有第三个for loop 找 min +- 每次在数列里面找: 除去自己之外的最小值, 利用最小值/次小值的思想 +- 维持2个最值: 最小值/次小值. +- 计算的时候, 如果除掉的不是最小值的index, 就给出最小值; 如果除掉的是最小值的index, 就给出次小值. +- Every loop: 1. calculate the two min vlaues for each i; 2. calcualte dp[i][j] +- 如何想到优化: 把表达式写出来, 然后看哪里可以优化 +- 另外, 还是可以rolling array, reduce space complexity to O(K) + + + +--- + +**8. [Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)** Level: Hard Tags: [Array, DP, Sequence DP] + + +比stock II 多了一个限制:只有2次卖出机会. + +#### DP加状态 +- 只卖2次, 把买卖分割成5个状态模块. +- 在状态index 0, 2, 4: 没有持有股票. 1. 一直在此状态, max profit不变; 2. 刚卖掉, dp[i][前状态] + profit +- 在状态index 1, 3: 持有股票. 1. 一直在此状态, daily profit. 2. 刚刚买进, 状态改变, 但是没有profit yet: dp[i][前状态] + +##### Partial profit +- 把每天的partial profit (diff)加在一起, 最终的overall profit是一样的. 唯一更好的是, 不需要记录中间买入的时间点. +- 什么时候会积累profit呢? +- 1. 原本就持有股票的, 如果毫无动作, 那么状态不变, 积累profit diff. +- 2. 卖出了股票, 状态改变, 积累profit diff. +- 注意: 只有在状态index: 0, 2, 4, 也就是卖掉股票的时候, 才可以积累profit + +##### Rolling Array +- [i] 只有和 [i-1] 打交道, reduce space +- O(1) space, O(n) time + +#### 找峰头 +- 找峰头;然后往下再找一个峰头。 +- 怎么样在才能Optimize两次巅峰呢?从两边同时开始找Max!(棒棒的想法) +- leftProfit是从左往右,每个i点上的最大Profit。 +- rightProfit是从i点开始到结尾,每个点上的最大profit. +- 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 +- 三个O(n),还是O(n) + + + +--- + +**9. [Best Time to Buy and Sell Stock IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20IV.java)** Level: Hard Tags: [DP, Sequence DP] + + +有int[] price of stock, 最多做 k transactions. 求最大profit. + +#### DP +- 根据StockIII, 不难发现StockIV就是把状态划分为2k+1份. 那么同样的代码, 移植. + +##### 注意1: +- 如果k很大, k>n/2, 那么长度为n的数组里面, 最多也只能n/2个transaction +- 那么题目简化为stockII, 给n数组, 无限次transaction. +- 注意, status的数量是 2k+1 +- Time O(NK), Space O(2k+1) to store the status + +##### 注意2: +- 最后状态是'没有stock'的都该考虑, 做一个 for 循环比较max. +- 当然, 来一个profit variable, 不断比较, 也是可以的. + +#### 方法2 +- (previous notes, 熟练第一种方法的思考就可以) +- 记得要理解:为什么 i-1天的卖了又买,可以和第 i 天的卖合成一次交易? +- 因为每天交易的price是定的。所以卖了又买,等于没卖!这就是可以合并的原因。要对价格敏感啊少年。 +- Inspired from here: http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html + +##### 局部最优解 vs. 全局最优解: +- local[i][j] = max(global[i – 1][j – 1] + diff, local[i – 1][j] + diff) +- global[i][j] = max(global[i – 1][j], local[i][j]) +- local[i][j]: 第i天,当天一定进行第j次交易的profit +- global[i][j]: 第i天,总共进行了j次交易的profit. + +- local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生。 +- 当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff; +- 当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。 +- (Note:在我下面这个solution里面没有省去 +diff) + +- global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况: +- 如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j]; +- 如果第i天有交易(卖出),那么global[i][j]=local[i][j]。 + + + + + +--- + +**10. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**11. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**12. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**13. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) + +#### DP +- 最多2个fence 颜色相同 +- 假设i是和 i-1不同,那么结果就是 (k-1)*dp[i - 1] +- 假设i是何 i-1相同,那么根据条件,i-1和i-2肯定不同。那么所有的结果就是(k-1)*dp[i-2] +- dp[i]: count # of ways to paint 前i个 fence +- 加法原理 +- time, space: O(n) +- rolling array: space O(1) + +#### Previous Notes +- 这题目很有意思. 一开始分析的太复杂, 最后按照这个哥们的想法(http://yuanhsh.iteye.com/blog/2219891) 的来做,反而简单了许多。 +- 设定T(n)的做法,最后题目化简以后就跟Fibonacci number一样一样的。详细分析如下。 +- 做完,还是觉得如有神。本来是个Easy题,想不到,就是搞不出。 + + + + +--- + +**14. [Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- true/false problem, think about dp +- 子问题: 前i个字母, 是否可以有valid break +- 检查dp[j] && `if substring(j, i) valid`, for all j = [0 ~ i] +- dp = new boolean[n + 1]; dp[0] = true; +- goal: if there is a j, `dp[j] == true && word[j, n] in dict`. Need iterate over i = [0 ~ n], also j = [0, i] +- 注意, 用set代替list, 因为要用 contains(). + +#### Previous notes +##### 方法2(attempt4 code) +- 与Word BreakII用同样的DP。 +- valid[i]: 记录从i到valid array末尾是否valid. + +##### 方法1:(attempt3 code) +- state,rst[i]: 从[0~i] inclusive的string是否可以在dict中break开来找到? +- function: rst[i] = true if (rst[i - j] && set.contains(s.substring(i - j, i))); j in[0~i] +- 1. rst[i - j] 记录的是[0, i-j]这一段是否可以break后在dict找到。 +- 2. 若true,再加上剩下所有[i-j, i]都能在dict找到,那么rst[i] = rst[0, i - j] && rst[i-j, i] == true +- 优化:找dict里面最长string, 限制j的增大。 + + + + +--- + +**15. [Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### 理解意思是关键 +- 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出 +- 记录每天最小值Min是多少. O(n) +- 每天都算和当下的Min买卖,profit最大多少. + +#### DP +- Find min value for first i items, new dp[n + 1]. +- dp[i]: 前i天, prices最小的price是多少: min cost of first i days +- 然后用当天的price做减法dp[i]算max profit. +- Time, Space: O(n) +- 更进一步, 用一个min来表示min[i], 因为计算中只需要当下的min. + +#### Rolling array +- index i only depend on [i - 2] +- Space O(n) + +#### Brutle Failed +- 每天都试着买进,然后之后的每一天尝试卖出. double for loop, O(n^2). timeout. +- 其中很多都是没必要的计算:[7, 1, 5, 3, 6, 4]。 if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. + + + +--- + +**16. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**17. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**18. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**19. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**20. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + + + + + + + +## Minimum Binary Tree (3) +**0. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**1. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**2. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + + + + + + + +## Bitwise DP (1) +**0. [Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)** Level: Medium Tags: [Bit Manipulation, Bitwise DP, DP] + + +给一个数组, 算里面有多少bit 1. + +#### Bitwise DP +- 对于每一个数字, 其实很简单就能算出来: 每次 >>1, 然后 & 1 就可以count 1s. Time: 一个数字可以 >>1 O(logN) 次 +- 现在要对[0 ~ num] 都计算, 也就是N个数字, 时间复杂度: O(nLogN). +- 用DP来优化, 查找过的number的1s count, 存下来在 dp[number]里面. +- 计算你顺序从 0 -> num, count过的数字就可以重复利用. +- Bit题目 用num的数值本身表示DP的状态. +- 这里, dp[i] 并不是和 dp[i-1]有逻辑关系; 而是dp[i] 和dp[i>>1], 从binary representation看出有直接关系. + + + +--- + + + + + + + +## MiniMax (2) +**0. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**1. [Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)** Level: Medium Tags: [DP, MiniMax] + + +Detailed in `Coins in a Line III` + + + +--- + + + + + + + +## Two Pointers (38) +**0. [Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)** Level: Easy Tags: [Array, Two Pointers] + + +- 更正常的start/end partition pointer类似: when condition meet, swap +- Clean up TODO + + + +--- + +**1. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + + +--- + +**2. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + + +--- + +**3. [Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i target,那么其中所有num[left++]的加上num[right]都>target. +也就是,num[right]不动,计算加入挪动left能有多少组,那就是: right-left这么多。 全部加到count上去。 +然后right--.换个right去和前面的left部分作比较。 + + + +--- + +**6. [3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/3%20Sum%20Closest.java)** Level: Medium Tags: [Array, Two Pointers] + + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**7. [3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum.java)** Level: Medium Tags: [Array, Two Pointers] + + + +#### sort array, for loop + two pointer. O(n^2) +- 处理duplicate wthin triplets: +- 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. +- 如果是triplet内有重复, 用完start point, 移动到结尾. + +Previous notes: +注意: + 1. 找 value triplets, 多个结果。注意,并非找index。 + 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 + +步骤: + 1. For loop 挑个数字A. + 2. 2Sum 出一堆2个数字的结果 + 3. Cross match 步骤1里面的A. + +时间 O(n^2), 两个nested loop + +另外, 还是可以用HashMap来做2Sum。稍微短点。还是要注意handle duplicates. + + + + +--- + +**8. [3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum%20Smaller.java)** Level: Medium Tags: [Array, Two Pointers] + + +一般的O(n3)肯定不行。在此基础上优化。 +发现j,k满足条件时候,(k - j)就是所有 sum target, 又因为j不能后退,只能k--,那么问题就被锁定了. 这样可以做到O(n2) + + + +--- + +**9. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**10. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**11. [Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)** Level: Medium Tags: [Hash Table, String, Two Pointers] + + +方法1: +Two Pointers +双指针: 从start开始遍历, 但是第一步是while loop来推进end; 直到推不动end, 然后start++ +巧妙点: 因为end是外围variable, 在start的loop上, end不会重置;[star ~ end] 中间不需要重复计算. +最终可以O(n); + +Previous verison of two pointers: +用两个pointer, head和i. +注意:head很可能被退回到很早的地方,比如abbbbbba,当遇到第二个a,head竟然变成了 head = 0+1 = 1. +当然这是不对的,所以head要确保一直增长,不回溯 + +方法2: + HashMap: + 一旦有重复, rest map. + 没有重复时候, 不断map.put(), 然后求max值 + +问题: 每次reset map之后就开始从新从一个最早的index计算, 最坏情况是O(n^2): +'abcdef....xyza' + + + + +--- + +**12. [Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, String, Two Pointers] + + +基本思想: 用个char[]存string的frequency. 然后2pointer, end走到底, 不断validate. +符合的就process as result candidate. + +HashMap的做法比char[]写起来要复杂一点, 但是更generic + + + +--- + +**13. [Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Two Pointer: Slow Fast Pointer +- O(1) sapce: 用快慢指针。一个跑.next, 一个跑.next.next。 总有一次,fast会因为cycle而追上slow。 +- 那个时候其实slow.val = fast.val. + +#### Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**14. [Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +O(n), one pace, no extra space +找到窗口, 然后平移, 最后pre 和 head之间 skip一个node就好. + + + +--- + +**15. [Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Linked List, Math, Two Pointers] + + +LinkedList 里面有 cycle, 找到cycle的起始点(第一个重复出现的element). + +#### Slow, fast Pointer +- 快慢指针, O(1)space. +- 1. 确认有cycle后 2. 数学问题:找到开头. +- 当head == slow.next时候, head就是cycle starting point. +- 也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +#### 证明 +- 1. 假设慢指针走t步, 快指针走快一倍, 也就是2t. +- 2. 我们假设cycle的长度是Y, 而进入cycle之前的长度为X. +- 3. 假设慢指针走了m圈cycle, 而快指针走了n圈cycle之后, 两个pointer相遇. +- 4. 最终在Y cycle里面的K点相遇, 也就是两个指针都在这最后一圈里面走了K 步. +- 那么: +- t = X + mY + K +- 2t = X + nY + K +- 整合公式: X + K = (n - 2m)Y +- 这里的m和n不过是整数的跑圈数, 也就是说X和K加在一起, 总归是结束cycle. X 和 K 互补 +- 结论: 当slow/fast 指针在K点相遇后, 再走X步, 就到了cycle的起点, 也就是题目要求的起点. + +#### Hash Table, O(n) space + + + + +--- + +**16. [Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. +#### 方法1 +Array, 维持一个左手最高墙array, 右手最高强array. +对于每个index而言, vertically 能存放的最大水柱, 就是靠左右最高墙决定的: +min(leftHighestWall, rightHighestWall) - currHeight. + +#### 方法2 +方法1上面的优化, two pointer, 还是找左边最高和右边最高. O(1) space. +利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +左边按照maxLeft来计算, 右边按照maxRight来计算. + +#### 方法3 +2 Pointers, 双面夹击: +1. 找中间最高bar的index +2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条。 +3. 每次还要减去block自身的height + +#### 方法4 +主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. + + + + +--- + +**17. [Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +- 注意不要思维定式: 以为mid是index +- 这里mid其实是binary search on value [1, n] 的一个value. +- 再次用到validate() function + +Time: O(nLogN) + + + +--- + +**18. [Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20in%20String.java)** Level: Medium Tags: [Two Pointers] + + +#### Two Pointer +- 如果做s1的permudation, 时间复杂度是O(n!) 肯定不可以 +- 这里用HashTable的做法 (因为26字母, 所以用int[26]简化) 来记录window内的 character count +- 如果window内的character count 相等, 那么就是permudation +- 更进一步优化: 找两个map相互对应, 不如用一个 int[26]: s1对遇到的character做加法, s2对遇到的character做减法 +- two pointer 运用在 n1, n2 的把控; 以及 s2.charAt(i - n1) 这一步 + + + +--- + +**19. [Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20strStr().java)** Level: Easy Tags: [String, Two Pointers] + + +给两个string A, B, 找一个 B 在 A 种的起始位置. + +#### Two Pointer +- 找到B在A中的起始位置, 然后看一下从这个点开始的substring是否等于B就可以了 +- 还挺多坑的, 这些可以帮助优化: +- 1. 当B是“”的时候,也就是能在A的其实位置找到B....index = 0. +- 2. edge condition: 如果 haystack.length() < needle.length() 的话, 必须错, return -1 +- 3. 如果在某个index, A后面剩下的长度, 比B的长度短, 也是误解, return -1 + + + +--- + +**20. [Interleaving Positive and Negative Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20Positive%20and%20Negative%20Numbers.java)** Level: Medium Tags: [Two Pointers] + + +给一串数组 有正负数. 重新排列, 让数组里面 正数 和 负数 相隔开. 原来的order无所谓 + +#### Two pointer +- 需要知道正负的位置, 所以排序 O(nlogN) +- 考虑: 正数多还是负数多的问题, 举栗子就看出来端倪了 +- 然后Two Pointer, swap +- Time O(nlogn), space O(n) + +#### extra space +- 用extra O(n) space, 把正负分成两个list +- 然后分别按照index填回去 +- time O(n). space O(n) +- 但是就么有用到Two pointer了 + + + +--- + +**21. [Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单, 但是要在Linkde List random access坐标, 是很难得: 所以需要把一半 ListNode 翻转 +- reverse linked list: 遍历接开头 +- 用快慢指正找到mid point +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + +#### Previous Note +- Palindrome都是要两边回溯相等 +- linkedlist不能reverse iterating, 那么就reverse the list, 从中间开花作比较。 + + + +--- + +**22. [Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Check Palindrome +- 前后两个指针, 往中间移动, 查看是否字母重合 + +#### 过滤 alphanumeric +- 可以用 ASCII code 来手动过滤, 只要 '0' ~ '9', 'a' ~ 'z', 'A' - 'Z' 之间的 +- 也可以用 regular expression: match 所有这些字母, 是 [a-zA-Z0-9] +- 那凡是不是这些字母的 match, 就是取反: "[^a-zA-Z0-9]". 测试: https://regex101.com/ + + + +--- + +**23. [Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- Two pointers 其实也可以是一个 for loop pointer, 另一个 dynamic variable. +- track unique index +- skip duplicated items +- O(n) + +#### 思考模式: +- Remove Duplicate from Array 不同于remove from linked list. +- LinkedList里面我们是最好不要动node.val的,直接把node去掉。 +- 而array我们很难直接把node去掉,又不能用新array,那么就要: +- 把不重复的element一个个放到最前面。 +- 这个思想跟merge two sorted array (其中一个后续非常长的array可以放下arr1,arr2) 类似。 +- 就是找个不会事后mess up,不会去动得index,把满足条件的element 填进去。这样保证了in place. +- *反向思维*:remove duplicate, 实际上也是找unique elements, and insert into original array + + + +--- + +**24. [Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- 跟 `Remove Duplicates from Sorted Array` 几乎一模一样, 只不过unique index现在可以 validate 2 位 +- 其余一模一样, use index to track unique item; skip if duplicated for more than 2 times +- O(n) time, O(1) space +- 这里也可以真的用2个pointers 写while loop, 但是没有必要, 只是单纯地走一个for loop其实就足够. + + + +--- + +**25. [Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +给一个single linked list, 右移k steps. k non-negative. + +#### Linked List basics +- 记得用dummy.next来存head. +- 特殊: 这里k可能大于list总长. 写一写linked node 移动的步数, 然后 k = k % n. +- 找到newTail, newHead, 然后利用dummy, 换位子 + + + +--- + +**26. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**27. [Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Two Pointers +- 木桶理论。盛水的最高取决于最低的那面墙。 +- 左右两墙,往中间跑动。 +- 另:若一面墙已经小于另外一面,就要移动,换掉矮墙(可能下一面更高,或更低) +- 但决不能换掉当下的高墙,因为低墙已经limit的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**28. [Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Linked List +- linked list 不能像partitioin array一样从两边遍历 +- 把小于value的加在前半段, 把 >= value的加在后半段 +- 做法很普通: 建造两个list, midTail pointer, post pointer +- 把满足条件(=x)的数字分别放到两个list里面 +- 记得用dummyNode track head. +- 最终midTail.next = post链接起来。 + + + +--- + +**29. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**30. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**31. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + +**32. [Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素。 +- 注意,从尾部,是大数字优先排末尾的. + + + +--- + +**33. [Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Move%20Zeroes.java)** Level: Easy Tags: [Array, Two Pointers] + + +Move non-zero elements to front of array; preseve order. + +#### Two Pointers +- Outside pointer that moves in certain condition. +- Save appropirate elements + + + +--- + +**34. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + + +--- + +**35. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**36. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**37. [Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)** Level: Easy Tags: [Math, String, Two Pointers] + + +#### Two pointers +- Use two pointers i, j to track the 2 strings +- Add when i and j are applicable. While (i >= 0 || j >= 0) +- StringBuffer.insert(0, x); +- handle carry + +#### wrong: convert to int +- 土办法没技术,把binary换成数字,加起来,再换成binary +- 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + + + + + + + +## Basic Implementation (3) +**0. [Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)** Level: Easy Tags: [Basic Implementation] + + +根据 Cosine Similarity 的公式, basic implementation + + + +--- + +**1. [Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)** Level: Easy Tags: [Basic Implementation, String] + + +介绍一种count数字的方法, 然后每一行读出上一行的结果, 一行一行推算. 问nth行是啥样? + +#### Basic Implementation +- 主要是题意很难理解, 非常misleading, 等到看明白题目, 其实没有什么算法要求. +- Count duplicates and print + + + +--- + +**2. [Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)** Level: Medium Tags: [Basic Implementation, Enumeration, String] + + +给一个时间string"12:09", 用里面的4个integer组合成其他时间string, 目标找最小的next time. + +如果组合出的time string 在input time之前, 默认 + 24 hours. + +#### String +- enumerate all candidates and filter to keep the correct ones +- String.compareTo(string) -> gives lexicographical comparision + + + +--- + + + + + + + +## Backpack DP (8) +**0. [Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + +#### DP +- 找对方程dp[x], 积累到amount x最少用多少个coin: #coin是value, index是 [0~x]. +- 子问题的关系是: 如果用了一个coin, 那么就应该是f[x - coinValue]那个位置的#coins + 1 + +##### initialization +- 处理边界, 一开始0index的时候, 用value0. +- 中间利用Integer.MAX_VALUE来作比较, initialize dp[x] +- 注意, 一旦 Integer.MAX_VALUE + 1 就会变成负数. 这种情况会在coin=0的时候发生. + +##### Optimization +- 方法1: 直接用Integer.MAX_VALUE +- 方法2: 用-1, 稍微简洁一点, 每次比较dp[i]和 dp[i - coin] + 1, 然后save. 不必要做多次min比较. + +#### Memoization +- dp[i] 依然表示: min # of coints to make amount i +- initialize dp[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录dp[amount] 如果已经给过value, 不要重复计算, 直接return. +- 但是这道题没必要强行做memoization, 普通DP的状态和方程相对来说很好找到 + + + +--- + +**1. [Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)** Level: Medium Tags: [Backpack DP, DP] + + +给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法. + +nums 里的数字, 可以重复使用. 不同的order可以算作不同的拼法. + +#### Backpack DP +- dp[i] 表示: # of ways to fill weight i +- 1维: dp[w]: fill weigth w 有多少种方法. 前面有多少种可能性, 就sum多少个: +- dp[w] = sum{dp[w - nums[i]]}, i = 0~n + +##### 分析 +- 拼背包时, 可以有重复item, 所以考虑'最后被放入的哪个unique item' 就没有意义了. +- 背包问题, 永远和weight分不开关系. +- 这里很像coin chagne: 考虑最后被放入的东西的value/weigth, 而不考虑是哪个. + + + + + + +--- + +**2. [Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change%202.java)** Level: Medium Tags: [Backpack DP, DP] + + +给串数字, target amount, 求总共多少种方式可以reach the amount. + +#### DP +- O(MN): M, total target amount; N: size of coins +- 类似于: 网格dp, unique path 里面的2种走法: 从上到下, 从左到右 +- 状态: dp[i]: sum of ways that coins can add up to i. +- Function: dp[j] += dp[j - coins[i]]; +- Init: dp[0] = 1 for ease of calculation; other dp[i] = 0 by default +- note: 避免重复count, 所以 j = coins[i] as start +- 注意 coins 需要放在for loop 外面, 主导换coin的流程, 每个coin可以用无数次, 所以在每一个sum value上都尝试用一次每个coin + +#### knapsack problem: backpack problem + + + +--- + +**3. [Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 背包有自己的大小M, 看最多能放多少重量的书? + +#### Backpack DP 1 +- 简单直白的思考 dp[i][m]: 前i本书, 背包大小为M的时候, 最多能装多种的书? +- **注意**: 背包问题, 重量weight一定要是一维. +- dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + A[i - 1]); +- 每一步都track 最大值 +- 最后return dp[n][m] +- 时间空间 O(mn) +- Rolling array, 空间O(m) + +#### Backpack DP 2 +- true/false求解, 稍微曲线救国: 重点是, 最后, 按照weight从大到小遍历, 第一个遇到true的, index就是最大值. +- 考虑: 用i个item (可跳过地取), 是否能装到weight w? +- 需要从'可能性'的角度考虑, 不要搞成单一的最大值问题. +- 1. 背包可装的物品大小和总承重有关. +- 2. 不要去找dp[i]前i个物品的最大总重, 找的不是这个. + dp[i]及时找到可放的最大sum, 但是i+1可能有更好的值, 把dp[i+1]变得更大更合适. + +##### 做法 +- boolean[][] dp[i][j]表示: 有前i个item, 用他们可否组成size为j的背包? true/false. +- (反过来考虑了,不是想是否超过size j, 而是考虑是否能拼出exact size == j) +- **注意**: 虽然dp里面一直存在i的位置, 实际上考虑的是在i位置的时候, 看前i-1个item. + +##### 多项式规律 +- 1. picked A[i-1]: 就是A[i-1]被用过, weight j 应该减去A[i-1]. 那么dp[i][j]就取决于dp[i-1][j-A[i-1]]的结果. +- 2. did not pick A[i-1]: 那就是说, 没用过A[i-1], 那么dp[i][j]就取决于上一行d[i-1][j] +- dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]] + +##### 结尾 +- 跑一遍dp 最下面一个row. 从末尾开始找, 最末尾的一个j (能让dp[i][j] == true)的, 就是最多能装的大小 :) +- 时间,空间都是:O(mn) + + + + +--- + +**4. [Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 每本书有value int[] V + +背包有自己的大小M, 看最多能放多少value的书? + +#### Backpack DP +- 做了Backpack I, 这个就如出一辙, 只不过: dp存的不是max weight, 而是 value的最大值. +- 想法还是,选了A[i - 1] 或者没选A[i - 1]时候不同的value值. +- 时间空间O(mn) +- Rolling Array, 空间O(m) + +#### Previous DP Solution +- 如果无法达到的w, 应该mark as impossible. 一种简单做法是mark as -1 in dp. +- 如果有负数value, 就不能这样, 而是要开一个can[i][w]数组, 也就是backpack I 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**5. [Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)** Level: Medium Tags: [Backpack DP, DP] + + +#### Backpack DP +- 与背包1不同: 这里不是check可能性(OR)或者最多能装的size是多少; 而是计算有多少种正好fill的可能性. +- dp[i][w]: 用前i本书, 正好fill到 w weight的可能性. +- 对于末尾, 还是两种情况: +- 1. i-1位置没有加bag +- 2. i-1位置加了bag +- 两种情况可以fill满w的情况加起来, 就是我们要的结果. +- 如常: dp[n + 1][w + 1] +- 重点: dp[0][0] 表示0本书装满weight=0的包, 这里我们必须 dp[0][0] = 1, 给后面的 dp function 做base +- Space, time: O(MN) +- Rolling array, 空间优化, 滚动数组. Space: O(M) + +#### 降维打击, 终极优化 +- 分析row(i-1)的规律, 发现所有row(i)的值, 都跟row(i-1)的左边element相关, 而右边element是没用的. +- 所以可以被override. +- Space: O(M), 真*一维啊! +- Time: O(MN) + + + +--- + +**6. [Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)** Level: Hard Tags: [Backpack DP, DP] + + +给n种不同的物品, int[] A weight, int[] V value, 每种物品可以用无限次 + +问最大多少value可以装进size是 m 的包? + +#### DP +- 可以无限使用物品, 就失去了last i, last unique item的意义: 因为可以重复使用. +- 所以可以转换一个角度: +- 1. 用i **种** 物品, 拼出w, 并且满足题目条件(max value). 这里因为item i可以无限次使用, 所以考虑使用了多少次K. +- 2. K虽然可以无限, 但是也被 k*A[i]所限制: 最大不能超过背包大小. +- dp[i][w]: 前i种物品, fill weight w 的背包, 最大价值是多少. +- dp[i][w] = max {dp[i - 1][w - k*A[i-1]] + kV[i-1]}, k >= 0 +- Time O(nmk) +- 如果k = 0 或者 1, 其实就是 Backpack II: 拿或者不拿 + +#### 优化 +- 优化时间复杂度, 画图发现: +- 所计算的 (dp[i - 1][j - k*A[i - 1]] + k * V[i - 1]) +- 其实跟同一行的 dp[i][j-A[i-1]] 那个格子, 就多出了 V[i-1] +- 所以没必要每次都 loop over k times +- 简化: dp[i][j] 其中一个可能就是: dp[i][j - A[i - 1]] + V[i - 1] +- Time O(mn) + +#### 空间优化到1维数组 +- 根据上一个优化的情况, 画出 2 rows 网格 +- 发现 dp[i][j] 取决于: 1. dp[i - 1][j], 2. dp[i][j - A[i - 1]] +- 其中: dp[i - 1][j] 是上一轮 (i-1) 的结算结果, 一定是已经算好, ready to be used 的 +- 然而, 当我们 i++,j++ 之后, 在之前 row = i - 1, col < j的格子, 全部不需要. +- 降维简化: 只需要留着 weigth 这个 dimension, 而i这个dimension 可以省略: +- (i - 1) row 不过是需要用到之前算出的旧value: 每一轮, j = [0 ~ m], 那么dp[j]本身就有记录旧值的功能. +- 变成1个一位数组 +- 降维优化的重点: 看双行的左右计算方向 +- Time(mn). Space(m) + + + +--- + +**7. [Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)** Level: Medium Tags: [Array, Backpack DP, DP] + + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + +#### Backpack DP +- 计数问题, 可以想到DP. 其实就是Backpack VI. +- 从x个数字里面找candidate(可以重复用同一个数字), 来sum up to target. 找: # of ways to form the sequence. +- Backpack VI: 给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法 +- dp[i]: # of ways to build up to target i +- consider last step: 如果上一步取的是 candidate A, 那么就该加到dp[i]: +- dp[i] += dp[i - A] +- 要找overall dp[i], 就做一个for loop: dp[i] = sum{dp[i - num]}, where for (num: nums) +- Time: O(mn). m = size of nums, n = target +- If we optimize dp for loop, 需要Sort nums. O(mlogm). will efficient 如果m是constant或者relatively small. Overall: O(n) + +#### DFS, backtracking +- 尽管思考方式是对的, 但是 times out +- 可以重复使用数字的时候, 比如用1 来拼出 999, 这里用1就可以走999 dfs level, 不efficient + + + +--- + + + + + + + +## MaxHeap (4) +**0. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**1. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**2. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**3. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + + + + + + + +## Bit Manipulation (17) +**0. [O(1) Check Power of 2.java](https://github.com/awangdev/LintCode/blob/master/Java/O(1)%20Check%20Power%20of%202.java)** Level: Easy Tags: [Bit Manipulation] + + + + +--- + +**1. [Single Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20II.java)** Level: Medium Tags: [Bit Manipulation] + + +一串数字里面, 所有数字都重复三次, 除了一个数字. 找到这个数字, linear time, without extrace space (constant space) + +TODO: bits + + + +--- + +**2. [Single Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20III.java)** Level: Medium Tags: [Bit Manipulation] + + +TODO: wut? + + +--- + +**3. [Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)** Level: Easy Tags: [Bit Manipulation, Math] + + +跟powerOfThree一样: 可以loop, check mod; 也可以用binary search找合适的数字. + + + +--- + +**4. [Sum of Two Integers.java](https://github.com/awangdev/LintCode/blob/master/Java/Sum%20of%20Two%20Integers.java)** Level: Easy Tags: [Bit Manipulation] + + +a^b 是: 不完全加法. +a&b 是: 所有可能的进位. a&b<<1是向左边进位的形态. + +Goal: 先a^b裸加, 算出进位; 再把结果和进位裸加, 再算出这一轮的进位; 再..裸价, 算进位....直到进位数==0. + +那么就,首先记录好进位的数字:carry. 然后 a^b 不完全加法一次。然后b用来放剩下的carry, 每次移动一位,继续加,知道b循环为0为止。 + +在第一回 a ^ b 之后, b 的本身意义就消失. 接下去应该给parameter重新命名. +sum = a ^ b; // sum without adding carries +nextCarry = (a & b) << 1; + +用其他variable name 取代 a, b 会更好理解一点. + +Bit Operation +Steps: + a & b: 每bit可能出现的进位数 + a ^ b: 每bit在此次操作可能留下的值,XOR 操作 + 每次左移余数1位,然后存到b, 再去跟a做第一步。loop until b == 0 + +(http://www.meetqun.com/thread-6580-1-1.html) + + + +--- + +**5. [Swap Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Bits.java)** Level: Easy Tags: [Bit Manipulation] + + +简单, 但是很多知识点: +1. Hex 0xaaaaaaaa 是1010101....1010; 0x55555555 是01010101....0101 +2. 可以用这两个hex取单数和负数. 如果需要取其他的pattern, 也可以做. +3. x很可能是negative number, 所以right-shift 要用logic shift, >>> 避免leading负数补位. + + + +--- + +**6. [Update Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Update%20Bits.java)** Level: Medium Tags: [Bit Manipulation] + + +熟悉bits的一些trick: +- ~0 = -1 = 111111...11111111 (32-bit) +- Create mask by shifting right >>>, and shifting left +- Reverse to get 0000...11110000 format mask +- & 0000 = clean up; | ABC = assign ABC + + + +--- + +**7. [Maximum XOR of Two Numbers in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20XOR%20of%20Two%20Numbers%20in%20an%20Array.java)** Level: Medium Tags: [Bit Manipulation, Trie] + + +比较难想到. 利用到XOR性质A^B=C, then A=B^C. +1. 枚举可能的A, 2. 然后一个个猜. + +1. 枚举A: 因为求MAX肯定是找leading-1最多的数字, 那么枚举A从(1000000...000)开始, +每次多一位取1或者0 +2. 因为枚举A的时候是按照每个bit来, 那么B和C也要以同样数位出现. +这里吧B和C变成了prefix的形式, 放在了set里面. +跟2sum用hashmap的思想类似, 每次用枚举的 A^B=C, 看看结果C是否已经在set里面. +如果在, 证明枚举的A可能被B^C得出, 那么就找到了一种情况. + +还用到一些技巧: +mask = (1 << i); // i位mask +mask = mask | (1 << i); // prefix mask + + + +--- + +**8. [Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer] + + +#### Vote 计数 +- vote++, vote--到最后剩下的就是winner. Time O(n), Space O(1) +- Majority Number是指超半数. 超半数的数字, 最后都会至少有vote>=1: match current majority number,vote++;if not, vote--. +- 注意:assume valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 + +#### HashMap count occurance +- Time, Space: O(n) + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**9. [Convert Integer A to Integer B.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Integer%20A%20to%20Integer%20B.java)** Level: Easy Tags: [Bit Manipulation] + + +把Integer A 转换成 Integer B 需要改变多少bits? + +#### Bit Manipulation +- a^b 显示出bit format里面有不同binary code的数位. +- 每次 (a^b)>>i 移动i位之后, 再 & 1时其实是指留下这一位的数字. +- count +- 其实用到了 ^ 找不同的bit, >> 移位, &1 mask + + + +--- + +**10. [Count 1 in Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%201%20in%20Binary.java)** Level: Easy Tags: [Bit Manipulation] + + +count 一个 32-bit number binary format 里面有多少1 + +#### Bit Manipulation +- shift >> i +- apply mask & 1 + +#### Convert to string O(n) space +可以把integer -> string -> char array. + + + +--- + +**11. [Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)** Level: Medium Tags: [Bit Manipulation, Bitwise DP, DP] + + +给一个数组, 算里面有多少bit 1. + +#### Bitwise DP +- 对于每一个数字, 其实很简单就能算出来: 每次 >>1, 然后 & 1 就可以count 1s. Time: 一个数字可以 >>1 O(logN) 次 +- 现在要对[0 ~ num] 都计算, 也就是N个数字, 时间复杂度: O(nLogN). +- 用DP来优化, 查找过的number的1s count, 存下来在 dp[number]里面. +- 计算你顺序从 0 -> num, count过的数字就可以重复利用. +- Bit题目 用num的数值本身表示DP的状态. +- 这里, dp[i] 并不是和 dp[i-1]有逻辑关系; 而是dp[i] 和dp[i>>1], 从binary representation看出有直接关系. + + + +--- + +**12. [Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)** Level: Easy Tags: [Array, Bit Manipulation, Math] + + +给一串unique数字, 数字取自 [0 ~ n], 无序, 找第一个skipped的数字. + +#### Swap +- 跟First Missing Positive 非常像, 只有一行代码的区别. +- swap 所有的数字, 到自己的correct position +- 最后一个for loop找到错位的index, 也就是缺的数字. + +#### Bit Manipulation +- XOR will only retain bits that are different 1 ^ 0 = 1, but 0^0, 1^1 == 0 +- Use that feature, 把所有value都和index XOR了 +- 剩下的多余的数字, 其实是那个index无法被XOR消掉, 也就是那个缺的number value. +- 注意: 题目告诉数字是 [0 ~ n], 然而缺一个数字, 那么在[0 ~ n - 1] 里面, 最大的数字(不管缺没缺), 一定是 n = nums.length. + +#### HastSet +- 全存, 找missing +- O(n) space, 不合题意 + +#### sorting +- sort, 找1st missing +- O(n log n) 太慢, 不合题意 + + + +--- + +**13. [Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)** Level: Hard Tags: [Bit Manipulation, String] + + +#### String +- 首先要分两半解决,断点是'.': str.split("\\."); +- Integer那一半好弄,whie loop里: num%2, num/2. 做一个 `parseInteger()` function +- Decimal那边复杂点. 做一个 `parseDecimal()` function: +- bit == 1的数学条件: 当下num * 2 >= 1。 更新: num = num * 2 - 1; +- bit == 0的数学条件: num * 2 < 1. 更新: num = num * 2 + +#### 注意 +- num是 double, 小数在 `num = num * 2 - 1` 的公式下可能无限循环 +- 因此check: num重复性,以及binary code < 32 bit. +- 所以题目也才有了32BIT的要求! + + + +--- + +**14. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**15. [Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)** Level: Medium Tags: [Bit Manipulation] + +time: O(n) +space: O(1), 32-bit array + +给出Hamming Distance定义(bit format时候有多少binary diff), 求一串数字的hamming distance总和. + +#### Bit Manipulation +- Bit题: 考验 bit >>, mask & 1, 还有对题目的理解能力 +- Put integers in binary, and compare each column: +- for each `1`, ask: how many are different from me? all the `0` +- `# of diffs at each bit-column = #ofZero * #ofOne ` +- 1. countZero[], countOne[]; 2. loop over nums and populate the two array + +##### 注意雷点 +- 问清楚: 10^9 < 2^31, we are okay with 32 bits +- `最终的hamming distance 要从 [1 ~ 32] 哪个bit开始算起`? 取决于 `最长`的那个binary format: 但不用先去找bit length +- 在做countZero, countOne时候, 都做32-bit; 最终做乘积的时候, 如果 `1` 或者 `0` 个数为零, 乘积自然为0. + + + + +--- + +**16. [Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)** Level: Easy Tags: [Bit Manipulation] + +time: O(n), n = # of bits +space: O(1) + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + + + +--- + + + + + + + +## Quick Select (1) +**0. [Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)** Level: Easy Tags: [Array, Quick Select, Quick Sort] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- quickSelect 可以找到 kth 最小的元素 +- 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- quick select 的template要熟悉一下, 一下子可能想得到, 但写不出来 +- 主要步骤: partition, dfs, only recur on one part of the array + + + + +--- + + + + + + + +## Divide and Conquer (35) +**0. [Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer] + + +#### Vote 计数 +- vote++, vote--到最后剩下的就是winner. Time O(n), Space O(1) +- Majority Number是指超半数. 超半数的数字, 最后都会至少有vote>=1: match current majority number,vote++;if not, vote--. +- 注意:assume valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 + +#### HashMap count occurance +- Time, Space: O(n) + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**1. [Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +如题, build balanced BST from sorted array + +#### DFS +- Binary Search Tree特点: 左边的node都比右边的node小. +- height balance, subtree height 相差<1, 必须左右sub tree均分. 做DFS(num, start, end) +- 在每一个level, 找到中间点, 然后分割2半, 继续dfs +- Divide and Conquer +- time/space: O(n), visit all nodes, no redundant visits. + + + +--- + +**2. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**3. [Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +给一个特殊的binary tree, treeNode里面有一个 next pointer. + +写一个function, 把所有node都更同level的node 连在一起. 最右边的node.next = NULL + +#### DFS + Divide and Conquer +- 题目要求DFS. 想清楚了如何在DFS level把几种情况都考虑了, 写起来很简单. NOT BFS, because requires O(1) space +- 对于一个root来说, 只有几个点可以顾忌到: root.left, root.right, root.next. +- 想办法把这三个方向的点, 能连起来的都连起来: +- 1. `node.left.next = node.right` +- 2. If `node.next != null`, link `node.right.next = node.next.left`; +- 然后在dfs(root.left), dfs(root.right) +- Time: visit && connect all nodes, O(n) + +#### BFS +- 不和题意,用了queue space,与Input成正比。太大。 +- BFS over Tree。 用Queue 和 queue.size(),老规矩。 +- process每层queue时, 注意把next pointer加上去就好. + + + +--- + +**4. [Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +如题, 验证是否是BST. + +#### DFS +- 查看每个parent-child关系: leftchild < root < rightChild; +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest +- imagine we know the two extreme border: Integer.MIN_VALUE, Integer.MAX_VALUE; pass node around and compare node vs. node.parent. +- 方法: 把root.val 传下来作为 max 或者 min, 然后检查children +- + +##### Note: +- min/max需要时long type. +- 如果题目真的给node.val = Integer.MAX_VALUE, 我们需要能够与之比较, long就可以. + + + +--- + +**5. [Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List] + + +如题, 把一个sorted singly linked list 转换成一个 height balanced BST + +#### DFS +- Divide and Conquer +- 找到mid node +- 然后分割两半, 分别dfs做各自两个subtree: node.left,node.right +- 用长度来定位mid, 每次找中间点做root, 然后前半段, 后半段分别dfs with length. +- 用快慢pointer 找到mid. Better: 不用traverse entire linked list + +#### Details +- slowPointer = node; +- fastPointer = node.next; +- 然后把root = mid.next +- 然后开始sortedListToBST(mid.next.next); //后半段 +- mid.next = null;//非常重要,要把后面拍过序的断掉 +- sortedListToBST(head); //从头开始的前半段 +- 最后root.left, root.right merge一下。 + + + +--- + +**6. [Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)** Level: Medium Tags: [DFS, Divide and Conquer, Stack] + + +给一个expression string. 里面包括数字, 字母, 括号. 其中数字代表括号里面的内容重复几次. + +括号里面可以是String, 也可能是expression. + +目的: 把expression展开成一个正常的String. + + +#### Stack, Iteratively +- Process inner item first: last come, first serve, use stack. +- Record number globally and only use it when '[' is met. +- Stack存 [ ] 里面的内容, detect 括号开头结尾: 结尾时process inner string +- 有很多需要注意的细节才能做对: +- Stack 也可以用, 每个地方要注意 cast. 存进去的需要是Object: String, Integer +- 几个 type check: instanceof String, Character.isDigit(x), Integer.valueOf(int num) +- 出结果时候: `sb.insert(0, stack.pop())` + + +#### DFS +- Bottom->up: find deepest inner string first and expand from inside of `[ ]` +- 与Stack时需要考虑的一些function类似. 特别之处: **检查`[ ]`的结尾** +- 因为DFS时候, 括号里的substring会被保留着进入下一个level, 所以我们在base level要keep track of substring. +- 用int paren 来track 括号的开合, 当paren再次==0的时候 找到closure ']' +- 其他时候, 都要继续 append to substring + + + + +--- + +**7. [Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)** Level: Hard Tags: [Binary Search, DFS, Divide and Conquer] + + +2Dmatrix, 里面的value有一些递增, 递减的特点(细节比较长, 看原题). 目标是找到peak element + +peak: 比周围4个方向的点value大 + +#### DFS + +##### 基本原理 +- 我们不可能一口气准确定位(x,y), 但是我们可以再一个row/col里面, 找到1D array的 peak. +- 根据这个点, 再往剩下两个方向移动 +- 1. 在中间的一行i=midX, 找到peak所在的y. +- 2. 在中间的一列j=midY, 找到peak所在的x. (有可能强势override之前找到的y, 也就是放弃那一行的peak, 在midY上找peak) +- 3. 根据 (x,y) 的4个neighbor check (x,y)是不是 peak, 如果不是, 像更高的位置移动一格 +- 4. 根据之前算的 midX, midY 把board分成4个象限, 在每一份里面再继续找 +- 这个题目LintCode不给做了, 所以思路对的, 但是解答还没有再次验证. + +##### 剪枝/切分象限 +- 每次只是找到一个row/col里面的peak而已! +- 找到这个点, 就等于把board切成了两半. +- 然后, 再跟剩下的相邻的两个位置比较, 就知道了哪里更大, 就去哪里找peak, 也就是又切了第二刀. +- 切第二刀的时候, 也要把(x, y) 移到需要取的象限. 进行DFS +- 根据mid row 切割: +- http://www.jiuzhang.com/solution/find-peak-element-ii/#tag-highlight-lang-java +- http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf + +##### 时间复杂度 +- 每一个level都减一半 +- T(n) = n + T(n/2) = n + n/2 + n/4 + ... + 1 = n(1 + 1/2 + .... + 1/n) = 2n = O(n) + +#### Binary Search +- TODO +- O(nLogN) + + + +--- + +**8. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**9. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**10. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**11. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**12. [Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +找到binary tree 里的最长 consecutive sequence. + +#### DFS +- Divide and Conquer. dfs +- 分开 看左边/右边 +- 如果左边满足连续递增的规则, dfs (depth + 1), 如果不满足规则, dfs(depth = 1) +- 右边也是一样 +- 对结果跟max作比较, return + + + +--- + +**13. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**14. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**15. [Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**16. [Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 + + + +--- + +**17. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**18. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**19. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**20. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**21. [Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)** Level: Medium Tags: [DFS, Divide and Conquer] + + +如题: Calculate the a^n % b where a, b and n are all 32bit integers. + +#### Divide and Conquer +- a^n可以被拆解成(a*a*a*a....*a), 是乘机形式,而%是可以把每一项都mod一下的。所以就拆开来take mod. +- 这里用个二分的方法,recursively二分下去,直到n/2为0或者1,然后分别对待. +- 注意1: 二分后要conquer,乘积可能大于Integer.MAX_VALUE, 所以用个long. +- 注意2: 要处理n%2==1的情况,二分时候自动省掉了一份 a,要乘一下。 + + + + +--- + +**22. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**23. [Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)** Level: Medium Tags: [Binary Search, Divide and Conquer] + + +给matrix, 每一行sorted, 每一列从上往下sorted, 找target是否存在 + +#### Binary Search +- 根据给定的性质, 其实点选的极端一点: x = 最下面的row, y = 当下一行里面最小的left position. +- (x,y)在左下角 +- 在此情况下, 只能往一个方向运行: 如果小于target, y++; 如果大于target, 那么只能x-- +- 每次操作, 都是删掉一行, 或者一列, 再也不需要回头看 +- `while (x >= 0 && y < col) {}` 确保不会跑脱 +- 同样的方式: 可以从右上角(0, col - 1) 开始, 代码稍微改一改 + +#### Divide and Conquer? +- TODO + + + +--- + +**24. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**25. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**26. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**27. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**28. [Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + +time: O(n) +space: O(n) + +给一个tree, 按照题意找最一个node满足: +1. 这个node的subtree涵盖最深level的所有leaves. +2. 这个node必须是能找到的最deep那个 + +条件2的需求是因为: root本身就是满足条件1的node, 还有很多Higher-level node也是如此, 所以要找那个deepest. + + +#### DFS on tree +- 分析题目, 思想是: 看到tree里面所有的leaves, 找到他们最deep的 common ancestor +- Maintain a map +- Recursively dfs: return deepest node that has all leaves by these comparisons: +- 1. If left,right child same depth, return root: they need common ancestor +- 2. If not same depth, return the one with larger depth +- 被传送去上一个level的, 永远都是subtree里面符合题意的: the node containing all leaf nodes +- Visit all nodes once O(n), space O(n) + +#### BFS +- Find all leaves at deepest level +- Use map to track each node-parent +- Backtrack all nodes to find common ancestor + + + +--- + +**29. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + +**30. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**32. [Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +#### Tree +- Traverse tree: left, right +- Concept of partial compare vs. whole compare + + + +--- + +**33. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + +**34. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + + + + + + + +## Status DP (7) +**0. [Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + +time: O(nm), m = # of colors +space: O(nm) + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, 但是不知道最后一个房子选什么颜色, 那么就遍历最后一个房子(i - 1)的颜色 +- 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- 考虑DP最后一个位置的情况(颜色选择):需要附带颜色status在DP[i]上: 定义二维数组, 其中一位是status +- dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. +- dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- 然后选ith 房子的 color, 再选(i-1)th 房子的color. Double for loop, skip same color + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 + + + +--- + +**1. [House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)** Level: Medium Tags: [DP, Sequence DP, Status DP] + + +和House Robber I 类似, 搜刮房子, 相邻不能动. 特点是: 现在nums排成了圈, 首尾相连. + +#### Sequence DP +- dp[i][status]: 在 status=[0,1] 情况下, 前i个 房子拿到的 max rob gain. status=0, 1st house robbed; status=1, 1st house skipped +- 根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]); +- 特别的是,末尾的last house 和 first house相连. 这里就需要分别讨论两种情况: 第一个房子被搜刮, 或者第一个房子没被搜刮 +- be careful with edge case nums = [0], only with 1 element. +- Time,space: O(n) + +#### 两个状态 +- 是否搜刮了第一个房子, 分出两个branch, 可以看做两种状态. +- 可以考虑用两个DP array; 也可以加一dp维度, 补充这个状态. +- 连个维度表示的是2种状态(1st house being robbed or not); 这两种状态是平行世界的两种状态, 互不相关. + +#### Rolling array +- 与House Robber I一样, 可以用%2 来操作rolling array, space reduced to O(1) + + + +--- + +**2. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**3. [Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + +time: O(NK^2): +space: (NK) + +一排n个房子, 每个房子可涂成k种颜色, 涂每个房子的价钱不一样, 用costs[][]表示. + +costs[0][1]表示涂了index是0的房子, 用了color 1. + +规则: 相邻的两个房子不能使同一种颜色 + +求: 最少的cost + +#### DP +- 跟Paint House I 几乎一模一样, 只不过paint color更多了: k colors. +- 先考虑单纯地用dp[i]表示涂前 i 个房子的最小cost +- 但是 dp[i] 和 dp[i-1] 两个index选什么颜色会互相影响, 难讨论, 于是加状态: 序列DP被加了状态变成2D. +- 考虑最后位, 而前一位i-1又被i位的颜色限制, 于是在考虑 min dp[i] 时候, 又多了一层iteration. +- 做dp[i][j]: # cost for 前 i 个房子, 所以要先pick (i-1) 房子的cost, 然后在找出 (i-2)房子的cost +- K种颜色 => O(NK^2) +- 如果不优化, 跟Paint House I 几乎是一模一样的代码 +- Time O(NK^2), space(NK) +- Rolling array: reduce space to O(K) + +#### 注意 +- 序列型dp[i]表示'前i-1个'的结果. 所以dp最好设定为 int[n + 1] size. +- 然而, 颜色在这里是状态, 所以保留在 j: [ 0~k) +- [[8]] 这样的edge case. 跑不进for loop, 所以特殊handle. + +#### Optimization Solution +- Time: O(NK) +- 如果已知每次都要从cost里面选两个不同的最小cost,那么先把最小两个挑出来, 就不必有第三个for loop 找 min +- 每次在数列里面找: 除去自己之外的最小值, 利用最小值/次小值的思想 +- 维持2个最值: 最小值/次小值. +- 计算的时候, 如果除掉的不是最小值的index, 就给出最小值; 如果除掉的是最小值的index, 就给出次小值. +- Every loop: 1. calculate the two min vlaues for each i; 2. calcualte dp[i][j] +- 如何想到优化: 把表达式写出来, 然后看哪里可以优化 +- 另外, 还是可以rolling array, reduce space complexity to O(K) + + + +--- + +**4. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**5. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**6. [Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)** Level: Medium Tags: [Coordinate DP, DP, Status DP] + + + +#### DP +- 特点: 上一步可能是swaped也可能是fixed +- 考虑A,B之间的现状: `A[i] > A[i - 1] && B[i] > B[i - 1]` 或者 `A[i] > B[i - 1] && B[i] > A[i - 1]` +- 问题: 如何把这个状态变成合理的strick-increasing状态? +- `A[i] > A[i - 1] && B[i] > B[i - 1]`: 1. 已经合理, 也不动. 2. [i], [i-1] 全部都swap +- `A[i] > B[i - 1] && B[i] > A[i - 1]`, 交错开来, 所以调换[i], 或者[i-1]: 1. 换[i-1]. 2. 换[i] +- 注意因为求min, 所以init value应该是 Integer.MAX_VALUE; + + + +--- + + + + + + + +## Sliding Window (6) +**0. [Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String] + + +大清洗 O(nk) +map.size一旦>k,要把longest string最开头(marked by pointer:start)的那个char抹掉 +一旦某一个char要被清除,所以在这个char 的1st and last appearance之间的char都要被清洗from map + + + + +--- + +**1. [Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Anagrams%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, Sliding Window] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### HashTable +- count character apperance 就想到hashtable +- 注意countS, countP 的技巧: 作比较只需要O(26) +- Overall timeO(n) +- 小心不要用一个int[] count 来技术 查0, 复杂度是O(n) + + + +--- + +**2. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**3. [Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison + + + +--- + +**4. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**5. [Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Deque, Monotonous queue +- 维持monotonuous queue: one end is always at max and the other end is min. Always need to return the max end of queue. +- when adding new elements x: start from small-end of the queue, drop all smaller elements and append to first element larger than x. +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`. +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! + + + +--- + + + + + + + +## Topological Sort (5) +**0. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**1. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**2. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**3. [Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Topological Sort] + + +#### Topological Sort BFS +- indegree tracking: Track all neighbors/childrens. 把所有的children都存在 inDegree里面 +- Process with a queue: 先把所有的root加一遍(indegree == 0),可能多个root。并且全部加到queue里面。 +- BFS with Queue: +- Only when map.get(label) == 0, add into queue && rst. (indegree剪完了, 就是root啦) +- inDegree在这里就 count down indegree, 确保在后面出现的node, 一定最后process. + + +#### Basics about graph +- 几个graph的condition: +- 1. 可能有多个root +- 2. directed node, 可以direct backwards. + +TODO: +- build`Map inDegree = new HashMap<>();` and include the root itself +- that is more traditional indegree building + + + +--- + +**4. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + + + + + + + +## Quick Sort (6) +**0. [Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)** Level: Easy Tags: [Array, Quick Select, Quick Sort] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- quickSelect 可以找到 kth 最小的元素 +- 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- quick select 的template要熟悉一下, 一下子可能想得到, 但写不出来 +- 主要步骤: partition, dfs, only recur on one part of the array + + + + +--- + +**1. [QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)** Level: Medium Tags: [Quick Sort, Sort] + + +implement quick sort. + +#### Quick Sort +- 首先partition. 返还一个partition的那个中间点的位置: 这个时候, 所有小于nums[partitionIndex] 都应该在 partitionIndex左边 +- 然后劈开两半 +- 前后各自 quick sort, recursively +- 注意:在partition里面, 比较的时候nums[start] < pivot, nums[end]>pivot, 如果写成了 <= 会 stack overflow. +- Time O(nlogn), Space: O(1) + + + +--- + +**2. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**3. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**4. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**5. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high= h`, 就是第一个符合 h-index 规则的paper, return h + +##### 反向思考 +- 如果从 h = n, 每次h--; 那么 `x = n - h` 就是从 `[0 ~ n)` 开始找第一个 `dictations[x] >= h`, 就是结果 +- 同时, `dictations[x-1]` 就是最后一个(dictation最多的)其余的paper. + +#### Bucket count / Bucket Sort +- O(n) +- Bucket sort的思想(更像是counting sort?): 过一遍 input, 把dictation value 作为 index, 分布在bucket[index]上++ +- bucket[x] 是 count when # of citation == x. +- 如果 x 大于 n的时候, 就超出了index范围, 但是刚好这个问题可以包容, 把这样的情况记位在bucket[n]就可以 +- 巧妙: `sum += bucket[h]` where `h = [n ~ 0]` 利用了h-index的definition: +- #of papers (sum of bucket[n]...bucket[0]) has more than h cidations +- 这里运用到了bucket sort的思想, 但是并不是sorting, 而h-index的定义运用的很巧妙. +- Read more about actual bucket sort: https://en.wikipedia.org/wiki/Bucket_sort + + + +--- + + + + + + + +## Greedy (18) +**0. [Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)** Level: Medium Tags: [Enumeration, Greedy] + + +#### Array +- 分三份:a b c考虑 +- 若a: countA++; 或b: countB++ +- 或c:countA--, countB-- +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 +- 最后出现的两个count>0的a和b,自然是potentially大于1/3的。其中有一个大于1/3. +- 比较countA和countB哪个大,就return哪一个。 + + + +--- + +**1. [Queue Reconstruction by Height.java](https://github.com/awangdev/LintCode/blob/master/Java/Queue%20Reconstruction%20by%20Height.java)** Level: Medium Tags: [Greedy] + + +别无他法, 只能写一遍例子, 找规律,然后greedy.  +需要写一遍发现的规律比如: 从h大的开始排列, 先放入k小的. 写comparator的时候要注意正确性. +如果要sort, 并且灵活insert:用arrayList. 自己做一个object. +最后做那个'matchCount'的地方要思路清晰, 找到最正确的spot, 然后greedy insert. + +O(n) space, O(nLog(n)) time, because of sorting. + +可能有简化的余地, 代码有点太长. +比如试一试不用额外空间? + + + +--- + +**2. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**3. [Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)** Level: Medium Tags: [DP, Game Theory, Greedy] + + +拿棋子游戏, 每个人可以拿1个或者2个, 拿走最后一个子儿的输. 问: 根据给的棋子输, 是否能确定先手的输赢? + +Game Theory: 如果我要赢, 后手得到的局面一定要'有输的可能'. + +#### DP, Game Theory +- 要赢, 必须保证对手拿到棋盘时, 在所有他可走的情况中, '有可能败', 那就足够. +- 设计dp[i]:表示我面对i个coins的局面时是否能赢, 取决于我拿掉1个,或者2个时, 对手是不是会可能输? +- dp[i] = !dp[i - 1] || !dp[i-2] +- 时间: O(n), 空间O(n) +- 博弈问题, 常从'我的第一步'角度分析, 因为此时局面最简单. + +#### Rolling Array +空间优化O(1). Rolling array, %2 + + + +--- + +**4. [Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)** Level: Medium Tags: [Array, DP, Greedy] + + +给出步数,看能不能jump to end. + +#### Greedy - start from index = 0 +- Keep track of farest can go +- 一旦 farest >= nums.length - 1, 也就是到了头, 就可以停止, return true. +- 一旦 farest <= i, 也就是说, 在i点上, 已经走过了步数, 不能再往前跳, 于是 return false +- This can be done using DP. However, greedy algorithm is fast in this particular problem. + +#### Greedy - start from index = n - 1 +- greedy: start from end, and mark last index +- loop from i = [n - 2 -> 0], where i + nums[i] should always >= last index +- check if last == 0 when returning. It means: can we jump from index=0 to the end? +- Time: O(n), beat 100% + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (A[j] >= i - j), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- Time: O(n^2) + + + + +--- + +**5. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**6. [Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)** Level: Hard Tags: [Greedy, Hash Table, Stack] + + +#### Hash Table, Greedy +- count[] = int[256], 不需要 `c-'a'` +- boolean visited[]: 一旦一个字母固定了位置后, 再次遇到时候, 直接跳过用过的character +- 如果tail字母可以变小, 那就delete掉tail, 重新接上新字母 (前提条件: 去掉的字母后面还会再出现, set visited[tail] = false) +- Space: O(1) count[], visited[]. +- Time: Go through all letters O(n) + +#### Stack +- Use stack instead of stringBuffer: keep append/remove last added item +- However, stringBuffer appears to be faster than stack. + + + +--- + +**7. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**8. [Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/Gas%20Station.java)** Level: Medium Tags: [Greedy] + + +给一串gas station array, 每个index里面有一定数量gas. + +给一串cost array, 每个index有一个值, 是reach下一个gas station的油耗. + +array的结尾地方, 再下一个点是开头, 形成一个circle route. + +找一个index, 作为starting point: 让车子从这个点, 拿上油, 开出去, 还能开回到这个starting point + +#### Greedy +- 不论从哪一个点开始, 都可以记录总油耗, `total = {gas[i] - cost[i]}`. 最后如果total < 0, 无论从哪开始, 必然都不能走回来 +- 可以记录每一步的油耗积累, `remain += gas[i] - cost[i]` +- 一旦 remain < 0, 说明之前的starting point 不合适, 也就是说, 初始点肯定在后面的index. 重设: start = i + 1 +- single for loop. Time: O(n) + +#### NOT DP +- 看似有点像 House Robber II, 但是问题要求的是: 一个起始点的index +- 而不是求: 最后点可否走完/最值/计数 + + + +--- + +**9. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**10. [Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)** Level: Hard Tags: [Greedy, Hash Table, Heap] + + +给一个string, 全是lowercase letter, 要求重新排列: 然后每个unique的character要有k distance apart. + +跟Task Scheduler有点像, 只不过Task那道题里面还可以用其他方法求count, 这道题要求出排列结果 + +#### PriorityQueue + HashTable +- PriorityQueue排序+分布排列的一个经典用法. +- Count frequency and store in pq. +- Consume element of pq for k rounds, each time pick one element from queue. +- Exception: if k still has content but queue is consumed: cannot complete valid string, return ""; +- space, O(n) extra space in sb, O(26) constant space with pq. +- time: O(n) to add all items + + + +--- + +**11. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**12. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**13. [Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Celebrity.java)** Level: Medium Tags: [Array, Greedy] + +time: O(n) +space: O(1) + +有n个人, 其中有个人是celebrity, 满足条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +#### Understand the property +- If brutly find celeb by comparing all possible pair: take complete O(n^2) handshakes. +- Instead, we can perform pruning, or like survival mode: +- 1. Assume a celeb = 0, and compare with all i = [1~ n-1] +- 2. If `celeb candidate know i, set celeb = i` as the next candidate (ex: prev canddiate invalid when he knows i) +- 3. For last standing celeb candidate: compare with all for validation +- Why performing the last run of validation? There could be someone dropped out before we execute `know(celeb, i)`. + +##### 思考逻辑 +- 先写出来[0 ~ n - 1], 最简单的方式 O(n^2) 检查, 记录每个人的状态. +- 逐渐发现, 因为 celeb 谁都不会认识, 那么当任何candidate knows anyone, 他自身就不是celeb. +- 我们可以greedy地, 一旦fail一个, 就立刻假设下一个是celeb candidate +- 最终还是要检查一遍, 避免错漏. +- 想一下happy case: 如果 celeb=0, 那么 know(celeb, i) 永远都是false, 然后 celeb一直保持0, 坚持到verify所有人. + + + +--- + +**14. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**15. [Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)** Level: Hard Tags: [DFS, Greedy, Math] + + +#### Greedy, Iterative +- For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +- We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +- Track the newly formed string; if new, add the new char to overall result +- Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] +- Same concept as dfs + +#### DFS +- Same concept: use window to cut out tail, and append with new candidate +- do this for k^n = Math.pow(k, n) times + + + +--- + +**16. [Delete Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Digits.java)** Level: Medium Tags: [Greedy, Priority Queue] + + +#### Priority Queue +- TODO: parse into node(index, digitValue) +- find the top k, and remove from char array +- O(nlogn) time + +#### Greedy +- 数位靠前的,权值更大. 所以硬来把靠前的相对更大的(跟following digit相比)去掉。 + + + +--- + +**17. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + + + + + + + +## Queue (3) +**0. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**1. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**2. [Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison + + + +--- + + + + + + + +## Coordinate DP (17) +**0. [Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +跟unique path的grid一样, 目标走到右下角, 但是grid里面可能有obstacle, 不能跨越. 求unique path 的count. + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- 考虑最终结尾需要的状态:如何组成,写出公式. +- 公式中注意处理能跳掉的block, marked as 1. '到不了', 即为 0 path in dp[i][j]. + + + +--- + +**1. [Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Corrdinate DP +- Space, Time: O(MN) +- dp[i][j] 就是(i, j)上最多能炸掉的enemy数量 +- dp[i][j] 需要从4个方向加起来, 也就是4个方向都要走一遍, 所以分割成 UP/Down/Left/Right 4个 int[][] +- 最后一步的时候求max +- 分方向考虑的方法很容易想到,但是四个方向移动的代码比较繁琐. +- 往上炸: 要从顶向下考虑 +- 往下炸: 要从下向上考虑 +- 熟练写2D array index 的变换. + +似乎还有一个更简洁的方法, 用col count array: http://www.cnblogs.com/grandyang/p/5599289.html + + + +--- + +**2. [Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +##### DP +- 计数DP.注意方程式前两位置加在一起: 前两种情况没有overlap, 也不会缺情况. +- 注意initialization, 归1. +- 需要initialize的原因是,也是一个reminder: 在方程中会出现-1index +- Of course, row i = 0, or col j = 0, there is only 1 way to access +- time O(mn), space O(mn) + +##### 滚动数组 +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + + + + +--- + +**3. [Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Square.java)** Level: Medium Tags: [Coordinate DP, DP] + + +只能往右边,下面走, 找面积最大的 square. 也就是找到变最长的 square. + +#### DP +- 正方形, 需要每条边都是一样长度. +- 以右下角为考虑点, 必须满足条件: left/up/diagonal的点都是1 +- 并且, 如果三个点分别都衍生向三个方向, 那么最长的 square 边就是他们之中的最短边 (受最短边限制) +- dp[i][j]: max square length when reached at (i, j), from the 3 possible directions +- dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; +- Space, time O(mn) + +##### init +每个点都可能是边长1, 如果 matrix[i][j] == '1' + +##### 滚动数组 +[i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + +--- + +**4. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**5. [Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +找连续的持续上升子序列的长度. + +#### Coordinate DP +- 1D coordinate, dp 的角标, 就是代表 index i 的状态 +- 求最值, dp[i] = 在index i位置的最长子序列 +- 如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1 +- 如果没有持续上升, 那么dp[i] = 1, 重头来过 +- maintain max + +#### Basic +- 用一个数存current count, maintain max + + + +--- + +**6. [Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +#### DP +- Time, Space O(MN) +- 往右下角走, 计算最短的 path sum. 典型的坐标型. +- 注意: init 第一行的时候, 要accumulate dp[0][j - 1] + grid[i][j], 而不是单纯assign grid[i][j] + +#### Rolling Array +- Time O(MN), Space O(1) +- 需要在同一个for loop里面完成initialization, 和使用dp[i][j] +- 原因: dp[i % 2][j] 在被计算出来的时候, 是几乎马上在下一轮是要被用的; 被覆盖前不备用,就白算 +- 如果按照第一种方法, 在开始initialize dp, 看起来固然简单, 但是不方便空间优化 + + + +--- + +**7. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**8. [Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)** Level: Hard Tags: [Binary Search, Coordinate DP, DP] + + +俄罗斯套娃, 这里用envelope来表现. 给一串array, 每一个[x, y] 是envelope 长宽. [[5,4],[6,4],[6,7],[2,3]]. + +看用这些套娃, 可以最多套几个. + +#### DP: 1D Coordinate +- envelopes没有顺序, 先排序 (主要根据第一个index排序) +- 然后观察: 排序过后, 就变成了1D的坐标动态规划. +- max number 取决于上一个成功Russian doll的 max value + 1 +- 上一个index不知道, 所以遍历找上一个index. +- 当下index i 的状态, 取决于前面index j 的状态, 所以遍历两个index. +- O(n^2)的DP, n = envelopes.length; + +#### DP: 2D Coordinate +- 这个方法是自己想出来的, 但是时间复杂度太大, timeout +- 把envelop标记在2D grid上面, 然后像走机器人一样, 求到最右下角的最大 count max. +- count 当下能存在多少Russian doll +- 两种情况: 当下coordinate 没有target, 当下coordinate有target +- 当下coordinate 没有target: 如同机器人走法, Math.max(dp[i - 1][j], dp[i][j - 1]) +- 当下coordinate 有target: dp[i - 1][j - 1] + dp[i][j] +- timeout: O(n^2), n = largest coordinate. + + + + +--- + +**9. [Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + +O(n)跑2遍for. +O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. +特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 + + + +--- + +**10. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**11. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**12. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**13. [Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)** Level: Hard Tags: [Coordinate DP, Stack, String] + + +给一串string, 里面只有`(`, `)`. 找最长valid parentheses 的长度. + +#### 1D Coordinate DP +- use dp[i] track local max, maintain global max +- int[] dp. dp[i]: longest valid string that ends on i. +- 结尾是 ')', 2种情况: 1. 刚好s[i-1]是'('; 2. s[i]的')'更前面的一个起始'(' 对应 +- 注意, 结尾如果是'('属于不合理情况, 忽略. +- init: dp[0] = 0, 单个char不可能成型. +- 计算顺序: 从左到右, 找local max, maintain global max +- O(n) space, O(n) runtime + +#### Stack +- Stack 里面存所有的open/close parentheses. +- 如果遇到stack.top()刚好开合结掉, 就stack.pop(). +- 剩下的都是不合理的elements. +- 有点像negatively找 solution: `endIndex - 最后一个failedIndex(stack.pop()) - 1`, 应该就是最后一个succeeded string的长度 +- 每次更新 endIndex 为stack.top(), 然后从stack继续找下一个failedIndex +- 所有的length作比较, 就可以找出最长length +- O(n) stack space, O(n) runtime. 应该比dp慢一点, 因为做了2遍O(n) + + + + +--- + +**14. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**15. [Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Coordinate DP, DP] + +time: O(n^2) +time: O(n) + +给一串 unsorted sequence, 找到长 increasing subsequence 的个数! + +#### Coordinate DP +- 需要能够判断综合题, 分清楚情况和套路: combination of `longest subsequence` and `ways to do`, as well as global variable. +- len[i] (我们平时的dp[i]): 在前i个元素中, 最长的 increasing subsequence length; +- count[i]: 在前i个元素中, 并且以 len[i]这个长度为准的 subsequence的 count. 或者: 在前i个元素中, ways to reach longest increasing subsequence. +- `len[i] == len[j] + 1`: same length, but different sequence, so add all `count[i] += count[j]` +- `len[i] < len[j] + 1`: 这就是更长的情况找到了, 那么有多少次 count[j] 有多少, count[i] 就有多少. 仔细想sequence: 长度增长了, 但是ways to reach i 没有增长. +- 同样的判断需要用在 maxLen 和 maxFreq上: +- 如果没有增长 maxLen 不变, maxFreq上面需要 +=count[i] (同一种长度, 多了更多的做法) +- 如果maxLen 变长, maxFreq 也就是采用了 count[i] = count[j] +- TODO: Is rolling array possible? + +#### 相关 +- 都是 Coordiate DP, DP的鼻祖家族: +- Longest Increasing Subsequence (跟这道题的一部分一模一样) +- Longest Continuous Increasing Subsequence (连续, 只check dp[i - 1]) +- Longest Increasing Continuous Subsequence I, II (Lintcode, II 是matrix) + + + +--- + +**16. [Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)** Level: Medium Tags: [Coordinate DP, DP, Status DP] + + + +#### DP +- 特点: 上一步可能是swaped也可能是fixed +- 考虑A,B之间的现状: `A[i] > A[i - 1] && B[i] > B[i - 1]` 或者 `A[i] > B[i - 1] && B[i] > A[i - 1]` +- 问题: 如何把这个状态变成合理的strick-increasing状态? +- `A[i] > A[i - 1] && B[i] > B[i - 1]`: 1. 已经合理, 也不动. 2. [i], [i-1] 全部都swap +- `A[i] > B[i - 1] && B[i] > A[i - 1]`, 交错开来, 所以调换[i], 或者[i-1]: 1. 换[i-1]. 2. 换[i] +- 注意因为求min, 所以init value应该是 Integer.MAX_VALUE; + + + +--- + + + + + + + +## Monotonous Stack (1) +**0. [Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)** Level: Hard Tags: [Array, Monotonous Stack, Stack] + + +给n个bar,组成柱状图histogram. 求在这一排柱状图里面可以找到的面积最大的长方形. + +思考: 找长方形面积, 无非是找两个index, 然后底边长度 * height. + +#### Monotonous Stack +- 重点是根据找Histogram里面rectangle的性质, 维持一个单调递增的Stack +- 在loop over indexes的时候: +- 如果高度>= previous peek(), 那么对于那个peek, 就意味着, 往下走, 一直走高嘛, 之前的peek总可以继续抄底 +- 什么时候不能抄底了呢? 就是有一个下降趋势的时候 +- 这时候并不是calculate所有前面的peek, 而是考虑 大于 current height的之前所有的peek. +- 把这些peek到 current height 前一格的rectangle全部找出来: stack.pop() +- 这个stack.pop()的过程里面, 其实没有算上 current height, 因为需要留到下一轮, 把current index加进stack 再说 +- 为什么用stack? 因为需要知道连续递增的peek, stack.peek() O(1), 好用 + 而其实不用stack, 也可以用其他方式记录所有height, 只不过要 O(n)去找peek不方便 + +#### 知识点 +- 理解monotonous stack 是如何被维护的 +- 维护monotonous stack 是题目需要, 而不是stack本身性质, 是一种借助 stack.peek() O(1)的巧妙用法. + + + + +--- + + + + + + + +## Partition (3) +**0. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**1. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**2. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + + + + + + + +## Enumeration (15) +**0. [Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)** Level: Medium Tags: [Enumeration, Greedy] + + +#### Array +- 分三份:a b c考虑 +- 若a: countA++; 或b: countB++ +- 或c:countA--, countB-- +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 +- 最后出现的两个count>0的a和b,自然是potentially大于1/3的。其中有一个大于1/3. +- 比较countA和countB哪个大,就return哪一个。 + + + +--- + +**1. [Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)** Level: Medium Tags: [Array, Enumeration] + + +#### 找公式规律 +- 找到个转角度的规律公式: r = c; c = (w - r) +- 用temp variable, swap in place. + + + +--- + +**2. [Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited value. +- 在nest for loop里面validate row,col,and block. +- validate block要利用i 和 j 增长的规律。 +- 说白了,i && j是按照0~n增长的index, 具体怎么用是可以flexible的。这个方法在同一个nest for loop解决所有运算。 +- `int c = 3 * (i % 3) + j % 3;` //make use of how i and j increases +- `int r = 3 * (i / 3) + j / 3;` + +#### A bit Slower approach +- 单独做block validation: validate block的时候虽然看到了4层for. 其实也就是n^2 +- 可能代码稍微复杂一点 + + + +--- + +**3. [Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)** Level: Hard Tags: [DP, Enumeration, Partition DP] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +其中字符可能是 "*", 可以代表 [1 - 9] + +#### DP +- 乘法原理 +- 跟decode way I 一样, 加法原理, 切割点时: 当下是取了 1 digit 还是 2 digits 来decode +- 定义dp[i] = 前i个digits最多有多少种decode的方法. new dp[n + 1]. +- 不同的情况是: 每一个partition里面, 如果有"*", 就会在自身延伸出很多不同的可能 +- 那么: dp[i] = dp[i - 1] * (#variations of ss[i]) + dp[i - 2] * (#variations of ss[i,i+1]) + +##### 特点 +- 枚举的能力: 具体分析 '*' 出现的位置, 枚举出数字, 基本功. +- 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) +- 理解取MOD的原因: 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. +- 枚举好以后, 其实这个题目的写法和思考过程都不难 + + + + +--- + +**4. [Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)** Level: Medium Tags: [Basic Implementation, Enumeration, String] + + +给一个时间string"12:09", 用里面的4个integer组合成其他时间string, 目标找最小的next time. + +如果组合出的time string 在input time之前, 默认 + 24 hours. + +#### String +- enumerate all candidates and filter to keep the correct ones +- String.compareTo(string) -> gives lexicographical comparision + + + +--- + +**5. [Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)** Level: Medium Tags: [Array, Enumeration] + + +从(0,0)坐标, 走完spiral matrix, 把结果存在list里. + +#### DX, DY +- Basic implementation, array, enumeration +- 写一下position前进的方向: RIGHT->DOWN->LEFT->UP +- 用一个direction status 确定方向 +- 写一个compute direction function 改变方向 `(direction + 1) % 4` +- `boolean[][] visited` 来track走过的地方 + + + +--- + +**6. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + +**7. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**8. [Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + +time: O(n) + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**9. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**10. [Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4.java)** Level: Easy Tags: [Enumeration, String] + + +Read4 题目. 理解题目: 是有个input object buff, 会被populated with data. + +#### String in char[] format +- 理解题目: 其实就是track `可以read多少bytes by read4() response` +- 另外一个有用的function `System.arraycopy(src, srcIndex, dest, destIndex, length)` + + + +--- + +**11. [Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)** Level: Hard Tags: [Enumeration, Math, String] + + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 + +#### 注意 +- StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 +- 注意加 " " 的时候, 如果多余, 要`trim()` +- 注意, 小于20的数字, 有自己的特殊写法, 需要额外handle +- 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 + + + + +--- + +**12. [Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- Summing space = `width + (size-1)`. maintain: 1. list of candidates, 2. width of actual words +- calculate space in between: `remain/(size - 1)` +- overall for loop; clean up list: 1. over size; 2. last item +- 一点也不难, 但是要小心: deal with list of string的时候, 注意处理干净sum size of list, 就行了. +- `干净处理space`: 只处理 (n-1) items, 然后最后一个拿到for loop 外面, 特殊处理. + +#### Notes +- Clarification, observation: +- can start with greedy approach to stack as many words as possible +- once exceed the length, pop the top, and justify the added words (untouched words tracked by index) +- left justify: given list/stack of words with size t, overall remaining space length m, +- deal with last line with special care: just fill one space, and fill the rest of the row with space +- Does not seem very complicated, but need additional care of calculating the amount of space needed. +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**13. [Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)** Level: Hard Tags: [Enumeration, String] + + +Read N Character using `Read4(char[] buf)` 的加强版: 可以不断读 read(buf, n) + +#### String +- 注意String的index handle, 慢慢写edge case +- 理解题目意思: `read4(char[] buf)` 这样的 `populate input object` 的function稍微少一点. +- 遇到时候, 仔细理解function用法, 不要慌乱. 其实思考方式很简单, 仔细handle string 还有 edge case就好了. + + + +--- + +**14. [Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math] + + +根据题意枚举, 再根据规则basic implementation + +#### Alter input + +#### HashTable + Two Pointer + + + +--- + + + + + + + +## PreProduct (1) +**0. [Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + +time: O(n) +space: O(1) + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 看第二个解答, 进一步简化了代码 +- PreProduct, 跟preSum的感觉有点像, 就是差一位. + + + +--- + + + + + + + +## Binary Tree (13) +**0. [Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### DFS +- 分析题意后, 按照题意: Flatten the tree, no extra space. +- 1. reserve right child: `reservedRightNode` +- 2. Connect `root.right = root.left`, DFS flatten(root.right) +- 3. 移花接木, coneect end of list -> reservedRightNode +- 4. flatten 剩下的. root.right... + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**1. [Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)** Level: Easy Tags: [Backtracking, Binary Tree, DFS] + + +给一个binary tree, 返回所有root-to-leaf path + +#### DFS, backtracking +- Find all paths, bfs/dfs all works. dfs will be simplier to write +- Recursive:分叉. dfs. +- top->bottom: enumerate current node into the list, carry to next level, and backtrack +- top->bottom is trivial to consider: path flows from top->bottom + +#### DFS, bottom->up +- We can also take current node.left or node.right to generate list of results from the subproblem +- let dfs return list of string candidates, and we can run pair the list with currenet node, once they come back. +- TODO: can write code to practice + +#### Iterative +- Iterative, 非递归练习了一下 +- 因为要每次切短list, 所以再加了一个Stack 来存level +- 单这道题用dfs更简单, 因为找的就是从头到尾的path, 是dfs的pattern + + + + +--- + +**2. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**3. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**4. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**5. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**6. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + +**7. [Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**8. [Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 + + + +--- + +**9. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**10. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**11. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**12. [Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)** Level: Medium Tags: [BST, Binary Tree] + + +给一个BST, integer range (k1, k2), 找range 里面所有的integer. + +#### BST +- 等于dfs遍历了所有k1<= x <= k2的x node。 +- dfs left, process root, then dfs right +- 这里, 把 left/right/match的情况全部cover了,然后把k1,k2的边框限制好,中间就全部遍历了。 + + + +--- + + + + + + + +## Reservior Sampling (1) +**0. [Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Random%20Pick%20Index.java)** Level: Medium Tags: [Reservior Sampling] + +time: O(n) +space: O(n) for input int[], O(1) extra space used + +#### Reservior sampling +- Random choose: think about reservoir sampling. https://www.youtube.com/watch?v=A1iwzSew5QY +- Use random generator rd.nextInt(x) pick integer between [0, x) +- try all numbers, when target is met, we want to model reservoir sampling: +- item was chosen out of i samples, and all other samples are failed. +- where we can use 'count' to represent the denominator/base to choose. +- **HAVE TO finish all samples** to make sure equal opportunity +- we can pick that last matched item as result +- `rd.nextInt(count++) == 0` make sure we are always picking num == 0 to meet definition of reservoir sampling. + +#### Knowledge +- If multiply these probablities together to get the probability of one item being chosen with reservior sampling: +- probability = 1/i * (1 - 1/i+1) * (1 - 1/i+2) ....(1 - 1/n) = 1/n + + + + +--- + + + + + + + +## Expression Tree (5) +**0. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**1. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**2. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**3. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**4. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + + + + + + + +## Binary Search (40) +**0. [Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +Allow duplicates之后: +因为最终binary search的结果也是O(n) +所以这道题要记得: 既然是O(n), 那来个简单的for loop 也就好了。 + +当然,要跟面试官提起来原因。别一上来就只有for。。。 + + +--- + +**1. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + + +--- + +**2. [Guess Number Higher or Lower.java](https://github.com/awangdev/LintCode/blob/master/Java/Guess%20Number%20Higher%20or%20Lower.java)** Level: Easy Tags: [Binary Search] + + +binary search 公式 + + + +--- + +**3. [Two Sum II - Input array is sorted.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20II%20-%20Input%20array%20is%20sorted.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +升序array, 找2SUM. + +#### Two pointers +- 排序好的array. Two pointer移动start和end,核查sum. +- 注意sum用long. +- O(n) time + +#### Binary Search, 因为已经排好序了啊 +- 定住一个valueA, 然后在剩下的里面 binary serach 找 (target - valueB) +- for loop O(n), binary search O(logn) +- overall time: O(nLogN), 就不写了 + + + +--- + +**4. [2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +与 2sum II - input array is sorted类似. 都是sort array, 然后two pointer. + +LintCode的题. 注意找的是greater/bigger than target。 + +由于给定条件允许O(nLogn): + sort + two pointer + +while里面two pointer移动。每次如果num[left]+num[right] > target,那么其中所有num[left++]的加上num[right]都>target. +也就是,num[right]不动,计算加入挪动left能有多少组,那就是: right-left这么多。 全部加到count上去。 +然后right--.换个right去和前面的left部分作比较。 + + + +--- + +**5. [Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)** Level: Hard Tags: [Binary Search, DP, Partition DP] + + +给一串书pages[i], k个人, pages[i] 代表每本书的页数. k个人从不同的点同时开始抄书. + +问, 最快什么时候可以抄完? + +#### Partition DP +- 第一步, 理解题目要求的问题: 前k个人copy完n本书, 找到最少的用时; 也可以翻译成: `n本书, 让k个人来copy, 也就是分割成k段`. +- 最后需要求出 dp[n][k]. 开: int[n+1][k+1]. +- 原理: +- 1. 考虑最后一步: 在[0 ~ n - 1]本书里, 最后一个人可以选择copy 1 本, 2 本....n本, 每一种切割的方法的结果都不一样 +- 2. 讨论第k个人的情况, 在 j = [0 ~ i] 循环. 而循环j时候最慢的情况决定 第k个人的结果(木桶原理): `Math.max(dp[j][k - 1], sum)`. +- 3. 其中: `dp[j][k-1]` 是 [k-1]个人读完j本书的结果, 也就是著名的`上一步`. 这里循环考虑的是第k个人不同的j种上一步 : ) +- 4. 循环的结果, 是要存在 dp[i][k] = Math.min(Math.max(dp[j][k - 1], sum[j, i]), loop over i, k, j = [i ~ 0]) +- Time: O(kn^2), space O(nk) + +##### Init +- Init: dp[0][0] = 0, 0个人0本书 +- Integer.MAX_VALUE的运用: +- 当 i = 1, k = 1, 表达式: dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); +- 唯一可行的情况就只有一种: i=0, k=0, 刚好 0 个人 copy 0 本书, dp[0][0] = 0. +- 其他情况, i = 1, k = 0, 0 个人读 1本书, 不可能发生: 所以用Integer.MAX_VALUE来冲破 Math.max, 维持荒谬值. +- 当 i=0, k=0 的情况被讨论时候, 上面的方程式才会按照实际情况计算出 dp[i][k] +- 这道题的init是非常重要而tricky的 + +##### 计算顺序 +- k个人, 需要一个for loop; +- k个人, 从copy1本书开始, 2, 3, ... n-1,所以 i=[1, n], 需要第二个for loop +- 在每一个i上, 切割的方式可以有[0 ~ i] 中, 我们要计算每一种的worst time + +##### 滚动数组 +- [k] 只有和 [k - 1] 相关 +- Space: O(n) + +#### Binary Search +- 根据: 每个人花的多少时间(time)来做binary search: 每个人花多久时间, 可以在K个人之内, 用最少的时间完成? +- time variable的范围不是index, 也不是page大小. 而是[minPage, pageSum] +- validation 的时候注意3种情况: 人够用 k>=0, 人不够所以结尾减成k<0, 还有一种是time(每个人最多花的时间)小于当下的页面, return -1 +- O(nLogM). n = pages.length; m = sum of pages. + + + + +--- + +**6. [Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Perfect%20Square.java)** Level: Review Tags: [Binary Search, Math] + + +Binary找sqrt. 基本 mid+1, mid-1 template. +注意: define index as long. + + + +--- + +**7. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**8. [Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)** Level: Medium Tags: [Array, Binary Search] + + +binary search. +Goal: find peak, where both sides are descending +最左边, 最右边是Integer.MIN_VALUE时候, 也能构成中间数mid是peak的条件. + +Note: +没有必要特别check (mid-1)<0或者(mid+1)>=n. +证明: +1. 最左端: 当start=0, end = 2 => mid = 1, mid-1 = 0; +2. 最右端: 当end = n - 1, start = n - 3; mid = (start+end)/2 = n - 2; +那么mid + 1 = n - 2 + 1 = n - 1 < n 是理所当然的 + + + +--- + +**9. [Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)** Level: Medium Tags: [Binary Search, Math] + + +傻做就O(n), 要更好就考虑O(logN). +减少重复计算, 一切两半. + +注意: +- n/2的奇数偶数 +- n的正负 +- n == 0的情况, x == 0, x == 1 的情况. + + +--- + +**10. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**11. [Kth Smallest Element in a Sorted Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20Sorted%20Matrix.java)** Level: Medium Tags: [Binary Search, Heap] + +time: O(n + klogn) +space: O(n) + +给一个sorted matrix, 找kth smallest number (not distinct) + +Related: `Kth Largest Element in an Array` + +#### PriorityQueue +- 和Merge K sorted Array/ List 类似:使用PriorityQueue。 +- 因为Array的element无法直接找到next,所以用一个class Node 存value, x,y positions. +- Initial O(n) time, also find k O(k), sort O(logn) => O(n + klogn) +- 变型: Kth Largest in N Arrays + +#### Binary Search +- we know where the boundary is start/end are the min/max value. +- locate the kth smallest item (x, y) by cutt off partition in binary fasion: +- find mid-value, and count # of items < mid-value based on the ascending matrix +- O(nlogn) + + + + +--- + +**12. [Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +画图, 最小值被rotate之后, 变成array中间的最低谷. +并且, 左边山坡的最小值, 大于右边山坡的最大值. +以此来给binary search做判断. + +O(nlogn) + + + +--- + +**13. [Find Minimum in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Hard Tags: [Array, Binary Search] + + +一个需要严谨思考的题目. 因为有duplicate, 会导致不断平移, 所以最终time complexity是O(n) +所以不如直接扫一遍, 给出答案. + +但是还是写一个Binary Search的样子, 只不过worst结果是O(n) + + + +--- + +**14. [Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)** Level: Hard Tags: [Binary Search, DFS, Divide and Conquer] + + +2Dmatrix, 里面的value有一些递增, 递减的特点(细节比较长, 看原题). 目标是找到peak element + +peak: 比周围4个方向的点value大 + +#### DFS + +##### 基本原理 +- 我们不可能一口气准确定位(x,y), 但是我们可以再一个row/col里面, 找到1D array的 peak. +- 根据这个点, 再往剩下两个方向移动 +- 1. 在中间的一行i=midX, 找到peak所在的y. +- 2. 在中间的一列j=midY, 找到peak所在的x. (有可能强势override之前找到的y, 也就是放弃那一行的peak, 在midY上找peak) +- 3. 根据 (x,y) 的4个neighbor check (x,y)是不是 peak, 如果不是, 像更高的位置移动一格 +- 4. 根据之前算的 midX, midY 把board分成4个象限, 在每一份里面再继续找 +- 这个题目LintCode不给做了, 所以思路对的, 但是解答还没有再次验证. + +##### 剪枝/切分象限 +- 每次只是找到一个row/col里面的peak而已! +- 找到这个点, 就等于把board切成了两半. +- 然后, 再跟剩下的相邻的两个位置比较, 就知道了哪里更大, 就去哪里找peak, 也就是又切了第二刀. +- 切第二刀的时候, 也要把(x, y) 移到需要取的象限. 进行DFS +- 根据mid row 切割: +- http://www.jiuzhang.com/solution/find-peak-element-ii/#tag-highlight-lang-java +- http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf + +##### 时间复杂度 +- 每一个level都减一半 +- T(n) = n + T(n/2) = n + n/2 + n/4 + ... + 1 = n(1 + 1/2 + .... + 1/n) = 2n = O(n) + +#### Binary Search +- TODO +- O(nLogN) + + + +--- + +**15. [Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + + +#### s- qrt(int x) +- 理解题意, 从[0, x]找一个可以m*m=x的值. +- 注意, 如果找不到, 最后问考官该return一个什么值:按道理,因为return int, 会取整,那么return一个平方最close to x就可以. +- 注意 mid 用 long, 因为很可能超过最大int. + +#### sqrt(double x) +- 二分float number, 应该用精度来定义结尾. +- 还是二分, 但是判断条件变成: while ( end - start > eps) +- eps = 1e-12,也就是精度到1e-12 + + + +--- + +**16. [First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Bad%20Version.java)** Level: Easy Tags: [Binary Search] + + +Binary Search + +根据isBadVersion的性质,判断还如何end=mid or start=mid. +isBadVersion 是有方向的嘛,一个点错了,后面全错。 + + + +--- + +**17. [Wood Cut.java](https://github.com/awangdev/LintCode/blob/master/Java/Wood%20Cut.java)** Level: Medium Tags: [Binary Search] + + +二分的思想: 判断的是一个 validate() function, 而不是简单的'==' + +不需要sort! 为什么呢? 因为我们不是在given array上面二分, 我们是根据最大值在[0, max]上二分. + +Overall time: O(nLogM), where M = largest wood length + + + +--- + +**18. [Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +- 注意不要思维定式: 以为mid是index +- 这里mid其实是binary search on value [1, n] 的一个value. +- 再次用到validate() function + +Time: O(nLogN) + + + +--- + +**19. [Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)** Level: Review Tags: [Array, Binary Search, PreSum] + + +给int[] nums 和 window min size k. window size可以大于K. 找最大的连续数列average value. + +- Binary Search的思想, 用在所要找的这个 average sum 上面. 大小是在[min, max]之中 +- 找k的时候, 是>=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + + + +--- + +**20. [Classical Binary Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Classical%20Binary%20Search.java)** Level: Easy Tags: [Binary Search] + + +#### Binary Search Template +- while: start + 1 < end +- mid = start + (end - start) / 2; +- 根据mid作比较 +- 末尾double check start, end. + + + + +--- + +**21. [Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +#### Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, +- 找到 node.val == target, 或者走位走完, return closest + + + +--- + +**22. [Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样, 如果一样, 直接 2 ^ h - 1就好 +- 不一样的话, 再DFS + +##### Trick +- 直接DFS会timeout, O(n), 其实可以optimize +- to pass the test with O(h^2), 位运算: Math.pow(2, h) = 2 << (h - 1). 神奇! +- 2 << 1就是把所有bits往左移动一位, 也就是 * 2 + +#### Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + + + +--- + +**23. [Closest Number in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Number%20in%20Sorted%20Array.java)** Level: Easy Tags: [Binary Search] + + +- Binary Search 的一种变型, LintCode无法再跑一边. +- 考虑mid-1, mid+1. +- 一旦没有mid = target.index。 那么target最终就narrow down在(mid-1,mid) 或者(mid,mid+1) + + + +--- + +**24. [Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)** Level: Hard Tags: [Binary Search, Coordinate DP, DP] + + +俄罗斯套娃, 这里用envelope来表现. 给一串array, 每一个[x, y] 是envelope 长宽. [[5,4],[6,4],[6,7],[2,3]]. + +看用这些套娃, 可以最多套几个. + +#### DP: 1D Coordinate +- envelopes没有顺序, 先排序 (主要根据第一个index排序) +- 然后观察: 排序过后, 就变成了1D的坐标动态规划. +- max number 取决于上一个成功Russian doll的 max value + 1 +- 上一个index不知道, 所以遍历找上一个index. +- 当下index i 的状态, 取决于前面index j 的状态, 所以遍历两个index. +- O(n^2)的DP, n = envelopes.length; + +#### DP: 2D Coordinate +- 这个方法是自己想出来的, 但是时间复杂度太大, timeout +- 把envelop标记在2D grid上面, 然后像走机器人一样, 求到最右下角的最大 count max. +- count 当下能存在多少Russian doll +- 两种情况: 当下coordinate 没有target, 当下coordinate有target +- 当下coordinate 没有target: 如同机器人走法, Math.max(dp[i - 1][j], dp[i][j - 1]) +- 当下coordinate 有target: dp[i - 1][j - 1] + dp[i][j] +- timeout: O(n^2), n = largest coordinate. + + + + +--- + +**25. [Last Position of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Last%20Position%20of%20Target.java)** Level: Easy Tags: [Binary Search] + + +给一个sorted integer array, 找target出现的最后的index. array 里有重复数字 + +有重复,不是末尾点,继续binary search + + + +--- + +**26. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**27. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**28. [Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的sum. + +#### Segment Tree + Binary Search +- 其实是segment tree 每个node上面加个sum +- 记得Segment Tree methods: Build, Query +- Note: 存在SegmentTreeNode里面的是sum. 其他题目可能是min,max,count ... or something else. + + + +--- + +**29. [Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)** Level: Medium Tags: [Array, Binary Search] + + +给2D matrix, 每行sorted, 每行的首位都大于上一行的末尾. goal: find target from matrix + +#### 2D matrix 转1D array +- 一行一行是从小到大, sorted, 连续的, 可以看做1D sorted array +- Binary Search + + + +--- + +**30. [Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)** Level: Medium Tags: [Binary Search, Divide and Conquer] + + +给matrix, 每一行sorted, 每一列从上往下sorted, 找target是否存在 + +#### Binary Search +- 根据给定的性质, 其实点选的极端一点: x = 最下面的row, y = 当下一行里面最小的left position. +- (x,y)在左下角 +- 在此情况下, 只能往一个方向运行: 如果小于target, y++; 如果大于target, 那么只能x-- +- 每次操作, 都是删掉一行, 或者一列, 再也不需要回头看 +- `while (x >= 0 && y < col) {}` 确保不会跑脱 +- 同样的方式: 可以从右上角(0, col - 1) 开始, 代码稍微改一改 + +#### Divide and Conquer? +- TODO + + + +--- + +**31. [Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)** Level: Medium Tags: [Array, Binary Search] + + +给sorted array, 有重复数字, 找跟target重合所在的range. + +#### Binary Search +- 2个while loop +- 找first/last occurance +- TODO: Can the code be simplified? + + + + +--- + +**32. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**33. [H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index%20II.java)** Level: Medium Tags: [Binary Search] + + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + + + +--- + +**34. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**35. [Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + +time: log(n) +space: O(1) + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 +- 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` +- `start mid`: start = mid; +- 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- `mid < target < end`: start = mid; +- `target < mid`: end = mid; + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**36. [Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)** Level: Hard Tags: [Binary Search, Lint, Segment Tree] + + +SegmentTree大集合. Methods: `build, query, modify`. 不难。只是要都记得不犯错. + +#### Segment Tree +- build: recursively build children based on index-mid and link to curr node +- query: recursively try to find `node.start == targetStart && node.end == targetEnd`. Compare with node.mid +- modify: recursively try to find `node.start == targetStart && node.end == targetEnd`; modify and recursively assign upper interval with updated interval property. + + + +--- + +**37. [Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字, array size = n. 给一串query: 每个query是一个数, 目的找 count# items smaller than query element. + +#### Segment Tree +- 和平时的segment tree问题不同。 [0 ~ n] 代表实际数字: based on real value的segment tree. +- Modify时,把array里面的value带进去,找到特定的位子, 然后count + 1. +- 最终在SegmentTree leaf上面全是array里面实际的数字。 +- node.count: 在node range里面的有多少个数字 + +##### right use of modify() +- build() 只是 empty segment tree, 没有property +- modify() 需要: 1. 找到left, update count+=1; 2. aggregate all parent when after returning +- 所以每一个modify 都是在整个path上所有的node上 + count + +##### query trick +- 在query前,给进去的start和end是: 0 ~ value-1. +- `value-1`: 找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. + +##### About other basic segment tree setup +- [那么其他做过的SegmentTree是怎么样呢?] +- 那些构成好的SegmentTree(找min,max,sum)也有一个Array。但是构成Tree时候,随Array的index而构架。 +- 也就是说,假如有Array[x,y,....]:在leaf,会有[0,0] with value = x. [1,1] with value = y. +- [但是这题] +- 构成时,是用actual value.也就是比如Array[x,y,....]会产生leaf:[x,x]with value = ..; [y,y]with value =... +- 其实很容易看穿: +- 若给出一个固定的array构成 SegmentTree,那估计很简单:按照index从0~array.lengh,leaf上就是[0,0] with value = x. +- 若题目让构造一个空心SegmentTree, `based on value 0 ~ n-1 (n <= 10000)`, 然后把一个Array的value modify 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**38. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**39. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + + + + + + + +## TreeMap (1) +**0. [My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)** Level: Medium Tags: [Array, TreeMap] + + +Given a list of interval as calendar items. Check if newly added calendar item is overlapping. + +Understand it is only checking time, but not requiring to insert into right spot. No need to overthink. + +#### Simply O(n) check on array +- number of test cases is small, like 1000, so less concern about the time complexity +- simply loop over the list of intervals, and check if any overlapping. +- where to insert does not really matter: every time we are just checking for overlaopping, not merging any range +- **IMPORTANT**: if interval over lapping, they will have this property `Math.max(s1, s2) < Math.min(e1, e2)`. This will help detect the overlapping very easily. +- O(n^2) runtime, with simple code. But somehow this approach is faster than the TreeMap solution: maybe the test cause causes avg O(n)? + +#### TreeMap +- One constraint from the simply array solution: it always cost O(n) to find the potential overlapping interval +- We can manually sort and always manually try to find the correct element via binary search, or we could store the interval in a treeMap, where the intervals are sorted by `start`. +- As result, all we need to do for book(start, end) is to find the next element ceiling(start), last element floor(start), and check for overlapping +- This approach also saves the custom data structure +- Overall cost O(nlogn) + +##### About TreeMap +- always with key sorted ascendingly +- more costly than regular HashMap because of the sorting. Building treemap of n items: O(nlogn) + +#### Sweep line +- use `Point{int start, end; boolean start}` to mark start/end of class. Add to pq. +- Adding new item to pq, sort, and check if overlapping occurs by counting started classes +- If started classes > 1, that means we overlapped. +- Every time it could consume all classes to find the overlap, O(n^2). +- Not quite need to sort or insert at correct point, and this solution requires longer code. Not quite worthy it for a simple problem. + + + + +--- + + + + + + + +## Deque (2) +**0. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**1. [Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Deque, Monotonous queue +- 维持monotonuous queue: one end is always at max and the other end is min. Always need to return the max end of queue. +- when adding new elements x: start from small-end of the queue, drop all smaller elements and append to first element larger than x. +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`. +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! + + + +--- + + + + + + + +## Array (107) +**0. [Missing Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Ranges.java)** Level: Medium Tags: [Array] + + +#### Basic Implementation +- O(n) +- 两个pointer, 每次计较prev和curr之间的部分. +- 然后prev = curr,向前移动一格 +- TODO: check the edge case and make sure max/min of int are checked + + + +--- + +**1. [Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Permutation.java)** Level: Medium Tags: [Array] + + +需斟酌: why reverse is need? why we are looking for k? + +Permutation的规律: +1. 从小的数字开始变化因为都是从小的数字开始recursive遍历。 +2. 正因为1的规律,所以找大的断点数字要从末尾开始: 确保swap过后的permutation依然是 前缀固定时 当下最小的。 + +steps: +1. 找到最后一个上升点,k +2. 从后往前,找到第一个比k大的点, bigIndex +3. swap k && bigIndex +4. 最后反转 (k+1,end) + + + + +--- + +**2. [Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)** Level: Easy Tags: [Array, Two Pointers] + + +- 更正常的start/end partition pointer类似: when condition meet, swap +- Clean up TODO + + + +--- + +**3. [Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array] + + + + + +--- + +**4. [Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array] + + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**5. [Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)** Level: Medium Tags: [Array, Enumeration] + + +#### 找公式规律 +- 找到个转角度的规律公式: r = c; c = (w - r) +- 用temp variable, swap in place. + + + +--- + +**6. [Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +Allow duplicates之后: +因为最终binary search的结果也是O(n) +所以这道题要记得: 既然是O(n), 那来个简单的for loop 也就好了。 + +当然,要跟面试官提起来原因。别一上来就只有for。。。 + + +--- + +**7. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + + +--- + +**8. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + + +--- + +**9. [Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)** Level: Easy Tags: [Array, Math] + + +简单的实现, 加1, 进位. 唯一取巧的地方, 最后如果要多一位, 一定是10000...这个模式, 可以走捷径, 直接来个+1size的array, 然后第一位=1. +注意,转换成long也不合理,用太多memory. + + +--- + +**10. [Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)** Level: Medium Tags: [Array, Sort] + + +方法1: +排序, nLog(n). 然后把直线上坡变成层叠山峰, 需要每隔几个(题目中是每隔2位)就做个swap 造成高低不平. +Note: 每隔山峰之间是相互没有关系的, 所以每次只要操心 [i], [i-1]两个位置就好了. + +方法2: +O(n) +看好奇数偶数位的规律, 然后根据题目给出的规律, 跑一遍, 每次只关注两个位置: 把不合适的[i], [i-1]调换位置就好了. + +方法3: +跟法2一样, 只是更巧妙一点罢了: +第一遍想太多. 其实做一个fall-through就能把问题解决,原因是因为: +这样的fall-through每次在乎两个element,可以一口气搞定,无关乎再之前的elements。 +特别的一点:flag来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**11. [Two Sum II - Input array is sorted.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20II%20-%20Input%20array%20is%20sorted.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +升序array, 找2SUM. + +#### Two pointers +- 排序好的array. Two pointer移动start和end,核查sum. +- 注意sum用long. +- O(n) time + +#### Binary Search, 因为已经排好序了啊 +- 定住一个valueA, 然后在剩下的里面 binary serach 找 (target - valueB) +- for loop O(n), binary search O(logn) +- overall time: O(nLogN), 就不写了 + + + +--- + +**12. [2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +与 2sum II - input array is sorted类似. 都是sort array, 然后two pointer. + +LintCode的题. 注意找的是greater/bigger than target。 + +由于给定条件允许O(nLogn): + sort + two pointer + +while里面two pointer移动。每次如果num[left]+num[right] > target,那么其中所有num[left++]的加上num[right]都>target. +也就是,num[right]不动,计算加入挪动left能有多少组,那就是: right-left这么多。 全部加到count上去。 +然后right--.换个right去和前面的left部分作比较。 + + + +--- + +**13. [Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### DP +- 求最值, 想到DP. Time/Space O (n) +- 两个特别处: +- 1. 正负数情况, 需要用两个DP array. +- 2. continuous prodct 这个条件决定了在Math.min, Math.max的时候, +- 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. +- 这也就注定了需要一个global variable 来hold result. + +#### Space optimization, rolling array +- maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins +- Time: O(n), space: O(1) + + + +--- + +**14. [3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/3%20Sum%20Closest.java)** Level: Medium Tags: [Array, Two Pointers] + + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**15. [Triangle Count.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangle%20Count.java)** Level: Medium Tags: [Array] + + +其实也就是3sum的变形, 或者而说2sum的变形. 主要用2 pointers来做. +注意, 在选index时候每次定好一个 [0 ~ i], 在这里面找点start, end, 然后i 来组成triangle. +Note巧妙点: +在此之中, 如果一种start/end/i 符合, 那么从这个[start~end]中, 大于start的都可以, 所以我们count+= end-start. +反而言之, nums[i] + + + +--- + +**16. [3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum.java)** Level: Medium Tags: [Array, Two Pointers] + + + +#### sort array, for loop + two pointer. O(n^2) +- 处理duplicate wthin triplets: +- 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. +- 如果是triplet内有重复, 用完start point, 移动到结尾. + +Previous notes: +注意: + 1. 找 value triplets, 多个结果。注意,并非找index。 + 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 + +步骤: + 1. For loop 挑个数字A. + 2. 2Sum 出一堆2个数字的结果 + 3. Cross match 步骤1里面的A. + +时间 O(n^2), 两个nested loop + +另外, 还是可以用HashMap来做2Sum。稍微短点。还是要注意handle duplicates. + + + + +--- + +**17. [Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +跟unique path的grid一样, 目标走到右下角, 但是grid里面可能有obstacle, 不能跨越. 求unique path 的count. + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- 考虑最终结尾需要的状态:如何组成,写出公式. +- 公式中注意处理能跳掉的block, marked as 1. '到不了', 即为 0 path in dp[i][j]. + + + +--- + +**18. [3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum%20Smaller.java)** Level: Medium Tags: [Array, Two Pointers] + + +一般的O(n3)肯定不行。在此基础上优化。 +发现j,k满足条件时候,(k - j)就是所有 sum target, 又因为j不能后退,只能k--,那么问题就被锁定了. 这样可以做到O(n2) + + + +--- + +**19. [Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/Array%20Partition%20I.java)** Level: Easy Tags: [Array] + + +给串数字, size=2n, 找pairs, 然后需要sum of min(pair) 最大. + +(a1, b1), (a2, b2), ..., (an, bn) which makes sum of min(ai, bi) for all i from 1 to n as large as possible. + +#### Sort, basics +- 从结果出发, 只需要找到加法的结果,而不强调具体配对. +- 写一写example就能做 +- 找到排列取单数位的规律,再考虑负数和正数的相同规律,即可找到排列求解的方法。 +- sort, O(nlogn) + + + + +--- + +**20. [1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/1-bit%20and%202-bit%20Characters.java)** Level: Easy Tags: [Array] + + +方法1: +Greedy. +从第一个bit开始数: 如果遇到1, 一定是跳两位;如果遇到0, 一定是跳一位. +loop to end, and see if index reaches the end. + +方法2: +用DP硬做了一下: +1. 如果i位是0, 那么前面dp[i-1]或者dp[i-2] true就够了. +2. 如果i位是1, 那么i-1位必须是1才满足规则, 并且 dp[i-2]需要true. + + + +--- + +**21. [Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Non-decreasing%20Array.java)** Level: Easy Tags: [Array] + + +比较升序的时候, 必须要估计到 i-1, i, i+1三个数位. +写出来i-1, i+1之间的关系, 然后做合理的fix. + +需要真的fix数组, 因为loop through做比较时会用到fix后的数字. + + + +--- + +**22. [Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Consecutive%20Ones.java)** Level: Easy Tags: [Array] + + +Basic. Math.max track结果。 +记得在有对外操作的loop后,一定要把result object清理干净。 + + + +--- + +**23. [Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)** Level: Easy Tags: [Array] + + +方法1: +换到正确的位置。 +需要小心handle i, 因为不知道换到nums[i]上的是什么,所以必须原地清理干净 方能move on. + +方法2: +做标记! +很巧妙地运用了标记的方法, 标记成负数,证明visit过。 +Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! + +方法3: +做标记! +跟方法2类似,也是做标记,这一次是加上一个大于n的数(因为题目给了n的border),最后check一下就好。为不超Integer.MAX_VALUE, 每次加n前,取余数。 + +做标记的方法固然快,但是相对来说比较hacky,在常规的代码中,估计不会用到. + + + + +--- + +**24. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + +time: O(n) +space: O(1) + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + + + +--- + +**25. [Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number%20At%20Least%20Twice%20of%20Others.java)** Level: Easy Tags: [Array] + + +找最大值, 和第二大的值, 看是否符合题意, 就行了. +分析题意, 最简单方法, 可以loop 两遍: 找最值; 作比较. +但其实, 举个反例: 有一个不满足, 就够反对这个'at least twice of alllll others'. + + + +--- + +**26. [Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Toeplitz%20Matrix.java)** Level: Easy Tags: [Array] + + +似乎没什么算法特点, 就是array基本运算, 然后分割成一个helper function去做重复计算, 剪短代码. +注意check MxN 的分界线. + + + +--- + +**27. [Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)** Level: Medium Tags: [Array, Binary Search] + + +binary search. +Goal: find peak, where both sides are descending +最左边, 最右边是Integer.MIN_VALUE时候, 也能构成中间数mid是peak的条件. + +Note: +没有必要特别check (mid-1)<0或者(mid+1)>=n. +证明: +1. 最左端: 当start=0, end = 2 => mid = 1, mid-1 = 0; +2. 最右端: 当end = n - 1, start = n - 3; mid = (start+end)/2 = n - 2; +那么mid + 1 = n - 2 + 1 = n - 1 < n 是理所当然的 + + + +--- + +**28. [Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer] + + +#### Vote 计数 +- vote++, vote--到最后剩下的就是winner. Time O(n), Space O(1) +- Majority Number是指超半数. 超半数的数字, 最后都会至少有vote>=1: match current majority number,vote++;if not, vote--. +- 注意:assume valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 + +#### HashMap count occurance +- Time, Space: O(n) + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**29. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**30. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**31. [Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +画图, 最小值被rotate之后, 变成array中间的最低谷. +并且, 左边山坡的最小值, 大于右边山坡的最大值. +以此来给binary search做判断. + +O(nlogn) + + + +--- + +**32. [Find Minimum in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Hard Tags: [Array, Binary Search] + + +一个需要严谨思考的题目. 因为有duplicate, 会导致不断平移, 所以最终time complexity是O(n) +所以不如直接扫一遍, 给出答案. + +但是还是写一个Binary Search的样子, 只不过worst结果是O(n) + + + +--- + +**33. [Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)** Level: Medium Tags: [Array, Backtracking, DFS] + + +#### DFS, Backtracking: +- 找到开头的字母, 然后recursively DFS 去把word串到底: +- 每到一个字母, 朝四个方向走, 之中一个true就可以. +- Note:每次到一个字母,mark一下'#'. 4个path recurse回来后,mark it back. + +#### Note: other ways of marking visited: +- 用一个boolean visited[][] +- Use hash map, key = x@y + + + + +--- + +**34. [Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. +#### 方法1 +Array, 维持一个左手最高墙array, 右手最高强array. +对于每个index而言, vertically 能存放的最大水柱, 就是靠左右最高墙决定的: +min(leftHighestWall, rightHighestWall) - currHeight. + +#### 方法2 +方法1上面的优化, two pointer, 还是找左边最高和右边最高. O(1) space. +利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +左边按照maxLeft来计算, 右边按照maxRight来计算. + +#### 方法3 +2 Pointers, 双面夹击: +1. 找中间最高bar的index +2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条。 +3. 每次还要减去block自身的height + +#### 方法4 +主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. + + + + +--- + +**35. [Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)** Level: Hard Tags: [Array, Monotonous Stack, Stack] + + +给n个bar,组成柱状图histogram. 求在这一排柱状图里面可以找到的面积最大的长方形. + +思考: 找长方形面积, 无非是找两个index, 然后底边长度 * height. + +#### Monotonous Stack +- 重点是根据找Histogram里面rectangle的性质, 维持一个单调递增的Stack +- 在loop over indexes的时候: +- 如果高度>= previous peek(), 那么对于那个peek, 就意味着, 往下走, 一直走高嘛, 之前的peek总可以继续抄底 +- 什么时候不能抄底了呢? 就是有一个下降趋势的时候 +- 这时候并不是calculate所有前面的peek, 而是考虑 大于 current height的之前所有的peek. +- 把这些peek到 current height 前一格的rectangle全部找出来: stack.pop() +- 这个stack.pop()的过程里面, 其实没有算上 current height, 因为需要留到下一轮, 把current index加进stack 再说 +- 为什么用stack? 因为需要知道连续递增的peek, stack.peek() O(1), 好用 + 而其实不用stack, 也可以用其他方式记录所有height, 只不过要 O(n)去找peek不方便 + +#### 知识点 +- 理解monotonous stack 是如何被维护的 +- 维护monotonous stack 是题目需要, 而不是stack本身性质, 是一种借助 stack.peek() O(1)的巧妙用法. + + + + +--- + +**36. [Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +- 注意不要思维定式: 以为mid是index +- 这里mid其实是binary search on value [1, n] 的一个value. +- 再次用到validate() function + +Time: O(nLogN) + + + +--- + +**37. [Game of Life.java](https://github.com/awangdev/LintCode/blob/master/Java/Game%20of%20Life.java)** Level: Medium Tags: [Array] + + +#### basic +- 简单的implementation, 把count function写清楚就好. +- time: O(mn), extra space: O(mn) +- 注意结尾要一个个board[i][j]copy + +#### follow up +unlimited border? 可能需要分割board. 用大框分割, 每次换行的时候, 重复计算2行就好了. see details below. + +#### improvement: do it in place! +- time: O(mn), extra space: O(1) +- bit manipulation: 用第二个bit来存previous value. +- 因为我们只考虑 0 和 1 而已, 所以可以这样取巧. 但是并不scalable. +- 如果需要存multiple state, 可能需要移动更多位, 或者用一个 state map +- 注意 bit manipulation 的细节: <<1, >>1, 还有 mast的用法: |, & + + + + +--- + +**38. [Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)** Level: Review Tags: [Array, Binary Search, PreSum] + + +给int[] nums 和 window min size k. window size可以大于K. 找最大的连续数列average value. + +- Binary Search的思想, 用在所要找的这个 average sum 上面. 大小是在[min, max]之中 +- 找k的时候, 是>=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + + + +--- + +**39. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**40. [Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +##### DP +- 计数DP.注意方程式前两位置加在一起: 前两种情况没有overlap, 也不会缺情况. +- 注意initialization, 归1. +- 需要initialize的原因是,也是一个reminder: 在方程中会出现-1index +- Of course, row i = 0, or col j = 0, there is only 1 way to access +- time O(mn), space O(mn) + +##### 滚动数组 +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + + + + +--- + +**41. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**42. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**43. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**44. [Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate.java)** Level: Easy Tags: [Array, Hash Table] + + +无序数组, 找是否有重复element, return true/false. + +#### HashSet +- No brain: HashSet. +- Time O(n), Space O(n) + +#### Sort, Binary Search +- Arrays.sort(x): Time O(nLogN), Space O(1) +- 排序后, 重复数会排在一起, 然后 binary search + + + +--- + +**45. [Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20II.java)** Level: Easy Tags: [Array, Hash Table] + + +Unsorted array, 找出是否有duplicate elemenets: 必要条件是, 这两个element的index i,j 的大小最多相差k. + +#### HashSet +- 很巧妙地根据k range地条件, 把HashSet里面的值控制在[i - k, i] +- 每次不断地往set里面加新元素, 从set里减去末尾index的元素 +- 而set.add(x)如果遇到重复, 会return false. +- 一旦在这个length k 的 range里面, 有重复, 就符合条件. +- Time O(n) + +#### HashTable +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k +- Time O(nm), m = # of duplicates + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**46. [Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)** Level: Medium Tags: [Array, DP, Greedy] + + +给出步数,看能不能jump to end. + +#### Greedy - start from index = 0 +- Keep track of farest can go +- 一旦 farest >= nums.length - 1, 也就是到了头, 就可以停止, return true. +- 一旦 farest <= i, 也就是说, 在i点上, 已经走过了步数, 不能再往前跳, 于是 return false +- This can be done using DP. However, greedy algorithm is fast in this particular problem. + +#### Greedy - start from index = n - 1 +- greedy: start from end, and mark last index +- loop from i = [n - 2 -> 0], where i + nums[i] should always >= last index +- check if last == 0 when returning. It means: can we jump from index=0 to the end? +- Time: O(n), beat 100% + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (A[j] >= i - j), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- Time: O(n^2) + + + + +--- + +**47. [Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +找连续的持续上升子序列的长度. + +#### Coordinate DP +- 1D coordinate, dp 的角标, 就是代表 index i 的状态 +- 求最值, dp[i] = 在index i位置的最长子序列 +- 如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1 +- 如果没有持续上升, 那么dp[i] = 1, 重头来过 +- maintain max + +#### Basic +- 用一个数存current count, maintain max + + + +--- + +**48. [Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +#### DP +- Time, Space O(MN) +- 往右下角走, 计算最短的 path sum. 典型的坐标型. +- 注意: init 第一行的时候, 要accumulate dp[0][j - 1] + grid[i][j], 而不是单纯assign grid[i][j] + +#### Rolling Array +- Time O(MN), Space O(1) +- 需要在同一个for loop里面完成initialization, 和使用dp[i][j] +- 原因: dp[i % 2][j] 在被计算出来的时候, 是几乎马上在下一轮是要被用的; 被覆盖前不备用,就白算 +- 如果按照第一种方法, 在开始initialize dp, 看起来固然简单, 但是不方便空间优化 + + + +--- + +**49. [Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)** Level: Hard Tags: [Array, DP, Sequence DP] + + +比stock II 多了一个限制:只有2次卖出机会. + +#### DP加状态 +- 只卖2次, 把买卖分割成5个状态模块. +- 在状态index 0, 2, 4: 没有持有股票. 1. 一直在此状态, max profit不变; 2. 刚卖掉, dp[i][前状态] + profit +- 在状态index 1, 3: 持有股票. 1. 一直在此状态, daily profit. 2. 刚刚买进, 状态改变, 但是没有profit yet: dp[i][前状态] + +##### Partial profit +- 把每天的partial profit (diff)加在一起, 最终的overall profit是一样的. 唯一更好的是, 不需要记录中间买入的时间点. +- 什么时候会积累profit呢? +- 1. 原本就持有股票的, 如果毫无动作, 那么状态不变, 积累profit diff. +- 2. 卖出了股票, 状态改变, 积累profit diff. +- 注意: 只有在状态index: 0, 2, 4, 也就是卖掉股票的时候, 才可以积累profit + +##### Rolling Array +- [i] 只有和 [i-1] 打交道, reduce space +- O(1) space, O(n) time + +#### 找峰头 +- 找峰头;然后往下再找一个峰头。 +- 怎么样在才能Optimize两次巅峰呢?从两边同时开始找Max!(棒棒的想法) +- leftProfit是从左往右,每个i点上的最大Profit。 +- rightProfit是从i点开始到结尾,每个点上的最大profit. +- 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 +- 三个O(n),还是O(n) + + + +--- + +**50. [First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Missing%20Positive.java)** Level: Hard Tags: [Array] + + +给一串无序数字, 有负数: 找这个array里面第一个 missing的 positive integer + +missing positive integer 其实是以 [1, n] 来做比较的. + +#### Array分析, index 技巧 +- 用while loop, 不断地尝试把 number 送到该放的地方 +- 如果 index = nums[i] 超过了nums.length, 当然就不移动了 +- 注意: 检查 val != nums[val], avoid infinitely loop +- 检验: nums[i] 是否等于 i, 如果不对, 就找到了结果 + +#### Edge Case +- 如果nums==null, 其实missing positive integer 自然而然是 1 +- validation时, 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) +- 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +- 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + +**51. [Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + +O(n)跑2遍for. +O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. +特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 + + + +--- + +**52. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**53. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**54. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**55. [Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)** Level: Easy Tags: [Array, Quick Select, Quick Sort] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- quickSelect 可以找到 kth 最小的元素 +- 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- quick select 的template要熟悉一下, 一下子可能想得到, 但写不出来 +- 主要步骤: partition, dfs, only recur on one part of the array + + + + +--- + +**56. [Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)** Level: Easy Tags: [Array, Bit Manipulation, Math] + + +给一串unique数字, 数字取自 [0 ~ n], 无序, 找第一个skipped的数字. + +#### Swap +- 跟First Missing Positive 非常像, 只有一行代码的区别. +- swap 所有的数字, 到自己的correct position +- 最后一个for loop找到错位的index, 也就是缺的数字. + +#### Bit Manipulation +- XOR will only retain bits that are different 1 ^ 0 = 1, but 0^0, 1^1 == 0 +- Use that feature, 把所有value都和index XOR了 +- 剩下的多余的数字, 其实是那个index无法被XOR消掉, 也就是那个缺的number value. +- 注意: 题目告诉数字是 [0 ~ n], 然而缺一个数字, 那么在[0 ~ n - 1] 里面, 最大的数字(不管缺没缺), 一定是 n = nums.length. + +#### HastSet +- 全存, 找missing +- O(n) space, 不合题意 + +#### sorting +- sort, 找1st missing +- O(n log n) 太慢, 不合题意 + + + +--- + +**57. [Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- Two pointers 其实也可以是一个 for loop pointer, 另一个 dynamic variable. +- track unique index +- skip duplicated items +- O(n) + +#### 思考模式: +- Remove Duplicate from Array 不同于remove from linked list. +- LinkedList里面我们是最好不要动node.val的,直接把node去掉。 +- 而array我们很难直接把node去掉,又不能用新array,那么就要: +- 把不重复的element一个个放到最前面。 +- 这个思想跟merge two sorted array (其中一个后续非常长的array可以放下arr1,arr2) 类似。 +- 就是找个不会事后mess up,不会去动得index,把满足条件的element 填进去。这样保证了in place. +- *反向思维*:remove duplicate, 实际上也是找unique elements, and insert into original array + + + +--- + +**58. [Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- 跟 `Remove Duplicates from Sorted Array` 几乎一模一样, 只不过unique index现在可以 validate 2 位 +- 其余一模一样, use index to track unique item; skip if duplicated for more than 2 times +- O(n) time, O(1) space +- 这里也可以真的用2个pointers 写while loop, 但是没有必要, 只是单纯地走一个for loop其实就足够. + + + +--- + +**59. [Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)** Level: Medium Tags: [Array, Backpack DP, DP] + + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + +#### Backpack DP +- 计数问题, 可以想到DP. 其实就是Backpack VI. +- 从x个数字里面找candidate(可以重复用同一个数字), 来sum up to target. 找: # of ways to form the sequence. +- Backpack VI: 给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法 +- dp[i]: # of ways to build up to target i +- consider last step: 如果上一步取的是 candidate A, 那么就该加到dp[i]: +- dp[i] += dp[i - A] +- 要找overall dp[i], 就做一个for loop: dp[i] = sum{dp[i - num]}, where for (num: nums) +- Time: O(mn). m = size of nums, n = target +- If we optimize dp for loop, 需要Sort nums. O(mlogm). will efficient 如果m是constant或者relatively small. Overall: O(n) + +#### DFS, backtracking +- 尽管思考方式是对的, 但是 times out +- 可以重复使用数字的时候, 比如用1 来拼出 999, 这里用1就可以走999 dfs level, 不efficient + + + +--- + +**60. [Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)** Level: Hard Tags: [Array, Hash Table, Union Find] + + +给一串数字, unsorted, 找这串数字里面的连续元素序列长度 (consecutive序列, 是数字连续, 并不是说要按照原order) + +#### HashSet +- 要想看连续元素, 必须要num++, num--这样搜索 +- 1. 需要O(1)找到元素 +- 2. 需要简单快速找到 num - 1, num + 1. +- 如果用min,max开array, 耗费空间 +- 用HashSet来存, 用set.contains() 来查找 num - 1, num + 1 存在与否 +- for loop. O(n) +- 里面的while loop 一般不会有O(n); 一旦O(n), 也意味着set 清零, for loop也不会有更多 inner while 的衍生. +- overall O(n) 时间复杂度 + + +#### Union Find +- 最终是要把相连的元素算一下总长, 其实也就是把元素group起来, 相连的group在一起, 于是想到UnionFind +- 这里用到了一个`int[] size` 来帮助处理 `合并的时候parent是哪个`的问题: 永远往group大的union里去 +- main function 里面, 有一个map来track, 每个元素, 只处理1遍. +- union的内容: current number - 1, current number + 1 +- https://www.jianshu.com/p/e6b955ca208f + +##### 特点 +- Union Find 在index上做好像更加容易 +- 其他union find function: `boolean connected(a,b){return find(a) == find(b)}` + + + +--- + +**61. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**62. [Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Two Pointers +- 木桶理论。盛水的最高取决于最低的那面墙。 +- 左右两墙,往中间跑动。 +- 另:若一面墙已经小于另外一面,就要移动,换掉矮墙(可能下一面更高,或更低) +- 但决不能换掉当下的高墙,因为低墙已经limit的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**63. [Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element%20II.java)** Level: Medium Tags: [Array] + + +#### Sort + count +- O(nlogN) + +#### Two counters +- O(n), count and track valueA, valueB +- count overall apperance at the end for the two items +- save to result +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +--- + +**64. [Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)** Level: Medium Tags: [Array, Binary Search] + + +给2D matrix, 每行sorted, 每行的首位都大于上一行的末尾. goal: find target from matrix + +#### 2D matrix 转1D array +- 一行一行是从小到大, sorted, 连续的, 可以看做1D sorted array +- Binary Search + + + +--- + +**65. [Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)** Level: Medium Tags: [Array, Binary Search] + + +给sorted array, 有重复数字, 找跟target重合所在的range. + +#### Binary Search +- 2个while loop +- 找first/last occurance +- TODO: Can the code be simplified? + + + + +--- + +**66. [Merge Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array%20II.java)** Level: Easy Tags: [Array] + + +如题, merge two sorted array into 新的 sorted array + +- 长度已经固定. Basic Implementation +- 如果一个array足够大, merge into this array, 那么就是从末尾merge. + + + +--- + +**67. [Summary Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Summary%20Ranges.java)** Level: Medium Tags: [Array] + + +给一串sorted list, 中间有缺数字, return 所有数字的range string (example 看题目) + +#### Basic implementation +- 用一个list as the buffer to store candidates +- when: 1. end of nums; 2. not continuous integer => convert list to result + + + +--- + +**68. [Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)** Level: Medium Tags: [Array, Enumeration] + + +从(0,0)坐标, 走完spiral matrix, 把结果存在list里. + +#### DX, DY +- Basic implementation, array, enumeration +- 写一下position前进的方向: RIGHT->DOWN->LEFT->UP +- 用一个direction status 确定方向 +- 写一个compute direction function 改变方向 `(direction + 1) % 4` +- `boolean[][] visited` 来track走过的地方 + + + +--- + +**69. [Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort] + + +#### Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn) + + +#### Basic Implementation +- 这里已经给了 **sorted** intervals by start point. +- 直接找到可以insert newInterval的位子. Insert +- 然后loop to merge entire interval array +- 因为给的是个list, 所以方便`intervals.remove(i)` +- remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture +- O(n) + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**70. [Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum.java)** Level: Easy Tags: [Array, Hash Table] + + +#### HashMap +- 相对暴力简洁: 找到一个value, 存一个index +- 若在HashMap里面 match 到结果, 就return HashMap里存的index. +- O(n) space && time. + +#### Sort array, two pointer +- 前后++, --搜索. Sort 用时O(nlogn). +- 1. 第一步 two pointer 找 value. +- 2. 注意,要利用额外的空间保留original array, 用来时候找index. (此处不能用HashMap,因为以value 为key,但value可能重复) +- O(n) space, O(nlogn) time. + + + + +--- + +**71. [K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)** Level: Hard Tags: [Array, BST, TreeSet] + + +题目解析后: find 2 number, that: 1. k slots between the 2 number, 2. no slots taken between the two number. + +#### BST +- BST structure not given, use TreeSet to build BST with each node +- Every time find last/next inorder element +- `treeSet.lower(x)`, `treeSet.higher(x)` +- 一旦位置相隔(k + 1), 就满足题目条件 +- O(nlogn), good enough + +#### Track slots of days +- Reverse the array, save days index into days[], where the new index is slot. +- days[i]: at slot i, which day a flower will be planted +- O(n) +- Needs to understand: http://www.cnblogs.com/grandyang/p/8415880.html + + + +--- + +**72. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**73. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**74. [Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)** Level: Easy Tags: [Array, DFS] + + +#### DFS +- 虽然Easy, 但用到DFS最基本的想法. +- 1. dive deep +- 2. mark VISITED +- 3. sum it up +- Time: worst O(mn), traverse all possible nodes + +- 更要注意, 要从符合条件value==1的地方开始dfs. +- 对于什么island都没有的情况,area应该位0, 而不是Integer.MIN_VALUE, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**75. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + +**76. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**77. [Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)** Level: Medium Tags: [Array, Hash Table, PreSum] + + +给一个int[][] matrix, 找一个sub matrix, where the sum == 0. + +#### PreSum的思想 +- 算出一个右下角点(i,j)到(0,0)的大小: 上一块 + 左一块 + curr node - overlap area +- preSum[i][j]: sum from (0,0) to (i-1,j-1) +- same approach as `subarray sum`: use hashmap to store diff->index; if diff re-appears, that means sum of 0 has occurred +- sequence of calculation: 1. iterate over start row. 2. iterate over end row. 3. iterate over col number (this is where hashmap is stored based on) +- the iteration over col is like a screening: find previous sum and determine result +- Note: 其实并没有真的去找 `== 0` 的解答,而是根据特性来判断 `剩下的/后来加上的一定是0` + + + +--- + +**78. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**79. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**80. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + +**81. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**82. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**83. [Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Anagrams.java)** Level: Medium Tags: [Array, Hash Table] + + +把anagram找到并output + +#### HashMap +- 存在int[26], Arrays.toString(arr) 就是 string key: character frequency map +- anagram都有一样的key, 存进hashmap +- output anagrams + +#### HashMap + Sort +- HashMap 的做法. sort每个string, 存进HashMap, 重复的就是anagrams,最后输出。 +- toCharArray +- Arrays.sort +- Stirng.valueOf(char[]) +- 时间n*L*O(logL),L是最长string的长度。 + +#### Previous Notes +- Arrays.toString(arr)的做法。arr是int[26], assuming only have 26 lowercase letters. +- Count occurrance, 然后convert to String,作为map的key. +- Time complexity: nO(L) +- 另一种做法:http://www.jiuzhang.com/solutions/anagrams/ +- 1. take each string, count the occurrance of the 26 letters. save in int[]count. +- 2. hash the int[] count and output a unique hash value; hash = hash * a + num; a = a * b. +- 3. save to hashmap in the same way as we do. +- 这一步把for s: strs 里面的时间复杂度降到了O(L). L = s.length(). +- Need to work on the getHash() function. +- 时间变成n*O(L). Better. + + + + +--- + +**84. [Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Characters.java)** Level: Easy Tags: [Array, String] + + +determine if characters are unique in string + +#### HashSet +- space O(n), time O(n) + +#### char[] +- space O(n), time O(nlogn) + +#### no additional data structure +- double for loop: O(n^2) + + + + +--- + +**85. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**86. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**87. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**88. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**89. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + +**90. [Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + +time: O(n) +space: O(1) + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 看第二个解答, 进一步简化了代码 +- PreProduct, 跟preSum的感觉有点像, 就是差一位. + + + +--- + +**91. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**92. [Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array] + +time: O(n) +space: O(1) + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, track start/end +- start/end point, 然后比较大小记录dist +- 注意1: 如果第一个座位没有人, 特殊处理, dist = [0 ~ end] +- 注意2: 如果最后一个座位没有人, 特殊处理: dist = [n - 1 - start]; +- 其余: `dist = Math.max(dist, (end - start) / 2)` +- 相关题目: 几乎同样概念 `Binary Gap`, 升级复杂版`Exam Room` + + + + +--- + +**93. [Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### 理解意思是关键 +- 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出 +- 记录每天最小值Min是多少. O(n) +- 每天都算和当下的Min买卖,profit最大多少. + +#### DP +- Find min value for first i items, new dp[n + 1]. +- dp[i]: 前i天, prices最小的price是多少: min cost of first i days +- 然后用当天的price做减法dp[i]算max profit. +- Time, Space: O(n) +- 更进一步, 用一个min来表示min[i], 因为计算中只需要当下的min. + +#### Rolling array +- index i only depend on [i - 2] +- Space O(n) + +#### Brutle Failed +- 每天都试着买进,然后之后的每一天尝试卖出. double for loop, O(n^2). timeout. +- 其中很多都是没必要的计算:[7, 1, 5, 3, 6, 4]。 if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. + + + +--- + +**94. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**95. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**96. [Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Celebrity.java)** Level: Medium Tags: [Array, Greedy] + +time: O(n) +space: O(1) + +有n个人, 其中有个人是celebrity, 满足条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +#### Understand the property +- If brutly find celeb by comparing all possible pair: take complete O(n^2) handshakes. +- Instead, we can perform pruning, or like survival mode: +- 1. Assume a celeb = 0, and compare with all i = [1~ n-1] +- 2. If `celeb candidate know i, set celeb = i` as the next candidate (ex: prev canddiate invalid when he knows i) +- 3. For last standing celeb candidate: compare with all for validation +- Why performing the last run of validation? There could be someone dropped out before we execute `know(celeb, i)`. + +##### 思考逻辑 +- 先写出来[0 ~ n - 1], 最简单的方式 O(n^2) 检查, 记录每个人的状态. +- 逐渐发现, 因为 celeb 谁都不会认识, 那么当任何candidate knows anyone, 他自身就不是celeb. +- 我们可以greedy地, 一旦fail一个, 就立刻假设下一个是celeb candidate +- 最终还是要检查一遍, 避免错漏. +- 想一下happy case: 如果 celeb=0, 那么 know(celeb, i) 永远都是false, 然后 celeb一直保持0, 坚持到verify所有人. + + + +--- + +**97. [Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)** Level: Medium Tags: [Array, Math] + + +#### Array, Math +- 这个问题更在于问题本身的分析 (而且还有多余条件); 最终的for loop 也比较不standard. +- People younger than 15 cannot make requests due to the first rule. +- From the age of 15, people can make requests to the same age: a[i] * (a[i] - 1) requests. +- People can make requests to younger people older than 0.5 * i + 7: a[j] * a[i] requests. +- The third rule is redundant as the condition is already covered by the second rule. +- TODO: the approach. + + + +--- + +**98. [Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + +time: O(1) avg +space: O(n) + +#### Hash Table +- 用`map 来track value->index`, 用`list track index->value` +- map查看value是否存在 +- list maintain 用来 insert/remove/random operations. +- 特点: 一旦remove, 换到list结尾然后 `list.remove(list.size() - 1)`, 这样remove的cost更低. +- list.remove(object) 应该是要O(logn) 做一个search的. + + + +--- + +**99. [Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + +time: log(n) +space: O(1) + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 +- 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` +- `start mid`: start = mid; +- 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- `mid < target < end`: start = mid; +- `target < mid`: end = mid; + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**100. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**101. [My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)** Level: Medium Tags: [Array, TreeMap] + + +Given a list of interval as calendar items. Check if newly added calendar item is overlapping. + +Understand it is only checking time, but not requiring to insert into right spot. No need to overthink. + +#### Simply O(n) check on array +- number of test cases is small, like 1000, so less concern about the time complexity +- simply loop over the list of intervals, and check if any overlapping. +- where to insert does not really matter: every time we are just checking for overlaopping, not merging any range +- **IMPORTANT**: if interval over lapping, they will have this property `Math.max(s1, s2) < Math.min(e1, e2)`. This will help detect the overlapping very easily. +- O(n^2) runtime, with simple code. But somehow this approach is faster than the TreeMap solution: maybe the test cause causes avg O(n)? + +#### TreeMap +- One constraint from the simply array solution: it always cost O(n) to find the potential overlapping interval +- We can manually sort and always manually try to find the correct element via binary search, or we could store the interval in a treeMap, where the intervals are sorted by `start`. +- As result, all we need to do for book(start, end) is to find the next element ceiling(start), last element floor(start), and check for overlapping +- This approach also saves the custom data structure +- Overall cost O(nlogn) + +##### About TreeMap +- always with key sorted ascendingly +- more costly than regular HashMap because of the sorting. Building treemap of n items: O(nlogn) + +#### Sweep line +- use `Point{int start, end; boolean start}` to mark start/end of class. Add to pq. +- Adding new item to pq, sort, and check if overlapping occurs by counting started classes +- If started classes > 1, that means we overlapped. +- Every time it could consume all classes to find the overlap, O(n^2). +- Not quite need to sort or insert at correct point, and this solution requires longer code. Not quite worthy it for a simple problem. + + + + +--- + +**102. [Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素。 +- 注意,从尾部,是大数字优先排末尾的. + + + +--- + +**103. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**104. [Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Move%20Zeroes.java)** Level: Easy Tags: [Array, Two Pointers] + + +Move non-zero elements to front of array; preseve order. + +#### Two Pointers +- Outside pointer that moves in certain condition. +- Save appropirate elements + + + +--- + +**105. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + +**106. [The Spiral Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Spiral%20Matrix%20II.java)** Level: Medium Tags: [Array] + + +#### Move forward till end +- Similar concept as `The Maze`: keep walking until hit wall, turn back +- fix direction `dx[direction % 4]` + + + +--- + + + + + + + +## Geometry (2) +**0. [Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)** Level: Hard Tags: [Design, Geometry, Hash Table] + + +看的list of coordinates 是否能组成perfect rectangle, 并且不允许overlap area. + +#### 画图发现特点 +- 特点1: 所有给出的点(再找出没有specify的对角线点), 如果最后组成perfect rectangle, 都应该互相消除, 最后剩下4个corner +- 特点2: 找到所有点里面的min/max (x,y), 最后组成的 maxArea, 应该跟过程中accumulate的area相等 +- 特点1确保中间没有空心的部分, 保证所有的重合点都会互相消除, 最后剩下4个顶点 +- 特点2确保没有重合: 重合的area会最终超出maxArea + + + +--- + +**1. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + + + + + + + +## Memoization (12) +**0. [Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + +#### DP +- 找对方程dp[x], 积累到amount x最少用多少个coin: #coin是value, index是 [0~x]. +- 子问题的关系是: 如果用了一个coin, 那么就应该是f[x - coinValue]那个位置的#coins + 1 + +##### initialization +- 处理边界, 一开始0index的时候, 用value0. +- 中间利用Integer.MAX_VALUE来作比较, initialize dp[x] +- 注意, 一旦 Integer.MAX_VALUE + 1 就会变成负数. 这种情况会在coin=0的时候发生. + +##### Optimization +- 方法1: 直接用Integer.MAX_VALUE +- 方法2: 用-1, 稍微简洁一点, 每次比较dp[i]和 dp[i - coin] + 1, 然后save. 不必要做多次min比较. + +#### Memoization +- dp[i] 依然表示: min # of coints to make amount i +- initialize dp[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录dp[amount] 如果已经给过value, 不要重复计算, 直接return. +- 但是这道题没必要强行做memoization, 普通DP的状态和方程相对来说很好找到 + + + +--- + +**1. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**2. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**3. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**4. [Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)** Level: Easy Tags: [DP, Memoization, Sequence DP] + + +每一步可以走1步或者2步, 求总共多少种方法爬完梯子. + +#### Recursive + Memoization +- 递归很好写, 但是重复计算, timeout. time: O(2^n) +- O(2^n): each n can spawn 2 dfs child, at next level, it will keep spawn. Total 2^n nodes will spawn. +- 用全局变量int[] memo 帮助减少重复计算 +- O(n) time, space + +#### DP +- 加法原理, 最后一步被前两种走法决定: dp[i] = dp[i - 1] + dp[i - 2] +- 基础sequence DP, int[] dp = int[n + 1]; +- DP[]存的是以 1-based index的状态 +- dp[i]: count # of ways to finish 前i个 台阶 +- 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1] +- dp[0]往往是有特殊状态的 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**5. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**6. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**7. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**8. [Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)** Level: Easy Tags: [DP, Math, Memoization] + + +#### Memoization +- fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + +#### DP array. +- 滚动数组, 简化DP + +#### recursively calculate +- recursively calculate fib(n - 1) + fib(n - 2). 公式没问题, 但是时间太长, timeout. + + + + +--- + +**9. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + +**10. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**11. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + + + + + + + +## Two Stacks (1) +**0. [Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Recursive +trivial, 先加left recursively, 再加right recursively, 然后组成头部. + +#### Stack +- 双stack的思想, 需要在图纸上画一画 +- 原本需要的顺序是: 先leftChild, rightChild, currNode. +- 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild +- 这样出来的结果是reverse的, 那么翻转一下就可以了. +- v1做的时候用了stack1, stack2, 因为根据这个双stack的思想而来 +- v2简化, 可以放在一个stack里面, 每次record result 的时候: rst.add(0, item); + +##### 利用stack的特点 +- 每次加element进stack的时候, 想要在 bottom/后process的, 先加 +- 想要下一轮立刻process的, 最后push进stack. + +##### 注意 +这些binary tree traversal的题目.常常有多个做法:recursive or iterative + + + +--- + + + + + + + +## Tree DP (1) +**0. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + + + + diff --git a/WordPress.txt b/WordPress.txt index 4e01b35..67380f5 100644 --- a/WordPress.txt +++ b/WordPress.txt @@ -1,2 +1,2 @@ -Java Solutions to problems from LintCode(http://LintCode.com). -
#Problem      Level  Language
02 Sum.javaJava
13 Sum Closest.javaJava
23 Sum.javaJava
34 Sum.javaJava
4A+B.javaJava
5Add Binary.javaJava
6Anagrams.javaJava
7Backpack.javaJava
8Balanced Binary Tree.javaJava
9Best Time to Buy and Sell Stock I.javaJava
10Best Time to Buy and Sell Stock II.javaJava
11Best Time to Buy and Sell Stock III .javaJava
12Binary Representation.javaJava
13Binary Search.javaJava
14Binary Tree Level Order Traversal II.javaJava
15Binary Tree Level Order Traversal.javaJava
16Binary Tree Maximum Path Sum.javaJava
17Binary Tree Preorder Traversal.javaJava
18Binary Tree Zigzag Level Order Traversal.javaJava
19BinaryTreeInorderTraversal.javaJava
20BinaryTreePostorderTraversal.javaJava
21Climbing Stairs.javaJava
22Clone Graph.javaJava
23Combination Sum II.javaJava
24Combination Sum.javaJava
25Combinations.javaJava
26Compare Strings.javaJava
27Construct Binary Tree from Inorder and Postorder Traversal.javaJava
28Construct Binary Tree from Inorder and Preorder Traversal.javaJava
29Convert Integer A to Integer B.javaJava
30Convert Sorted Array to Binary Search Tree With Minimal Height.javaJava
31Convert Sorted List to Binary Search Tree.javaJava
32Copy List with Random Pointer.javaJava
33Count 1 in Binary.javaJava
34Count and Say.javaJava
35Delete Digits.javaJava
36Delete Node in the Middle of Singly Linked List.javaJava
37Distinct Subsequences.javaJava
38Easy Reverse Linked List.javaJava
39Edit Distance.javaJava
40Fast Power.javaJava
41Fibonacci.javaJava
42Find a Peak.javaJava
43Find Minimum in Rotated Sorted Array II.javaJava
44Find Minimum in Rotated Sorted Array.javaJava
45Find the Connected Component in the Undirected Graph.javaJava
46First Bad Version.javaJava
47First Missing Positive.javaJava
48Gas Station.javaJava
49Hash Function.javaJava
50Implement Queue by Two Stacks.javaJava
51Insert Interval.javaJava
52Insert Node in a Binary Search Tree .javaJava
53Insertion Sort List.javaJava
54Interleaving String.javaJava
55Invert Binary Tree.javaJava
56Jump Game II.javaJava
57Jump Game.javaJava
58Kth Largest Element.javaJava
59Largest Number.javaJava
60Largest Rectangle in Histogram.javaJava
61Length of Last Word.javaJava
62Linked List Cycle.javaJava
63Longest Common Prefix.javaJava
64Longest Common Subsequence.javaJava
65Longest Common Substring.javaJava
66Longest Consecutive Sequence.javaJava
67Longest Increasing Continuous subsequence.javaJava
68Longest Words.javaJava
69Lowest Common Ancestor.javaJava
70Majority Number II.javaJava
71Majority Number III.javaJava
72Majority Number.javaJava
73Maximum Depth of Binary Tree.javaJava
74Maximum Product Subarray.javaJava
75MaximumSubarray.javaJava
76MaximumSubarrayII.javaJava
77Median.javaJava
78Merge Intervals.javaJava
79Merge k Sorted Lists.javaJava
80Merge Sorted Array .javaJava
81Merge Sorted Array II.javaJava
82Merge Two Sorted List.javaJava
83Min Stack.javaJava
84Minimum Path Sum.javaJava
85Minimum Subarray.javaJava
86Minimum Window Substring.javaJava
87MinimumDepthOfBinaryTree.javaJava
88Next Permutation.javaJava
89NQueens.javaJava
90NQueensII.javaJava
91Nth to Last Node in List.javaJava
92Number of Islands.javaJava
93Number Triangles.javaJava
94O(1) Check Power of 2.javaJava
95Palindrome Partitioning II.javaJava
96Palindrome Partitioning.javaJava
97Partition Array by Odd and Even.javaJava
98Partition Array.javaJava
99Partition List.javaJava
100Permutations.javaJava
101Plus One.javaJava
102Product of Array Exclude Itself.javaJava
103Recover rotated array.javaJava
104Rehashing.javaJava
105Remove Duplicates from Sorted Array.javaJava
106Remove Duplicates from Sorted List II.javaJava
107Remove Duplicates from Sorted List.javaJava
108Remove Node in Binary Search Tree.javaJava
109Remove Nth Node From End of List.javaJava
110Reorder List.javaJava
111Reverse Integer.javaJava
112Reverse Linked List II .javaJava
113Reverse Words in a String.javaJava
114Rotate List.javaJava
115Rotate String.javaJava
116Search a 2D Matrix II.javaJava
117Search a 2D Matrix.javaJava
118Search for a Range.javaJava
119Search Insert Position.javaJava
120Search Range in Binary Search Tree .javaJava
121Search Rotated Sorted Array II.javaJava
122Search Rotated Sorted Array.javaJava
123Serilization and Deserialization Of Binary Tree.javaJava
124Single Number II.javaJava
125Single Number III.javaJava
126Single Number.javaJava
127Singleton.javaJava
128Sort Color.javaJava
129Sort Colors II.javaJava
130Sort Letters by Case.javaJava
131Sort List.javaJava
132Space Replacement.javaJava
133Sqrt(x).javaJava
134String to Integer(atoi).javaJava
135StrStr.javaJava
136Subarray Sum Closest.javaJava
137Subarray Sum.javaJava
138Subset.javaJava
139Subtree.javaJava
140Topological Sorting.javaJava
141Trailing Zeros.javaJava
142Two Lists Sum.javaJava
143Two Strings Are Anagrams.javaJava
144Ugly Number.javaJava
145Unique Binary Search Tree II.javaJava
146Unique Binary Search Tree.javaJava
147Unique Path.javaJava
148Unique Paths II.javaJava
149Unique Permutations.javaJava
150Unique Subset.javaJava
151Update Bits.javaJava
152Validate Binary Search Tree.javaJava
153Wood Cut.javaJava
154Word Break.javaJava
155Word Ladder II.javaJava
156Word Ladder.javaJava
157Word Search II.javaJava
158Word Search.javaJava
\ No newline at end of file +Java Solutions to algorithm problems from LintCode, LeetCode...etc. +
#Problem Language
01-bit and 2-bit Characters.javaJava
12 Sum II - Input array is sorted.javaJava
22 Sum II.javaJava
32 Sum.javaJava
43 Sum Closest.javaJava
53 Sum Smaller.javaJava
63 Sum.javaJava
74 Sum.javaJava
8A+B.javaJava
9Add and Search Word.javaJava
10Add Binary.javaJava
11Add Digits.javaJava
12Add Two Numbers II.javaJava
13Add Two Numbers.javaJava
14Alien Dictionary.javaJava
15Anagrams.javaJava
16Array Partition I.javaJava
17Backpack II.javaJava
18Backpack III.javaJava
19Backpack V.javaJava
20Backpack VI.javaJava
21Backpack.javaJava
22Balanced Binary Tree.javaJava
23Best Time to Buy and Sell Stock I.javaJava
24Best Time to Buy and Sell Stock II.javaJava
25Best Time to Buy and Sell Stock III .javaJava
26Best Time to Buy and Sell Stock IV.javaJava
27Best Time to Buy and Sell Stock with Cooldown.javaJava
28Binary Representation.javaJava
29Binary Search Tree Iterator.javaJava
30Binary Tree Inorder Traversal.javaJava
31Binary Tree Level Order Traversal II.javaJava
32Binary Tree Level Order Traversal.javaJava
33Binary Tree Longest Consecutive Sequence.javaJava
34Binary Tree Maximum Path Sum II.javaJava
35Binary Tree Maximum Path Sum.javaJava
36Binary Tree Path Sum.javaJava
37Binary Tree Paths.javaJava
38Binary Tree Postorder Traversal.javaJava
39Binary Tree Preorder Traversal.javaJava
40Binary Tree Right Side View.javaJava
41Binary Tree Serialization.javaJava
42Binary Tree Zigzag Level Order Traversal.javaJava
43Bomb Enemy.javaJava
44Building Outline.javaJava
45Burst Balloons.javaJava
46Change to Anagram.javaJava
47Classical Binary Search.javaJava
48Climbing Stairs.javaJava
49Clone Graph.javaJava
50Closest Binary Search Tree Value.javaJava
51Closest Number in Sorted Array.javaJava
52Coin Change.javaJava
53Coins in a Line III.javaJava
54Coins in a Line.javaJava
55ColorGrid.javaJava
56Combination Sum II.javaJava
57Combination Sum.javaJava
58Combinations.javaJava
59Compare Strings.javaJava
60Complete Binary Tree.javaJava
61Construct Binary Tree from Inorder and Postorder Traversal.javaJava
62Construct Binary Tree from Inorder and Preorder Traversal.javaJava
63Container With Most Water.javaJava
64Contains Duplicate II.javaJava
65Contains Duplicate III.javaJava
66Contains Duplicate.javaJava
67Convert Binary Search Tree to Doubly Linked List.javaJava
68Convert Expression to Polish Notation.javaJava
69Convert Expression to Reverse Polish Notation.javaJava
70Convert Integer A to Integer B.javaJava
71Convert Sorted Array to Binary Search Tree.javaJava
72Convert Sorted List to Binary Search Tree.javaJava
73Copy Books.javaJava
74Copy List with Random Pointer.javaJava
75Cosine Similarity.javaJava
76Count 1 in Binary.javaJava
77Count and Say.javaJava
78Count of Smaller Number before itself.javaJava
79Count of Smaller Number.javaJava
80Count Primes.javaJava
81Counting Bits.javaJava
82Course Schedule II.javaJava
83Course Schedule.javaJava
84Data Stream Median.javaJava
85Decode Ways.javaJava
86Delete Digits.javaJava
87Delete Node in the Middle of Singly Linked List.javaJava
88Distinct Subsequences.javaJava
89Edit Distance.javaJava
90Encode and Decode Strings.javaJava
91Encode and Decode TinyURL.javaJava
92ExcelSheetColumnNumber .javaJava
93Expression Evaluation.javaJava
94Expression Tree Build.javaJava
95Fast Power.javaJava
96Fibonacci.javaJava
97Find All Numbers Disappeared in an Array.javaJava
98Find Anagram Mappings.javaJava
99Find Minimum in Rotated Sorted Array II.javaJava
100Find Minimum in Rotated Sorted Array.javaJava
101Find Peak Element II.javaJava
102Find Peak Element.javaJava
103Find the Connected Component in the Undirected Graph.javaJava
104Find the Weak Connected Component in the Directed Graph.javaJava
105First Bad Version.javaJava
106First Missing Positive.javaJava
107First Unique Character in a String.javaJava
108Flatten 2D Vector.javaJava
109Flatten Binary Tree to Linked List.javaJava
110Flatten Nested List Iterator.javaJava
111Flip Game II.javaJava
112Flip Game.javaJava
113Fraction to Recurring Decimal.javaJava
114Game of Life.javaJava
115Gas Station.javaJava
116Generate Parentheses.javaJava
117Graph Valid Tree.javaJava
118Gray Code.javaJava
119Group Anagrams.javaJava
120Group Shifted Strings.javaJava
121Guess Number Higher or Lower.javaJava
122H-Index II.javaJava
123H-Index.javaJava
124Hamming Distance.javaJava
125Happy Number.javaJava
126Hash Function.javaJava
127HashHeap.javaJava
128HashWithArray.javaJava
129HashWithCustomizedClass(LinkedList).javaJava
130Heapify.javaJava
131Heaters.javaJava
132House Robber II.javaJava
133House Robber III.javaJava
134House Robber.javaJava
135Identical Binary Tree.javaJava
136Implement Queue by Two Stacks.javaJava
137Implement Queue using Stacks.javaJava
138Implement Stack by Two Queues.javaJava
139Implement Stack using Queues.javaJava
140Implement Stack.javaJava
141Implement strStr().javaJava
142Implement Trie (Prefix Tree).javaJava
143Implement Trie.javaJava
144IndexMatch.javaJava
145Inorder Successor in Binary Search Tree.javaJava
146Insert Interval.javaJava
147Insert Node in a Binary Search Tree .javaJava
148Insertion Sort List.javaJava
149Integer to English Words.javaJava
150Interleaving Positive and Negative Numbers.javaJava
151Interleaving String.javaJava
152Intersection of Two Arrays II.javaJava
153Intersection of Two Arrays.javaJava
154Intersection of Two Linked Lists.javaJava
155Interval Minimum Number.javaJava
156Interval Sum II.javaJava
157Interval Sum.javaJava
158Invert Binary Tree.javaJava
159Island Perimeter.javaJava
160Isomorphic Strings.javaJava
161Judge Route Circle.javaJava
162Jump Game II.javaJava
163Jump Game.javaJava
164k Sum.javaJava
165Kth Largest Element.javaJava
166Kth Smallest Element in a BST.javaJava
167Kth Smallest Number in Sorted Matrix.javaJava
168Kth Smallest Sum In Two Sorted Arrays.javaJava
169Largest Number At Least Twice of Others.javaJava
170Largest Number.javaJava
171Largest Rectangle in Histogram.javaJava
172Last Position of Target.javaJava
173Length of Last Word.javaJava
174Letter Combinations of a Phone Number.javaJava
175Linked List Cycle II.javaJava
176Linked List Cycle.javaJava
177Longest Common Prefix.javaJava
178Longest Common Subsequence.javaJava
179Longest Common Substring.javaJava
180Longest Consecutive Sequence.javaJava
181Longest Continuous Increasing Subsequence.javaJava
182Longest Increasing Continuous subsequence II.javaJava
183Longest Increasing Continuous subsequence.javaJava
184Longest Increasing Subsequence.javaJava
185Longest Palindromic Subsequence.javaJava
186Longest Palindromic Substring.javaJava
187Longest Substring with At Most K Distinct Characters.javaJava
188Longest Substring Without Repeating Characters.javaJava
189Longest Univalue Path.javaJava
190Longest Word in Dictionary.javaJava
191Longest Words.javaJava
192Lowest Common Ancestor II.javaJava
193Lowest Common Ancestor of a Binary Search Tree.javaJava
194Lowest Common Ancestor.javaJava
195LRU Cache.javaJava
196Majority Element.javaJava
197Majority Number II.javaJava
198Majority Number III.javaJava
199Matrix Zigzag Traversal.javaJava
200Max Area of Island.javaJava
201Max Consecutive Ones.javaJava
202Max Tree.javaJava
203Maximal Square.javaJava
204Maximum Average Subarray I.javaJava
205Maximum Average Subarray II.javaJava
206Maximum Depth of Binary Tree.javaJava
207Maximum Product Subarray.javaJava
208Maximum Subarray III.javaJava
209Maximum Subarray.javaJava
210Maximum SubarrayII.javaJava
211Maximum XOR of Two Numbers in an Array.javaJava
212MaximumSubarrayII.javaJava
213Median of two Sorted Arrays.javaJava
214Median.javaJava
215Meeting Rooms II.javaJava
216Meeting Rooms.javaJava
217Merge Intervals.javaJava
218Merge k Sorted Arrays.javaJava
219Merge k Sorted Lists.javaJava
220Merge Sorted Array II.javaJava
221Merge Sorted Array.javaJava
222Merge Two Binary Trees.javaJava
223Merge Two Sorted Lists.javaJava
224Middle of Linked List.javaJava
225Min Stack.javaJava
226Minimum Absolute Difference in BST.javaJava
227Minimum Height Trees.javaJava
228Minimum Path Sum.javaJava
229Minimum Size Subarray Sum.javaJava
230Minimum Subarray.javaJava
231Minimum Window Substring.javaJava
232MinimumDepthOfBinaryTree.javaJava
233Missing Ranges.javaJava
234Multiply Strings.javaJava
235Nested List Weight Sum.javaJava
236Next Permutation.javaJava
237Nim Game.javaJava
238Non-decreasing Array.javaJava
239NQueens.javaJava
240NQueensII.javaJava
241Nth to Last Node in List.javaJava
242Number of Airplane in the sky.javaJava
243Number of Islands II.javaJava
244Number of Islands.javaJava
245Number Triangles.javaJava
246O(1) Check Power of 2.javaJava
247One Edit Distance.javaJava
248Ones and Zeroes.javaJava
249Paint Fence.javaJava
250Paint House II.javaJava
251Paint House.javaJava
252Palindrome Linked List.javaJava
253Palindrome Partitioning II.javaJava
254Palindrome Partitioning.javaJava
255Palindrome Permutation II.javaJava
256Palindrome Permutation.javaJava
257Partition Array by Odd and Even.javaJava
258Partition Array.javaJava
259Partition List.javaJava
260Pascal's Triangle II.javaJava
261Path Sum.javaJava
262Peeking Iterator.javaJava
263Perfect Squares.javaJava
264Permutation Index.javaJava
265Permutation Sequence.javaJava
266Permutations II.javaJava
267Permutations.javaJava
268Plus One.javaJava
269Populating Next Right Pointers in Each Node II.javaJava
270Populating Next Right Pointers in Each Node.javaJava
271Pow(x,n).javaJava
272Power of Three.javaJava
273Power of Two.javaJava
274Product of Array Exclude Itself.javaJava
275Queue Reconstruction by Height.javaJava
276QuickSort.javaJava
277Recover Rotated Sorted Array.javaJava
278Regular Expression Matching.javaJava
279Rehashing.javaJava
280Remove Duplicates from Sorted Array.javaJava
281Remove Duplicates from Sorted List II.javaJava
282Remove Duplicates from Sorted List.javaJava
283Remove Duplicates from Unsorted List.javaJava
284Remove Linked List Elements.javaJava
285Remove Node in Binary Search Tree.javaJava
286Remove Nth Node From End of List.javaJava
287Reorder List.javaJava
288Reshape the Matrix.javaJava
289Restore IP Addresses.javaJava
290Reverse Integer.javaJava
291Reverse Linked List II .javaJava
292Reverse Linked List.javaJava
293Reverse String.javaJava
294Reverse Vowels of a String.javaJava
295Reverse Words in a String II.javaJava
296Reverse Words in a String.javaJava
297reverseInteger.javaJava
298Roman to Integer.javaJava
299Rotate Image.javaJava
300Rotate List.javaJava
301Rotate String.javaJava
302Russian Doll Envelopes.javaJava
303Same Tree.javaJava
304Scramble String.javaJava
305Search a 2D Matrix II.javaJava
306Search a 2D Matrix.javaJava
307Search for a Range.javaJava
308Search Insert Position.javaJava
309Search Range in Binary Search Tree .javaJava
310Search Rotated in Sorted Array II.javaJava
311Search Rotated in Sorted Array.javaJava
312Segment Tree Build II.javaJava
313Segment Tree Build.javaJava
314Segment Tree Modify.javaJava
315Segment Tree Query II.javaJava
316Segment Tree Query.javaJava
317Serilization and Deserialization Of Binary Tree.javaJava
318Shortest Word Distance.javaJava
319Single Number II.javaJava
320Single Number III.javaJava
321Single Number.javaJava
322Singleton.javaJava
323Sliding Window Maximum.javaJava
324Sliding Window Median.javaJava
325Sort Color.javaJava
326Sort Colors II.javaJava
327Sort Letters by Case.javaJava
328Sort List.javaJava
329Space Replacement.javaJava
330Sqrt(x).javaJava
331Stone Game.javaJava
332String Permutation.javaJava
333String to Integer(atoi).javaJava
334Strobogrammatic Number II.javaJava
335Strobogrammatic Number.javaJava
336StrStr.javaJava
337Subarray Sum Closest.javaJava
338Subarray Sum.javaJava
339Subset.javaJava
340Subsets II.javaJava
341Subtree.javaJava
342Summary Ranges.javaJava
343Surrounded Regions.javaJava
344Swap Bits.javaJava
345Swap Nodes in Pairs.javaJava
346Symmetric Binary Tree.javaJava
347The Smallest Difference.javaJava
348Toeplitz Matrix.javaJava
349Top K Frequent Elements.javaJava
350Top K Frequent Words.javaJava
351Topological Sorting.javaJava
352Total Occurrence of Target.javaJava
353Trailing Zeros.javaJava
354Trapping Rain Water II.javaJava
355Trapping Rain Water.javaJava
356Triangle Count.javaJava
357Trim a Binary Search Tree.javaJava
358Tweaked Identical Binary Tree.javaJava
359Two Lists Sum.javaJava
360Two Strings Are Anagrams.javaJava
361Ugly Number II.javaJava
362Ugly Number.javaJava
363Unique Binary Search Tree II.javaJava
364Unique Binary Search Tree.javaJava
365Unique Characters.javaJava
366Unique Path.javaJava
367Unique Paths II.javaJava
368Unique Word Abbreviation.javaJava
369Update Bits.javaJava
370Valid Anagram.javaJava
371Valid Palindrome.javaJava
372Valid Parentheses.javaJava
373Valid Perfect Square.javaJava
374Valid Sudoku.javaJava
375Validate Binary Search Tree.javaJava
376Wiggle Sort.javaJava
377Wildcard Matching.javaJava
378Wood Cut.javaJava
379Word Break II.javaJava
380Word Break.javaJava
381Word Ladder II.javaJava
382Word Ladder.javaJava
383Word Pattern.javaJava
384Word Search II.javaJava
385Word Search.javaJava
386Zigzag Iterator.javaJava
\ No newline at end of file diff --git a/fails.md b/fails.md new file mode 100644 index 0000000..d84239b --- /dev/null +++ b/fails.md @@ -0,0 +1,37 @@ +#### UnionFind.union, without checking rootA != rootB +- P: Number of Connected Components in an Undirected Graph +- when rootA == rootB, union exist already, no need to union again. + +#### Don't forget to map.put(i, new ArrayList<>()) +- P: Number of Connected Components in an Undirected Graph +- in DFS approach when initializing the adjacent list: use `map.put(x,y)`, not `add` + + +#### forgot to cast int to char +- P: wordladder +- `sb.setCharAt(i, (char) j + 'a');` +- Given input list: list.contains(), list.remove() are **O(logn)**!!! +- Make sure to convert it to HashSet() if need to use contains()/remove() + +#### Check input range for SegmentTree problem +- P: Segment Tree Query II +- Forgot to validate [start, end] range. +- Need to fail if completely out of range, or override one side of the range if partial of the range is applicable + +#### Jump too fast into 1 approach +- P: Max Points on a line +- Jump too fast into DP, when seeing a board, but ignored the fact: it's not nxn and not applicable. + +#### 正数/负数 +- P: Expression Add Operators +- dfs都对, 但是负数的地方, 忘记加符号: be consistent and mindful about negative number +- dfs(rst, list, s, i + 1, sum - currValue, **`- currValue`**, target); + +#### i++, i--, testing +- code快, 写错++, -- +- testing时候忽略了++,--, 那么其实就没有在真的test. + +#### Doubly Link BST +- 问清楚, 最后的 head 跟 tail也要link在一起 +- 在DFS中, 当dfs的结果出来以后(right or left linked list), 其实就可以利用结果的性质, 而不用再重新干做. +- `leftTail = leftHead.left`, `rightTail = rightHead.left` \ No newline at end of file diff --git a/knowledgeHash.json b/knowledgeHash.json old mode 100644 new mode 100755 index 2d358d4..41da2c5 --- a/knowledgeHash.json +++ b/knowledgeHash.json @@ -1,4 +1,4 @@ -//https://github.com/shawnfan/LintCode/blob/master/Java/ +//https://github.com/awangdev/LintCode/blob/master/Java/ /* //Format: "name" : { @@ -169,7 +169,13 @@ "which of these points are in a given interval" ], "problems" : [ - "", + "Segment Tree Query", + "Segment Tree Modify", + "Segment Tree Build", + "Interval Sum", + "Interval Minimum Number", + "Count of Smaller Number", + "Count of Smaller Number before itself", ] }, "Expression Tree" : { @@ -262,6 +268,24 @@ "", ] }, + "Scan Line" : { + "nickname": "扫描线", + "property" : [ + "把Interval分拆成Point{x,flag}.起点flag=1,终点flag=-1", + "用PriorityQueue可以sort这些点", + "维持一个count来判断同时又几个meeting在开,需要多少房间;一个人同时能参加所有meeting嘛,需要不重合", + "天上多少飞机同时在飞", + "根据count的情况merge interval, insert interval", + "在扫描时候,一定注意PriorityQueue里面在同一个point.x的很多点,可能同时发生,要全部处理完才能move on", + ], + "problems" : [ + "Meeting Rooms", + "Meeting Rooms II", + "Merge Intervals", + "Insert Interval", + "Planes in sky", + ] + }, "Math" : { "nickname": "数学", "property" : [ diff --git a/review/Array.md b/review/Array.md new file mode 100644 index 0000000..7093e1e --- /dev/null +++ b/review/Array.md @@ -0,0 +1,2259 @@ + + + +## Array (107) +**0. [Missing Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Ranges.java)** Level: Medium Tags: [Array] + + +#### Basic Implementation +- O(n) +- 两个pointer, 每次计较prev和curr之间的部分. +- 然后prev = curr,向前移动一格 +- TODO: check the edge case and make sure max/min of int are checked + + + +--- + +**1. [Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Permutation.java)** Level: Medium Tags: [Array] + + +需斟酌: why reverse is need? why we are looking for k? + +Permutation的规律: +1. 从小的数字开始变化因为都是从小的数字开始recursive遍历。 +2. 正因为1的规律,所以找大的断点数字要从末尾开始: 确保swap过后的permutation依然是 前缀固定时 当下最小的。 + +steps: +1. 找到最后一个上升点,k +2. 从后往前,找到第一个比k大的点, bigIndex +3. swap k && bigIndex +4. 最后反转 (k+1,end) + + + + +--- + +**2. [Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)** Level: Easy Tags: [Array, Two Pointers] + + +- 更正常的start/end partition pointer类似: when condition meet, swap +- Clean up TODO + + + +--- + +**3. [Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array] + + + + + +--- + +**4. [Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array] + + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**5. [Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)** Level: Medium Tags: [Array, Enumeration] + + +#### 找公式规律 +- 找到个转角度的规律公式: r = c; c = (w - r) +- 用temp variable, swap in place. + + + +--- + +**6. [Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +Allow duplicates之后: +因为最终binary search的结果也是O(n) +所以这道题要记得: 既然是O(n), 那来个简单的for loop 也就好了。 + +当然,要跟面试官提起来原因。别一上来就只有for。。。 + + +--- + +**7. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + + +--- + +**8. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + + +--- + +**9. [Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)** Level: Easy Tags: [Array, Math] + + +简单的实现, 加1, 进位. 唯一取巧的地方, 最后如果要多一位, 一定是10000...这个模式, 可以走捷径, 直接来个+1size的array, 然后第一位=1. +注意,转换成long也不合理,用太多memory. + + +--- + +**10. [Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)** Level: Medium Tags: [Array, Sort] + + +方法1: +排序, nLog(n). 然后把直线上坡变成层叠山峰, 需要每隔几个(题目中是每隔2位)就做个swap 造成高低不平. +Note: 每隔山峰之间是相互没有关系的, 所以每次只要操心 [i], [i-1]两个位置就好了. + +方法2: +O(n) +看好奇数偶数位的规律, 然后根据题目给出的规律, 跑一遍, 每次只关注两个位置: 把不合适的[i], [i-1]调换位置就好了. + +方法3: +跟法2一样, 只是更巧妙一点罢了: +第一遍想太多. 其实做一个fall-through就能把问题解决,原因是因为: +这样的fall-through每次在乎两个element,可以一口气搞定,无关乎再之前的elements。 +特别的一点:flag来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**11. [Two Sum II - Input array is sorted.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20II%20-%20Input%20array%20is%20sorted.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +升序array, 找2SUM. + +#### Two pointers +- 排序好的array. Two pointer移动start和end,核查sum. +- 注意sum用long. +- O(n) time + +#### Binary Search, 因为已经排好序了啊 +- 定住一个valueA, 然后在剩下的里面 binary serach 找 (target - valueB) +- for loop O(n), binary search O(logn) +- overall time: O(nLogN), 就不写了 + + + +--- + +**12. [2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +与 2sum II - input array is sorted类似. 都是sort array, 然后two pointer. + +LintCode的题. 注意找的是greater/bigger than target。 + +由于给定条件允许O(nLogn): + sort + two pointer + +while里面two pointer移动。每次如果num[left]+num[right] > target,那么其中所有num[left++]的加上num[right]都>target. +也就是,num[right]不动,计算加入挪动left能有多少组,那就是: right-left这么多。 全部加到count上去。 +然后right--.换个right去和前面的left部分作比较。 + + + +--- + +**13. [Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### DP +- 求最值, 想到DP. Time/Space O (n) +- 两个特别处: +- 1. 正负数情况, 需要用两个DP array. +- 2. continuous prodct 这个条件决定了在Math.min, Math.max的时候, +- 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. +- 这也就注定了需要一个global variable 来hold result. + +#### Space optimization, rolling array +- maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins +- Time: O(n), space: O(1) + + + +--- + +**14. [3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/3%20Sum%20Closest.java)** Level: Medium Tags: [Array, Two Pointers] + + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**15. [Triangle Count.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangle%20Count.java)** Level: Medium Tags: [Array] + + +其实也就是3sum的变形, 或者而说2sum的变形. 主要用2 pointers来做. +注意, 在选index时候每次定好一个 [0 ~ i], 在这里面找点start, end, 然后i 来组成triangle. +Note巧妙点: +在此之中, 如果一种start/end/i 符合, 那么从这个[start~end]中, 大于start的都可以, 所以我们count+= end-start. +反而言之, nums[i] + + + +--- + +**16. [3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum.java)** Level: Medium Tags: [Array, Two Pointers] + + + +#### sort array, for loop + two pointer. O(n^2) +- 处理duplicate wthin triplets: +- 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. +- 如果是triplet内有重复, 用完start point, 移动到结尾. + +Previous notes: +注意: + 1. 找 value triplets, 多个结果。注意,并非找index。 + 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 + +步骤: + 1. For loop 挑个数字A. + 2. 2Sum 出一堆2个数字的结果 + 3. Cross match 步骤1里面的A. + +时间 O(n^2), 两个nested loop + +另外, 还是可以用HashMap来做2Sum。稍微短点。还是要注意handle duplicates. + + + + +--- + +**17. [Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +跟unique path的grid一样, 目标走到右下角, 但是grid里面可能有obstacle, 不能跨越. 求unique path 的count. + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- 考虑最终结尾需要的状态:如何组成,写出公式. +- 公式中注意处理能跳掉的block, marked as 1. '到不了', 即为 0 path in dp[i][j]. + + + +--- + +**18. [3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum%20Smaller.java)** Level: Medium Tags: [Array, Two Pointers] + + +一般的O(n3)肯定不行。在此基础上优化。 +发现j,k满足条件时候,(k - j)就是所有 sum target, 又因为j不能后退,只能k--,那么问题就被锁定了. 这样可以做到O(n2) + + + +--- + +**19. [Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/Array%20Partition%20I.java)** Level: Easy Tags: [Array] + + +给串数字, size=2n, 找pairs, 然后需要sum of min(pair) 最大. + +(a1, b1), (a2, b2), ..., (an, bn) which makes sum of min(ai, bi) for all i from 1 to n as large as possible. + +#### Sort, basics +- 从结果出发, 只需要找到加法的结果,而不强调具体配对. +- 写一写example就能做 +- 找到排列取单数位的规律,再考虑负数和正数的相同规律,即可找到排列求解的方法。 +- sort, O(nlogn) + + + + +--- + +**20. [1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/1-bit%20and%202-bit%20Characters.java)** Level: Easy Tags: [Array] + + +方法1: +Greedy. +从第一个bit开始数: 如果遇到1, 一定是跳两位;如果遇到0, 一定是跳一位. +loop to end, and see if index reaches the end. + +方法2: +用DP硬做了一下: +1. 如果i位是0, 那么前面dp[i-1]或者dp[i-2] true就够了. +2. 如果i位是1, 那么i-1位必须是1才满足规则, 并且 dp[i-2]需要true. + + + +--- + +**21. [Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Non-decreasing%20Array.java)** Level: Easy Tags: [Array] + + +比较升序的时候, 必须要估计到 i-1, i, i+1三个数位. +写出来i-1, i+1之间的关系, 然后做合理的fix. + +需要真的fix数组, 因为loop through做比较时会用到fix后的数字. + + + +--- + +**22. [Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Consecutive%20Ones.java)** Level: Easy Tags: [Array] + + +Basic. Math.max track结果。 +记得在有对外操作的loop后,一定要把result object清理干净。 + + + +--- + +**23. [Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)** Level: Easy Tags: [Array] + + +方法1: +换到正确的位置。 +需要小心handle i, 因为不知道换到nums[i]上的是什么,所以必须原地清理干净 方能move on. + +方法2: +做标记! +很巧妙地运用了标记的方法, 标记成负数,证明visit过。 +Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! + +方法3: +做标记! +跟方法2类似,也是做标记,这一次是加上一个大于n的数(因为题目给了n的border),最后check一下就好。为不超Integer.MAX_VALUE, 每次加n前,取余数。 + +做标记的方法固然快,但是相对来说比较hacky,在常规的代码中,估计不会用到. + + + + +--- + +**24. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + +time: O(n) +space: O(1) + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + + + +--- + +**25. [Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number%20At%20Least%20Twice%20of%20Others.java)** Level: Easy Tags: [Array] + + +找最大值, 和第二大的值, 看是否符合题意, 就行了. +分析题意, 最简单方法, 可以loop 两遍: 找最值; 作比较. +但其实, 举个反例: 有一个不满足, 就够反对这个'at least twice of alllll others'. + + + +--- + +**26. [Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Toeplitz%20Matrix.java)** Level: Easy Tags: [Array] + + +似乎没什么算法特点, 就是array基本运算, 然后分割成一个helper function去做重复计算, 剪短代码. +注意check MxN 的分界线. + + + +--- + +**27. [Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)** Level: Medium Tags: [Array, Binary Search] + + +binary search. +Goal: find peak, where both sides are descending +最左边, 最右边是Integer.MIN_VALUE时候, 也能构成中间数mid是peak的条件. + +Note: +没有必要特别check (mid-1)<0或者(mid+1)>=n. +证明: +1. 最左端: 当start=0, end = 2 => mid = 1, mid-1 = 0; +2. 最右端: 当end = n - 1, start = n - 3; mid = (start+end)/2 = n - 2; +那么mid + 1 = n - 2 + 1 = n - 1 < n 是理所当然的 + + + +--- + +**28. [Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer] + + +#### Vote 计数 +- vote++, vote--到最后剩下的就是winner. Time O(n), Space O(1) +- Majority Number是指超半数. 超半数的数字, 最后都会至少有vote>=1: match current majority number,vote++;if not, vote--. +- 注意:assume valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 + +#### HashMap count occurance +- Time, Space: O(n) + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**29. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**30. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**31. [Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +画图, 最小值被rotate之后, 变成array中间的最低谷. +并且, 左边山坡的最小值, 大于右边山坡的最大值. +以此来给binary search做判断. + +O(nlogn) + + + +--- + +**32. [Find Minimum in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Hard Tags: [Array, Binary Search] + + +一个需要严谨思考的题目. 因为有duplicate, 会导致不断平移, 所以最终time complexity是O(n) +所以不如直接扫一遍, 给出答案. + +但是还是写一个Binary Search的样子, 只不过worst结果是O(n) + + + +--- + +**33. [Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)** Level: Medium Tags: [Array, Backtracking, DFS] + + +#### DFS, Backtracking: +- 找到开头的字母, 然后recursively DFS 去把word串到底: +- 每到一个字母, 朝四个方向走, 之中一个true就可以. +- Note:每次到一个字母,mark一下'#'. 4个path recurse回来后,mark it back. + +#### Note: other ways of marking visited: +- 用一个boolean visited[][] +- Use hash map, key = x@y + + + + +--- + +**34. [Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. +#### 方法1 +Array, 维持一个左手最高墙array, 右手最高强array. +对于每个index而言, vertically 能存放的最大水柱, 就是靠左右最高墙决定的: +min(leftHighestWall, rightHighestWall) - currHeight. + +#### 方法2 +方法1上面的优化, two pointer, 还是找左边最高和右边最高. O(1) space. +利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +左边按照maxLeft来计算, 右边按照maxRight来计算. + +#### 方法3 +2 Pointers, 双面夹击: +1. 找中间最高bar的index +2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条。 +3. 每次还要减去block自身的height + +#### 方法4 +主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. + + + + +--- + +**35. [Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)** Level: Hard Tags: [Array, Monotonous Stack, Stack] + + +给n个bar,组成柱状图histogram. 求在这一排柱状图里面可以找到的面积最大的长方形. + +思考: 找长方形面积, 无非是找两个index, 然后底边长度 * height. + +#### Monotonous Stack +- 重点是根据找Histogram里面rectangle的性质, 维持一个单调递增的Stack +- 在loop over indexes的时候: +- 如果高度>= previous peek(), 那么对于那个peek, 就意味着, 往下走, 一直走高嘛, 之前的peek总可以继续抄底 +- 什么时候不能抄底了呢? 就是有一个下降趋势的时候 +- 这时候并不是calculate所有前面的peek, 而是考虑 大于 current height的之前所有的peek. +- 把这些peek到 current height 前一格的rectangle全部找出来: stack.pop() +- 这个stack.pop()的过程里面, 其实没有算上 current height, 因为需要留到下一轮, 把current index加进stack 再说 +- 为什么用stack? 因为需要知道连续递增的peek, stack.peek() O(1), 好用 + 而其实不用stack, 也可以用其他方式记录所有height, 只不过要 O(n)去找peek不方便 + +#### 知识点 +- 理解monotonous stack 是如何被维护的 +- 维护monotonous stack 是题目需要, 而不是stack本身性质, 是一种借助 stack.peek() O(1)的巧妙用法. + + + + +--- + +**36. [Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +- 注意不要思维定式: 以为mid是index +- 这里mid其实是binary search on value [1, n] 的一个value. +- 再次用到validate() function + +Time: O(nLogN) + + + +--- + +**37. [Game of Life.java](https://github.com/awangdev/LintCode/blob/master/Java/Game%20of%20Life.java)** Level: Medium Tags: [Array] + + +#### basic +- 简单的implementation, 把count function写清楚就好. +- time: O(mn), extra space: O(mn) +- 注意结尾要一个个board[i][j]copy + +#### follow up +unlimited border? 可能需要分割board. 用大框分割, 每次换行的时候, 重复计算2行就好了. see details below. + +#### improvement: do it in place! +- time: O(mn), extra space: O(1) +- bit manipulation: 用第二个bit来存previous value. +- 因为我们只考虑 0 和 1 而已, 所以可以这样取巧. 但是并不scalable. +- 如果需要存multiple state, 可能需要移动更多位, 或者用一个 state map +- 注意 bit manipulation 的细节: <<1, >>1, 还有 mast的用法: |, & + + + + +--- + +**38. [Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)** Level: Review Tags: [Array, Binary Search, PreSum] + + +给int[] nums 和 window min size k. window size可以大于K. 找最大的连续数列average value. + +- Binary Search的思想, 用在所要找的这个 average sum 上面. 大小是在[min, max]之中 +- 找k的时候, 是>=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + + + +--- + +**39. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**40. [Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +##### DP +- 计数DP.注意方程式前两位置加在一起: 前两种情况没有overlap, 也不会缺情况. +- 注意initialization, 归1. +- 需要initialize的原因是,也是一个reminder: 在方程中会出现-1index +- Of course, row i = 0, or col j = 0, there is only 1 way to access +- time O(mn), space O(mn) + +##### 滚动数组 +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + + + + +--- + +**41. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**42. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**43. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**44. [Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate.java)** Level: Easy Tags: [Array, Hash Table] + + +无序数组, 找是否有重复element, return true/false. + +#### HashSet +- No brain: HashSet. +- Time O(n), Space O(n) + +#### Sort, Binary Search +- Arrays.sort(x): Time O(nLogN), Space O(1) +- 排序后, 重复数会排在一起, 然后 binary search + + + +--- + +**45. [Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20II.java)** Level: Easy Tags: [Array, Hash Table] + + +Unsorted array, 找出是否有duplicate elemenets: 必要条件是, 这两个element的index i,j 的大小最多相差k. + +#### HashSet +- 很巧妙地根据k range地条件, 把HashSet里面的值控制在[i - k, i] +- 每次不断地往set里面加新元素, 从set里减去末尾index的元素 +- 而set.add(x)如果遇到重复, 会return false. +- 一旦在这个length k 的 range里面, 有重复, 就符合条件. +- Time O(n) + +#### HashTable +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k +- Time O(nm), m = # of duplicates + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**46. [Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)** Level: Medium Tags: [Array, DP, Greedy] + + +给出步数,看能不能jump to end. + +#### Greedy - start from index = 0 +- Keep track of farest can go +- 一旦 farest >= nums.length - 1, 也就是到了头, 就可以停止, return true. +- 一旦 farest <= i, 也就是说, 在i点上, 已经走过了步数, 不能再往前跳, 于是 return false +- This can be done using DP. However, greedy algorithm is fast in this particular problem. + +#### Greedy - start from index = n - 1 +- greedy: start from end, and mark last index +- loop from i = [n - 2 -> 0], where i + nums[i] should always >= last index +- check if last == 0 when returning. It means: can we jump from index=0 to the end? +- Time: O(n), beat 100% + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (A[j] >= i - j), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- Time: O(n^2) + + + + +--- + +**47. [Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +找连续的持续上升子序列的长度. + +#### Coordinate DP +- 1D coordinate, dp 的角标, 就是代表 index i 的状态 +- 求最值, dp[i] = 在index i位置的最长子序列 +- 如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1 +- 如果没有持续上升, 那么dp[i] = 1, 重头来过 +- maintain max + +#### Basic +- 用一个数存current count, maintain max + + + +--- + +**48. [Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +#### DP +- Time, Space O(MN) +- 往右下角走, 计算最短的 path sum. 典型的坐标型. +- 注意: init 第一行的时候, 要accumulate dp[0][j - 1] + grid[i][j], 而不是单纯assign grid[i][j] + +#### Rolling Array +- Time O(MN), Space O(1) +- 需要在同一个for loop里面完成initialization, 和使用dp[i][j] +- 原因: dp[i % 2][j] 在被计算出来的时候, 是几乎马上在下一轮是要被用的; 被覆盖前不备用,就白算 +- 如果按照第一种方法, 在开始initialize dp, 看起来固然简单, 但是不方便空间优化 + + + +--- + +**49. [Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)** Level: Hard Tags: [Array, DP, Sequence DP] + + +比stock II 多了一个限制:只有2次卖出机会. + +#### DP加状态 +- 只卖2次, 把买卖分割成5个状态模块. +- 在状态index 0, 2, 4: 没有持有股票. 1. 一直在此状态, max profit不变; 2. 刚卖掉, dp[i][前状态] + profit +- 在状态index 1, 3: 持有股票. 1. 一直在此状态, daily profit. 2. 刚刚买进, 状态改变, 但是没有profit yet: dp[i][前状态] + +##### Partial profit +- 把每天的partial profit (diff)加在一起, 最终的overall profit是一样的. 唯一更好的是, 不需要记录中间买入的时间点. +- 什么时候会积累profit呢? +- 1. 原本就持有股票的, 如果毫无动作, 那么状态不变, 积累profit diff. +- 2. 卖出了股票, 状态改变, 积累profit diff. +- 注意: 只有在状态index: 0, 2, 4, 也就是卖掉股票的时候, 才可以积累profit + +##### Rolling Array +- [i] 只有和 [i-1] 打交道, reduce space +- O(1) space, O(n) time + +#### 找峰头 +- 找峰头;然后往下再找一个峰头。 +- 怎么样在才能Optimize两次巅峰呢?从两边同时开始找Max!(棒棒的想法) +- leftProfit是从左往右,每个i点上的最大Profit。 +- rightProfit是从i点开始到结尾,每个点上的最大profit. +- 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 +- 三个O(n),还是O(n) + + + +--- + +**50. [First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Missing%20Positive.java)** Level: Hard Tags: [Array] + + +给一串无序数字, 有负数: 找这个array里面第一个 missing的 positive integer + +missing positive integer 其实是以 [1, n] 来做比较的. + +#### Array分析, index 技巧 +- 用while loop, 不断地尝试把 number 送到该放的地方 +- 如果 index = nums[i] 超过了nums.length, 当然就不移动了 +- 注意: 检查 val != nums[val], avoid infinitely loop +- 检验: nums[i] 是否等于 i, 如果不对, 就找到了结果 + +#### Edge Case +- 如果nums==null, 其实missing positive integer 自然而然是 1 +- validation时, 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) +- 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +- 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + +**51. [Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + +O(n)跑2遍for. +O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. +特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 + + + +--- + +**52. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**53. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**54. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**55. [Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)** Level: Easy Tags: [Array, Quick Select, Quick Sort] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- quickSelect 可以找到 kth 最小的元素 +- 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- quick select 的template要熟悉一下, 一下子可能想得到, 但写不出来 +- 主要步骤: partition, dfs, only recur on one part of the array + + + + +--- + +**56. [Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)** Level: Easy Tags: [Array, Bit Manipulation, Math] + + +给一串unique数字, 数字取自 [0 ~ n], 无序, 找第一个skipped的数字. + +#### Swap +- 跟First Missing Positive 非常像, 只有一行代码的区别. +- swap 所有的数字, 到自己的correct position +- 最后一个for loop找到错位的index, 也就是缺的数字. + +#### Bit Manipulation +- XOR will only retain bits that are different 1 ^ 0 = 1, but 0^0, 1^1 == 0 +- Use that feature, 把所有value都和index XOR了 +- 剩下的多余的数字, 其实是那个index无法被XOR消掉, 也就是那个缺的number value. +- 注意: 题目告诉数字是 [0 ~ n], 然而缺一个数字, 那么在[0 ~ n - 1] 里面, 最大的数字(不管缺没缺), 一定是 n = nums.length. + +#### HastSet +- 全存, 找missing +- O(n) space, 不合题意 + +#### sorting +- sort, 找1st missing +- O(n log n) 太慢, 不合题意 + + + +--- + +**57. [Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- Two pointers 其实也可以是一个 for loop pointer, 另一个 dynamic variable. +- track unique index +- skip duplicated items +- O(n) + +#### 思考模式: +- Remove Duplicate from Array 不同于remove from linked list. +- LinkedList里面我们是最好不要动node.val的,直接把node去掉。 +- 而array我们很难直接把node去掉,又不能用新array,那么就要: +- 把不重复的element一个个放到最前面。 +- 这个思想跟merge two sorted array (其中一个后续非常长的array可以放下arr1,arr2) 类似。 +- 就是找个不会事后mess up,不会去动得index,把满足条件的element 填进去。这样保证了in place. +- *反向思维*:remove duplicate, 实际上也是找unique elements, and insert into original array + + + +--- + +**58. [Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- 跟 `Remove Duplicates from Sorted Array` 几乎一模一样, 只不过unique index现在可以 validate 2 位 +- 其余一模一样, use index to track unique item; skip if duplicated for more than 2 times +- O(n) time, O(1) space +- 这里也可以真的用2个pointers 写while loop, 但是没有必要, 只是单纯地走一个for loop其实就足够. + + + +--- + +**59. [Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)** Level: Medium Tags: [Array, Backpack DP, DP] + + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + +#### Backpack DP +- 计数问题, 可以想到DP. 其实就是Backpack VI. +- 从x个数字里面找candidate(可以重复用同一个数字), 来sum up to target. 找: # of ways to form the sequence. +- Backpack VI: 给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法 +- dp[i]: # of ways to build up to target i +- consider last step: 如果上一步取的是 candidate A, 那么就该加到dp[i]: +- dp[i] += dp[i - A] +- 要找overall dp[i], 就做一个for loop: dp[i] = sum{dp[i - num]}, where for (num: nums) +- Time: O(mn). m = size of nums, n = target +- If we optimize dp for loop, 需要Sort nums. O(mlogm). will efficient 如果m是constant或者relatively small. Overall: O(n) + +#### DFS, backtracking +- 尽管思考方式是对的, 但是 times out +- 可以重复使用数字的时候, 比如用1 来拼出 999, 这里用1就可以走999 dfs level, 不efficient + + + +--- + +**60. [Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)** Level: Hard Tags: [Array, Hash Table, Union Find] + + +给一串数字, unsorted, 找这串数字里面的连续元素序列长度 (consecutive序列, 是数字连续, 并不是说要按照原order) + +#### HashSet +- 要想看连续元素, 必须要num++, num--这样搜索 +- 1. 需要O(1)找到元素 +- 2. 需要简单快速找到 num - 1, num + 1. +- 如果用min,max开array, 耗费空间 +- 用HashSet来存, 用set.contains() 来查找 num - 1, num + 1 存在与否 +- for loop. O(n) +- 里面的while loop 一般不会有O(n); 一旦O(n), 也意味着set 清零, for loop也不会有更多 inner while 的衍生. +- overall O(n) 时间复杂度 + + +#### Union Find +- 最终是要把相连的元素算一下总长, 其实也就是把元素group起来, 相连的group在一起, 于是想到UnionFind +- 这里用到了一个`int[] size` 来帮助处理 `合并的时候parent是哪个`的问题: 永远往group大的union里去 +- main function 里面, 有一个map来track, 每个元素, 只处理1遍. +- union的内容: current number - 1, current number + 1 +- https://www.jianshu.com/p/e6b955ca208f + +##### 特点 +- Union Find 在index上做好像更加容易 +- 其他union find function: `boolean connected(a,b){return find(a) == find(b)}` + + + +--- + +**61. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**62. [Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Two Pointers +- 木桶理论。盛水的最高取决于最低的那面墙。 +- 左右两墙,往中间跑动。 +- 另:若一面墙已经小于另外一面,就要移动,换掉矮墙(可能下一面更高,或更低) +- 但决不能换掉当下的高墙,因为低墙已经limit的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**63. [Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element%20II.java)** Level: Medium Tags: [Array] + + +#### Sort + count +- O(nlogN) + +#### Two counters +- O(n), count and track valueA, valueB +- count overall apperance at the end for the two items +- save to result +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +--- + +**64. [Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)** Level: Medium Tags: [Array, Binary Search] + + +给2D matrix, 每行sorted, 每行的首位都大于上一行的末尾. goal: find target from matrix + +#### 2D matrix 转1D array +- 一行一行是从小到大, sorted, 连续的, 可以看做1D sorted array +- Binary Search + + + +--- + +**65. [Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)** Level: Medium Tags: [Array, Binary Search] + + +给sorted array, 有重复数字, 找跟target重合所在的range. + +#### Binary Search +- 2个while loop +- 找first/last occurance +- TODO: Can the code be simplified? + + + + +--- + +**66. [Merge Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array%20II.java)** Level: Easy Tags: [Array] + + +如题, merge two sorted array into 新的 sorted array + +- 长度已经固定. Basic Implementation +- 如果一个array足够大, merge into this array, 那么就是从末尾merge. + + + +--- + +**67. [Summary Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Summary%20Ranges.java)** Level: Medium Tags: [Array] + + +给一串sorted list, 中间有缺数字, return 所有数字的range string (example 看题目) + +#### Basic implementation +- 用一个list as the buffer to store candidates +- when: 1. end of nums; 2. not continuous integer => convert list to result + + + +--- + +**68. [Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)** Level: Medium Tags: [Array, Enumeration] + + +从(0,0)坐标, 走完spiral matrix, 把结果存在list里. + +#### DX, DY +- Basic implementation, array, enumeration +- 写一下position前进的方向: RIGHT->DOWN->LEFT->UP +- 用一个direction status 确定方向 +- 写一个compute direction function 改变方向 `(direction + 1) % 4` +- `boolean[][] visited` 来track走过的地方 + + + +--- + +**69. [Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort] + + +#### Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn) + + +#### Basic Implementation +- 这里已经给了 **sorted** intervals by start point. +- 直接找到可以insert newInterval的位子. Insert +- 然后loop to merge entire interval array +- 因为给的是个list, 所以方便`intervals.remove(i)` +- remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture +- O(n) + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**70. [Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum.java)** Level: Easy Tags: [Array, Hash Table] + + +#### HashMap +- 相对暴力简洁: 找到一个value, 存一个index +- 若在HashMap里面 match 到结果, 就return HashMap里存的index. +- O(n) space && time. + +#### Sort array, two pointer +- 前后++, --搜索. Sort 用时O(nlogn). +- 1. 第一步 two pointer 找 value. +- 2. 注意,要利用额外的空间保留original array, 用来时候找index. (此处不能用HashMap,因为以value 为key,但value可能重复) +- O(n) space, O(nlogn) time. + + + + +--- + +**71. [K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)** Level: Hard Tags: [Array, BST, TreeSet] + + +题目解析后: find 2 number, that: 1. k slots between the 2 number, 2. no slots taken between the two number. + +#### BST +- BST structure not given, use TreeSet to build BST with each node +- Every time find last/next inorder element +- `treeSet.lower(x)`, `treeSet.higher(x)` +- 一旦位置相隔(k + 1), 就满足题目条件 +- O(nlogn), good enough + +#### Track slots of days +- Reverse the array, save days index into days[], where the new index is slot. +- days[i]: at slot i, which day a flower will be planted +- O(n) +- Needs to understand: http://www.cnblogs.com/grandyang/p/8415880.html + + + +--- + +**72. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**73. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**74. [Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)** Level: Easy Tags: [Array, DFS] + + +#### DFS +- 虽然Easy, 但用到DFS最基本的想法. +- 1. dive deep +- 2. mark VISITED +- 3. sum it up +- Time: worst O(mn), traverse all possible nodes + +- 更要注意, 要从符合条件value==1的地方开始dfs. +- 对于什么island都没有的情况,area应该位0, 而不是Integer.MIN_VALUE, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**75. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + +**76. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**77. [Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)** Level: Medium Tags: [Array, Hash Table, PreSum] + + +给一个int[][] matrix, 找一个sub matrix, where the sum == 0. + +#### PreSum的思想 +- 算出一个右下角点(i,j)到(0,0)的大小: 上一块 + 左一块 + curr node - overlap area +- preSum[i][j]: sum from (0,0) to (i-1,j-1) +- same approach as `subarray sum`: use hashmap to store diff->index; if diff re-appears, that means sum of 0 has occurred +- sequence of calculation: 1. iterate over start row. 2. iterate over end row. 3. iterate over col number (this is where hashmap is stored based on) +- the iteration over col is like a screening: find previous sum and determine result +- Note: 其实并没有真的去找 `== 0` 的解答,而是根据特性来判断 `剩下的/后来加上的一定是0` + + + +--- + +**78. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**79. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**80. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + +**81. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**82. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**83. [Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Anagrams.java)** Level: Medium Tags: [Array, Hash Table] + + +把anagram找到并output + +#### HashMap +- 存在int[26], Arrays.toString(arr) 就是 string key: character frequency map +- anagram都有一样的key, 存进hashmap +- output anagrams + +#### HashMap + Sort +- HashMap 的做法. sort每个string, 存进HashMap, 重复的就是anagrams,最后输出。 +- toCharArray +- Arrays.sort +- Stirng.valueOf(char[]) +- 时间n*L*O(logL),L是最长string的长度。 + +#### Previous Notes +- Arrays.toString(arr)的做法。arr是int[26], assuming only have 26 lowercase letters. +- Count occurrance, 然后convert to String,作为map的key. +- Time complexity: nO(L) +- 另一种做法:http://www.jiuzhang.com/solutions/anagrams/ +- 1. take each string, count the occurrance of the 26 letters. save in int[]count. +- 2. hash the int[] count and output a unique hash value; hash = hash * a + num; a = a * b. +- 3. save to hashmap in the same way as we do. +- 这一步把for s: strs 里面的时间复杂度降到了O(L). L = s.length(). +- Need to work on the getHash() function. +- 时间变成n*O(L). Better. + + + + +--- + +**84. [Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Characters.java)** Level: Easy Tags: [Array, String] + + +determine if characters are unique in string + +#### HashSet +- space O(n), time O(n) + +#### char[] +- space O(n), time O(nlogn) + +#### no additional data structure +- double for loop: O(n^2) + + + + +--- + +**85. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**86. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**87. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**88. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**89. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + +**90. [Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + +time: O(n) +space: O(1) + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 看第二个解答, 进一步简化了代码 +- PreProduct, 跟preSum的感觉有点像, 就是差一位. + + + +--- + +**91. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**92. [Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array] + +time: O(n) +space: O(1) + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, track start/end +- start/end point, 然后比较大小记录dist +- 注意1: 如果第一个座位没有人, 特殊处理, dist = [0 ~ end] +- 注意2: 如果最后一个座位没有人, 特殊处理: dist = [n - 1 - start]; +- 其余: `dist = Math.max(dist, (end - start) / 2)` +- 相关题目: 几乎同样概念 `Binary Gap`, 升级复杂版`Exam Room` + + + + +--- + +**93. [Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### 理解意思是关键 +- 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出 +- 记录每天最小值Min是多少. O(n) +- 每天都算和当下的Min买卖,profit最大多少. + +#### DP +- Find min value for first i items, new dp[n + 1]. +- dp[i]: 前i天, prices最小的price是多少: min cost of first i days +- 然后用当天的price做减法dp[i]算max profit. +- Time, Space: O(n) +- 更进一步, 用一个min来表示min[i], 因为计算中只需要当下的min. + +#### Rolling array +- index i only depend on [i - 2] +- Space O(n) + +#### Brutle Failed +- 每天都试着买进,然后之后的每一天尝试卖出. double for loop, O(n^2). timeout. +- 其中很多都是没必要的计算:[7, 1, 5, 3, 6, 4]。 if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. + + + +--- + +**94. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**95. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**96. [Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Celebrity.java)** Level: Medium Tags: [Array, Greedy] + +time: O(n) +space: O(1) + +有n个人, 其中有个人是celebrity, 满足条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +#### Understand the property +- If brutly find celeb by comparing all possible pair: take complete O(n^2) handshakes. +- Instead, we can perform pruning, or like survival mode: +- 1. Assume a celeb = 0, and compare with all i = [1~ n-1] +- 2. If `celeb candidate know i, set celeb = i` as the next candidate (ex: prev canddiate invalid when he knows i) +- 3. For last standing celeb candidate: compare with all for validation +- Why performing the last run of validation? There could be someone dropped out before we execute `know(celeb, i)`. + +##### 思考逻辑 +- 先写出来[0 ~ n - 1], 最简单的方式 O(n^2) 检查, 记录每个人的状态. +- 逐渐发现, 因为 celeb 谁都不会认识, 那么当任何candidate knows anyone, 他自身就不是celeb. +- 我们可以greedy地, 一旦fail一个, 就立刻假设下一个是celeb candidate +- 最终还是要检查一遍, 避免错漏. +- 想一下happy case: 如果 celeb=0, 那么 know(celeb, i) 永远都是false, 然后 celeb一直保持0, 坚持到verify所有人. + + + +--- + +**97. [Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)** Level: Medium Tags: [Array, Math] + + +#### Array, Math +- 这个问题更在于问题本身的分析 (而且还有多余条件); 最终的for loop 也比较不standard. +- People younger than 15 cannot make requests due to the first rule. +- From the age of 15, people can make requests to the same age: a[i] * (a[i] - 1) requests. +- People can make requests to younger people older than 0.5 * i + 7: a[j] * a[i] requests. +- The third rule is redundant as the condition is already covered by the second rule. +- TODO: the approach. + + + +--- + +**98. [Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + +time: O(1) avg +space: O(n) + +#### Hash Table +- 用`map 来track value->index`, 用`list track index->value` +- map查看value是否存在 +- list maintain 用来 insert/remove/random operations. +- 特点: 一旦remove, 换到list结尾然后 `list.remove(list.size() - 1)`, 这样remove的cost更低. +- list.remove(object) 应该是要O(logn) 做一个search的. + + + +--- + +**99. [Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + +time: log(n) +space: O(1) + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 +- 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` +- `start mid`: start = mid; +- 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- `mid < target < end`: start = mid; +- `target < mid`: end = mid; + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**100. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**101. [My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)** Level: Medium Tags: [Array, TreeMap] + + +Given a list of interval as calendar items. Check if newly added calendar item is overlapping. + +Understand it is only checking time, but not requiring to insert into right spot. No need to overthink. + +#### Simply O(n) check on array +- number of test cases is small, like 1000, so less concern about the time complexity +- simply loop over the list of intervals, and check if any overlapping. +- where to insert does not really matter: every time we are just checking for overlaopping, not merging any range +- **IMPORTANT**: if interval over lapping, they will have this property `Math.max(s1, s2) < Math.min(e1, e2)`. This will help detect the overlapping very easily. +- O(n^2) runtime, with simple code. But somehow this approach is faster than the TreeMap solution: maybe the test cause causes avg O(n)? + +#### TreeMap +- One constraint from the simply array solution: it always cost O(n) to find the potential overlapping interval +- We can manually sort and always manually try to find the correct element via binary search, or we could store the interval in a treeMap, where the intervals are sorted by `start`. +- As result, all we need to do for book(start, end) is to find the next element ceiling(start), last element floor(start), and check for overlapping +- This approach also saves the custom data structure +- Overall cost O(nlogn) + +##### About TreeMap +- always with key sorted ascendingly +- more costly than regular HashMap because of the sorting. Building treemap of n items: O(nlogn) + +#### Sweep line +- use `Point{int start, end; boolean start}` to mark start/end of class. Add to pq. +- Adding new item to pq, sort, and check if overlapping occurs by counting started classes +- If started classes > 1, that means we overlapped. +- Every time it could consume all classes to find the overlap, O(n^2). +- Not quite need to sort or insert at correct point, and this solution requires longer code. Not quite worthy it for a simple problem. + + + + +--- + +**102. [Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素。 +- 注意,从尾部,是大数字优先排末尾的. + + + +--- + +**103. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**104. [Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Move%20Zeroes.java)** Level: Easy Tags: [Array, Two Pointers] + + +Move non-zero elements to front of array; preseve order. + +#### Two Pointers +- Outside pointer that moves in certain condition. +- Save appropirate elements + + + +--- + +**105. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + +**106. [The Spiral Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Spiral%20Matrix%20II.java)** Level: Medium Tags: [Array] + + +#### Move forward till end +- Similar concept as `The Maze`: keep walking until hit wall, turn back +- fix direction `dx[direction % 4]` + + + +--- + diff --git a/review/BFS.md b/review/BFS.md new file mode 100644 index 0000000..4d22904 --- /dev/null +++ b/review/BFS.md @@ -0,0 +1,937 @@ + + + +## BFS (39) +**0. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**1. [Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)** Level: Medium Tags: [BFS, Graph] + + +#### Graph + BFS +- Build graph `map` +- BFS to find the shortest path: when the neibhbor has the curr node as the only one neighbor, it is leaf. +- record shortest path in Map> as result +- TODO: code it up. + +#### Previous Solution +- removing leaf && edge + + + +--- + +**2. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**3. [Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS] + + +给一串integers, list里面可能有nest list. 算总的sum. 规则, 如果是nested list, 每深一个depth, sum要乘以depth. + +#### DFS +- New interface to understand: object contains integer or object +- Visit all && sum, consider dfs. +- bottom->up is easier: pick nested object and execute dfs, which returns sum of it, add with (level value * weight). +- 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) +- Note1: not multiplying on overall level sum. Only multiply level with single value at this level. +- Note2:top->bottom is not necessary: there is not need of passing added object into next level. + +#### BFS +- bfs, queue, 处理queue.size(). +- use a level variable to track levels + + + +--- + +**4. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**5. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + +**6. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**7. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**8. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**9. [Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +A complete binary tree is a binary tree in which every level, except possibly the last, + +is completely filled, and all nodes are as far left as possible + +#### BFS +- 当出现了第一次有 null children的node的时候, 说明到了leaf level, mark flag = true; +- 自此以后,queue再不该有node再有child; queue后面出现的node的left/right child应该都是null +- 否则就是有问题, return false; + + + + +--- + +**10. [Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### DFS +- 简单处理swap +- recursively swap children + +#### BFS +- BFS with Queue +- 每次process一个node, swap children; 然后把child加进queue里面 +- 直到queue process完 + + + +--- + +**11. [Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### BFS +- Shortest path; minimum depth: 想到BFS, check level by level, BFS更能确保更快找到结果 +- depth definition: reach to a leaf node, where node.left == null && node.right == null +- BFS using queue, track level. + + +#### DFS +- Divide and Conquery一个最小值. +- 注意处理Leaf的null: null leaf 出现的时候, 就忽略这个leaf, 直接return算有leaf +- 另一种count的方法: 用Integer.MAX_VALUE代替 null leaf,这样可以避免错误counting. (不能直接recursive) +- 这个无论如何都要走所有node, 所以dfs应该比较适合. + + + + +--- + +**12. [Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**13. [Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**14. [Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)** Level: Medium Tags: [BFS, Tree] + + +如题, 但是output要倒序. + +#### BFS +- 跟Binary Tree Level Order Traversal一样,只不过存result一直存在存在0位. + + +#### DFS +- 根据level来append每个list +- rst里面add(0,...)每次都add在list开头 + + + +--- + +**15. [Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右:即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list + +#### DFS +- Use Map 来存每一个level的结果 +- dfs function 里, 如果 input depth 不存在, 就add to map. +- dfs function 里面先: dfs(node.right), 然后 dfs(node.left) +- 由于always depth search on right side, 所以map会被right branch populate; 然后才是 leftChild.right + + + + +--- + +**16. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**17. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**18. [Word Ladder.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder.java)** Level: Medium Tags: [BFS] + + +给一串string[], 需要找shortest distance to change from wordA -> wordB. (限制条件细节见原题) + +#### BFS +- 通常, 给一个graph(这道题可以把beginWord看成一个graph的起始node), 找shortest path用BFS +- 在start string基础上,string的每个字母都遍历所有26个字母 +- visited 过的 从wordList里去掉 +- time: word length m, there can be n candidates => O(mn) +- 但是总是exceed time limit on LeetCode. However, it passes LintCode: +- 原因是 LeetCode给的是list, list.contains(), list.remove() 都是 O(logn) time!!! +- convert to set first. + +#### Trie +- timeout, overkill + + + +--- + +**19. [Find the Connected Component in the Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Connected%20Component%20in%20the%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS] + + +给一个undirected graph, return 所有的component. (这道题找不到了) + +#### BFS +- BFS遍历,把每个node的neighbor都加进来. +- 一定注意要把visit过的node Mark一下。因为curr node也会是别人的neighbor,会无限循环。 +- Component的定义:所有Component内的node必须被串联起来via path (反正这里是undirected, 只要链接上就好) +- 这道题:其实component在input里面都已经给好了,所有能一口气visit到的,全部加进queue里面,他们就是一个component里面的了。 +- 而我们这里不需要判断他们是不是Component + +#### DFS +- DFS 应该也可以 visit all nodes, mark visited. + + + +--- + +**20. [Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Topological Sort] + + +#### Topological Sort BFS +- indegree tracking: Track all neighbors/childrens. 把所有的children都存在 inDegree里面 +- Process with a queue: 先把所有的root加一遍(indegree == 0),可能多个root。并且全部加到queue里面。 +- BFS with Queue: +- Only when map.get(label) == 0, add into queue && rst. (indegree剪完了, 就是root啦) +- inDegree在这里就 count down indegree, 确保在后面出现的node, 一定最后process. + + +#### Basics about graph +- 几个graph的condition: +- 1. 可能有多个root +- 2. directed node, 可以direct backwards. + +TODO: +- build`Map inDegree = new HashMap<>();` and include the root itself +- that is more traditional indegree building + + + +--- + +**21. [Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)** Level: Review Tags: [BFS, DFS, DP] + + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- in dfs: remove the incorrect parentheses one at a time +- detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` +- once detected, remove the char from middle of s, and dfs on the rest of the s that has not been tested yet. + +##### Core concept: reverse test +- `if a parenthese string is valid, the reverse of it should also be valid` +- Test s with open='(', close=')' first; **reverse s**, and test it with open=')', close='(' + +##### Minor details +- only procceed to remove invalid parenthese when `count<0`, and also break && return dfs after the recursive calls. +- The above 2 facts eliminates all the redundant results. +- Reverse string before alternating open and close parentheses, so when returning final result, it will return the correct order. +- Open questions: how does it guarantee minimum removals? + +##### Backtracking +- 如果用stringbuffer, 那么久不会每次create new string, 但是需要maintain这个string buffer, 就会backtracking + +##### Complexity +- Seems to be O(n), but need to derive + +#### BFS +TODO + +#### DP + + + +--- + +**22. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**23. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**24. [Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + +time: O(n) +space: O(n) + +#### Queue +- 简单的level traversal.根据level奇数偶数而add到不同位子. +- Option1: based on level % 2, insert to front/end of list +- Option2: based on level, insert right/left of node into queue + + + +--- + +**25. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**26. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**27. [Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)** Level: Medium Tags: [BFS, DFS] + + +给一个room 2D grid. 里面有墙-1, 门0, 还有empty space INF(Math.MAX_VALUE). + +对每个empty space而言, fill it with dist to nearest gate. + +#### DFS +- Form empty room: it can reach different gate, but each shortest length will be determined by the 4 directions. +- Option1(NOT applicable). DFS on INF, mark visited, summerize results of 4 directions. +- hard to resue: we do not know the direction in cached result dist[i][j] +- Option2. DFS on gate, and each step taken to each direction will +1 on the spot: distance from one '0'; +- Through dfs from all zeros, update each spot with shorter dist +- Worst time: O(mn), where entre rooms[][] are gates. It takes O(mn) to complete the iteration. Other gates be skipped by `if (rooms[x][y] <= dist) return;` + +#### BFS +- Exact same concept. Init with `Queue queue = new LinkedList()` + + + +--- + +**28. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**29. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**30. [Shortest Distance from All Buildings.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Distance%20from%20All%20Buildings.java)** Level: Hard Tags: [BFS] + + +给Walls and Gates很像, 不同的是, 这道题要选一个 coordinate, having shortest sum distance to all buildings (marked as 1). + +#### BFS +- BFS 可以 mark shortest distance from bulding -> any possible spot. +- Try each building (marked as 1) -> BFS cover all 0. +- time: O(n^2) * # of building; use new visited[][] to mark visited for each building. +- O(n^2) find smallest point/aggregation value. +- 注意, 这道题我们update grid[][] sum up with shortest path value from building. +- 最后找个min value 就好了, 甚至不用return coordinate. +- 分析过, 还没有写. + + + +--- + +**31. [Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph] + + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes. +- 先能复制多少Node复制多少. 然后把neighbor 加上 +- Use `map` to mark visited + +#### DFS +- Given graph node obj `{val, list of neighbor}`: copy the node and all neighbors +- Mark visited using map +- for loop on the each one of the neighbors: map copy, record in map, and further dfs +- once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +- 主要思想是: 一旦复制过了, 不必要重新复制 + +#### BFS +- Copy the root node, then copy all the neighbors. +- Mark copied node in map. +- Use queue to contain the newly added neighbors. Need to work on them in the future. + + + +--- + +**32. [Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)** Level: Hard Tags: [BFS] + + + + +--- + +**33. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + + +--- + +**34. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**35. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**36. [The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)** Level: Medium Tags: [BFS, DFS] + + +#### BFS +- BFS on coordinates +- always attempt to move to end of border +- use boolean[][] visited to alingn with BFS solution in Maze II, III, where it uses Node[][] to store state on each item. + + + +--- + +**37. [The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)** Level: Medium Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- if already found a good/shorter route, skip +- `if (distMap[node.x][node.y] <= node.dist) continue;` +- This always terminates the possibility to go return to original route, because the dist will be double/higher + + + +--- + +**38. [The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)** Level: Hard Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- 跟 Maze I, II 类似, 用一个 Node[][] 来存每一个(x,y)的state. +- Different from traditional BFS(shortest path): `it terminates BFS when good solution exists (distance), but will finish all possible routes` +- 1. `Termination condition`: if we already have a good/better solution on nodeMap[x][y], no need to add a new one +- 2. Always cache the node if passed the test in step1 +- 3. Always offer the moved position as a new node to queue (as long as it fits condition) +- 4. Finally the item at nodeMap[target.x][target.y] will have the best solution. + + + +--- + diff --git a/review/BST.md b/review/BST.md new file mode 100644 index 0000000..0decb1c --- /dev/null +++ b/review/BST.md @@ -0,0 +1,472 @@ + + + +## BST (22) +**0. [Insert Node in a Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Node%20in%20a%20Binary%20Search%20Tree%20.java)** Level: Easy Tags: [BST] + + +往Binary Search Tree里面加东西,一定会找到一个合适的leaf加上去。 + +那么:就是说someNode.left or someNode.right是null时,就是insert node的地方。 + +找到那个someNode就按照正常的Binary Search Tree规律。 + + + +--- + +**1. [Minimum Absolute Difference in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Absolute%20Difference%20in%20BST.java)** Level: Easy Tags: [BST] + + +BST: inorder-traversal: 先left node(adding to stack till left leav), 再process stack.peek (mid node), 再 add rightNode && dive to rightNode.left leaf + + + +--- + +**2. [Remove Node in Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Node%20in%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST] + + +方法1: Brutle一点。找到target和target的parent. +把target remove时,把target的children nodes 重新排列组成新的BST: inorder traversal, build tree based on inorder traversal list. + +方法2: 分析规律,先找到target和parent, 然后根据性质,把target remove时,移动children nodes, 保证还是BST。 + + + +--- + +**3. [Zigzag Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Zigzag%20Iterator.java)** Level: Medium Tags: [BST] + + +这个题目相对简单. 做的时候我先考虑起来k条怎么办. 那么用个map把index和每个listmark一下就好了。 +每次next(), 相应的list的头拿下来就好。 +然后就跑圈呗,每次刷一个list头。不难。只要把几个variable维护清楚就行。 + + +--- + +**4. [Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DP, Tree] + + +Not quite clear. +根据左右分割而总结出了原理, 每次分割, 左右两边都会有一定数量的permutation, 总体上的情况数量当然是相乘. +然后每一个不同的分割点都加一遍: +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +然后把数学公式转换成DP的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**5. [Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, Tree] + + +方法1: +适合复习BST. 用DFS对待每个node. 注意BST的特征: 所有left nodes都小于当下node, 所有right nodes都大于当下node. + +根据题意用[L,R]切割.如果node.val>>>> (-t <= A - B <= t) =>>>>>> A >= B - t, A <= B + t +- 也就是说, 如果对于 B = nums[i], 来说, 能找到一个target A, 满足上面的公式, 那么就可以 return true. +- Time O(nLogk), treeSet的大小不会超过k, 而 treeSet.ceiling(), treeSet.add(), treeSet.remove() 都是 O(logK) +- Space O(k) + +#### Note +- 与Contains Duplicate II 类似概念. TreeSet有BST 因此可以直接用, 而不用自己构建BST +- 简化题目里面的重要条件 Math.abs(A-B) <= t 而推断出 A >= B - t, A <= B + t +- 并且需要需要用 TreeSet.ceiling(x): return number greater or equal to x. 这个用法要记住吧, 没别的捷径. + + + +--- + +**11. [Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的list +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Brutly寻找p和q的common ancestor, 然后recursively drive left/right +- 非常巧妙, 但是也比较局限; 稍微变条件, 就很难recursive. +- 几种情况: +- 1. one of p, q 在leaf, 那么此时的root其实就是lowest common ancestor +- 2. 如果p, q 在root的左右两边, 这就是分叉口, 那么root就是lowest common ancestor +- 3. 如果p,q 在root的同一边 (左,右), 那么继续dfs +- O(1) extra space, O(logn) time + + + +--- + +**12. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**13. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**14. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**15. [Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)** Level: Medium Tags: [BST, Binary Tree] + + +给一个BST, integer range (k1, k2), 找range 里面所有的integer. + +#### BST +- 等于dfs遍历了所有k1<= x <= k2的x node。 +- dfs left, process root, then dfs right +- 这里, 把 left/right/match的情况全部cover了,然后把k1,k2的边框限制好,中间就全部遍历了。 + + + +--- + +**16. [K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)** Level: Hard Tags: [Array, BST, TreeSet] + + +题目解析后: find 2 number, that: 1. k slots between the 2 number, 2. no slots taken between the two number. + +#### BST +- BST structure not given, use TreeSet to build BST with each node +- Every time find last/next inorder element +- `treeSet.lower(x)`, `treeSet.higher(x)` +- 一旦位置相隔(k + 1), 就满足题目条件 +- O(nlogn), good enough + +#### Track slots of days +- Reverse the array, save days index into days[], where the new index is slot. +- days[i]: at slot i, which day a flower will be planted +- O(n) +- Needs to understand: http://www.cnblogs.com/grandyang/p/8415880.html + + + +--- + +**17. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**18. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**19. [Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST, DFS, Tree] + + +BST里面有2个node misplace, 要归为. 要求: O(1) extra space + +#### Observation +- BST inorder traversal should give small -> large sequence +- misplaced means: a **large**->small item would occur, and later a large>**small** would occur. +- The first large && second small item are the 2 candidates. Example +- [1, 5, 7, 10, 12, 15, 18] +- [1, 5, `15, 10`, `12, 7`, 18] + +#### dfs, O(1) extra space +- traverse, and take note of the candidate +- at the end, swap value of the 2 candidates + +#### O(n) space +- inorder traversal the nodes and save in array, find the 2 items misplanced and swap them +- But O(n) space should not be allowed + + + + +--- + +**20. [Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)** Level: Medium Tags: [BST, Tree] + + +找 Inorder traversal规则里的下一个. + +主要想法是考虑: + 1. 如果 node.right == null, 找上一个unprocessed node alone the inorder traversal path + 2. 如果 node.right != null, successor 一定在这个node.right那个subtree里面 +最后竟然可以简化成几行, 非常全面的BST问题: 有search, 有对inorder traversal的理解, 还有坑. + +#### Short Recursive and Iterative without Stack +- Previous solution, we use stack to hold previous cached/unprocessed items: but do we need use catch to hold them? +- If moving left: `p.val < root.val`, then root (parent of left child) is a successor candidate, so save `rst = root`. +- If moving right or equal: `p.val >= root.val`, the successor has nothing to do with curr node, so just directly dive into root.right. +- Both iterative and recursive solution can be simplified as such. + + +#### Previous Iterative + stack +- Iteratively search +- Still need stack to store previously unprocessed items along the path + +#### Previous Recursive + Stack +- 画inorder图,发现规律.每个node的后继node(successor)有几种情况: +- 1. node.right 是个leaf到底了。那么就return. +- 2. set rightNode = node.right, 但发现rightNode has a lot left children to leaf. +- 3. 比如, node.right == null, 也就是node自己是leaf,要回头看山顶找Inorder traversal规则里的下一个。 +- 发现:其实就是每层都把路过的curr node放在stack里,最上面的,就是当下改return的那个successor:) Done. + + + +--- + +**21. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + diff --git a/review/Backpack DP.md b/review/Backpack DP.md new file mode 100644 index 0000000..9f9788b --- /dev/null +++ b/review/Backpack DP.md @@ -0,0 +1,234 @@ + + + +## Backpack DP (8) +**0. [Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + +#### DP +- 找对方程dp[x], 积累到amount x最少用多少个coin: #coin是value, index是 [0~x]. +- 子问题的关系是: 如果用了一个coin, 那么就应该是f[x - coinValue]那个位置的#coins + 1 + +##### initialization +- 处理边界, 一开始0index的时候, 用value0. +- 中间利用Integer.MAX_VALUE来作比较, initialize dp[x] +- 注意, 一旦 Integer.MAX_VALUE + 1 就会变成负数. 这种情况会在coin=0的时候发生. + +##### Optimization +- 方法1: 直接用Integer.MAX_VALUE +- 方法2: 用-1, 稍微简洁一点, 每次比较dp[i]和 dp[i - coin] + 1, 然后save. 不必要做多次min比较. + +#### Memoization +- dp[i] 依然表示: min # of coints to make amount i +- initialize dp[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录dp[amount] 如果已经给过value, 不要重复计算, 直接return. +- 但是这道题没必要强行做memoization, 普通DP的状态和方程相对来说很好找到 + + + +--- + +**1. [Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)** Level: Medium Tags: [Backpack DP, DP] + + +给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法. + +nums 里的数字, 可以重复使用. 不同的order可以算作不同的拼法. + +#### Backpack DP +- dp[i] 表示: # of ways to fill weight i +- 1维: dp[w]: fill weigth w 有多少种方法. 前面有多少种可能性, 就sum多少个: +- dp[w] = sum{dp[w - nums[i]]}, i = 0~n + +##### 分析 +- 拼背包时, 可以有重复item, 所以考虑'最后被放入的哪个unique item' 就没有意义了. +- 背包问题, 永远和weight分不开关系. +- 这里很像coin chagne: 考虑最后被放入的东西的value/weigth, 而不考虑是哪个. + + + + + + +--- + +**2. [Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change%202.java)** Level: Medium Tags: [Backpack DP, DP] + + +给串数字, target amount, 求总共多少种方式可以reach the amount. + +#### DP +- O(MN): M, total target amount; N: size of coins +- 类似于: 网格dp, unique path 里面的2种走法: 从上到下, 从左到右 +- 状态: dp[i]: sum of ways that coins can add up to i. +- Function: dp[j] += dp[j - coins[i]]; +- Init: dp[0] = 1 for ease of calculation; other dp[i] = 0 by default +- note: 避免重复count, 所以 j = coins[i] as start +- 注意 coins 需要放在for loop 外面, 主导换coin的流程, 每个coin可以用无数次, 所以在每一个sum value上都尝试用一次每个coin + +#### knapsack problem: backpack problem + + + +--- + +**3. [Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 背包有自己的大小M, 看最多能放多少重量的书? + +#### Backpack DP 1 +- 简单直白的思考 dp[i][m]: 前i本书, 背包大小为M的时候, 最多能装多种的书? +- **注意**: 背包问题, 重量weight一定要是一维. +- dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + A[i - 1]); +- 每一步都track 最大值 +- 最后return dp[n][m] +- 时间空间 O(mn) +- Rolling array, 空间O(m) + +#### Backpack DP 2 +- true/false求解, 稍微曲线救国: 重点是, 最后, 按照weight从大到小遍历, 第一个遇到true的, index就是最大值. +- 考虑: 用i个item (可跳过地取), 是否能装到weight w? +- 需要从'可能性'的角度考虑, 不要搞成单一的最大值问题. +- 1. 背包可装的物品大小和总承重有关. +- 2. 不要去找dp[i]前i个物品的最大总重, 找的不是这个. + dp[i]及时找到可放的最大sum, 但是i+1可能有更好的值, 把dp[i+1]变得更大更合适. + +##### 做法 +- boolean[][] dp[i][j]表示: 有前i个item, 用他们可否组成size为j的背包? true/false. +- (反过来考虑了,不是想是否超过size j, 而是考虑是否能拼出exact size == j) +- **注意**: 虽然dp里面一直存在i的位置, 实际上考虑的是在i位置的时候, 看前i-1个item. + +##### 多项式规律 +- 1. picked A[i-1]: 就是A[i-1]被用过, weight j 应该减去A[i-1]. 那么dp[i][j]就取决于dp[i-1][j-A[i-1]]的结果. +- 2. did not pick A[i-1]: 那就是说, 没用过A[i-1], 那么dp[i][j]就取决于上一行d[i-1][j] +- dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]] + +##### 结尾 +- 跑一遍dp 最下面一个row. 从末尾开始找, 最末尾的一个j (能让dp[i][j] == true)的, 就是最多能装的大小 :) +- 时间,空间都是:O(mn) + + + + +--- + +**4. [Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 每本书有value int[] V + +背包有自己的大小M, 看最多能放多少value的书? + +#### Backpack DP +- 做了Backpack I, 这个就如出一辙, 只不过: dp存的不是max weight, 而是 value的最大值. +- 想法还是,选了A[i - 1] 或者没选A[i - 1]时候不同的value值. +- 时间空间O(mn) +- Rolling Array, 空间O(m) + +#### Previous DP Solution +- 如果无法达到的w, 应该mark as impossible. 一种简单做法是mark as -1 in dp. +- 如果有负数value, 就不能这样, 而是要开一个can[i][w]数组, 也就是backpack I 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**5. [Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)** Level: Medium Tags: [Backpack DP, DP] + + +#### Backpack DP +- 与背包1不同: 这里不是check可能性(OR)或者最多能装的size是多少; 而是计算有多少种正好fill的可能性. +- dp[i][w]: 用前i本书, 正好fill到 w weight的可能性. +- 对于末尾, 还是两种情况: +- 1. i-1位置没有加bag +- 2. i-1位置加了bag +- 两种情况可以fill满w的情况加起来, 就是我们要的结果. +- 如常: dp[n + 1][w + 1] +- 重点: dp[0][0] 表示0本书装满weight=0的包, 这里我们必须 dp[0][0] = 1, 给后面的 dp function 做base +- Space, time: O(MN) +- Rolling array, 空间优化, 滚动数组. Space: O(M) + +#### 降维打击, 终极优化 +- 分析row(i-1)的规律, 发现所有row(i)的值, 都跟row(i-1)的左边element相关, 而右边element是没用的. +- 所以可以被override. +- Space: O(M), 真*一维啊! +- Time: O(MN) + + + +--- + +**6. [Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)** Level: Hard Tags: [Backpack DP, DP] + + +给n种不同的物品, int[] A weight, int[] V value, 每种物品可以用无限次 + +问最大多少value可以装进size是 m 的包? + +#### DP +- 可以无限使用物品, 就失去了last i, last unique item的意义: 因为可以重复使用. +- 所以可以转换一个角度: +- 1. 用i **种** 物品, 拼出w, 并且满足题目条件(max value). 这里因为item i可以无限次使用, 所以考虑使用了多少次K. +- 2. K虽然可以无限, 但是也被 k*A[i]所限制: 最大不能超过背包大小. +- dp[i][w]: 前i种物品, fill weight w 的背包, 最大价值是多少. +- dp[i][w] = max {dp[i - 1][w - k*A[i-1]] + kV[i-1]}, k >= 0 +- Time O(nmk) +- 如果k = 0 或者 1, 其实就是 Backpack II: 拿或者不拿 + +#### 优化 +- 优化时间复杂度, 画图发现: +- 所计算的 (dp[i - 1][j - k*A[i - 1]] + k * V[i - 1]) +- 其实跟同一行的 dp[i][j-A[i-1]] 那个格子, 就多出了 V[i-1] +- 所以没必要每次都 loop over k times +- 简化: dp[i][j] 其中一个可能就是: dp[i][j - A[i - 1]] + V[i - 1] +- Time O(mn) + +#### 空间优化到1维数组 +- 根据上一个优化的情况, 画出 2 rows 网格 +- 发现 dp[i][j] 取决于: 1. dp[i - 1][j], 2. dp[i][j - A[i - 1]] +- 其中: dp[i - 1][j] 是上一轮 (i-1) 的结算结果, 一定是已经算好, ready to be used 的 +- 然而, 当我们 i++,j++ 之后, 在之前 row = i - 1, col < j的格子, 全部不需要. +- 降维简化: 只需要留着 weigth 这个 dimension, 而i这个dimension 可以省略: +- (i - 1) row 不过是需要用到之前算出的旧value: 每一轮, j = [0 ~ m], 那么dp[j]本身就有记录旧值的功能. +- 变成1个一位数组 +- 降维优化的重点: 看双行的左右计算方向 +- Time(mn). Space(m) + + + +--- + +**7. [Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)** Level: Medium Tags: [Array, Backpack DP, DP] + + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + +#### Backpack DP +- 计数问题, 可以想到DP. 其实就是Backpack VI. +- 从x个数字里面找candidate(可以重复用同一个数字), 来sum up to target. 找: # of ways to form the sequence. +- Backpack VI: 给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法 +- dp[i]: # of ways to build up to target i +- consider last step: 如果上一步取的是 candidate A, 那么就该加到dp[i]: +- dp[i] += dp[i - A] +- 要找overall dp[i], 就做一个for loop: dp[i] = sum{dp[i - num]}, where for (num: nums) +- Time: O(mn). m = size of nums, n = target +- If we optimize dp for loop, 需要Sort nums. O(mlogm). will efficient 如果m是constant或者relatively small. Overall: O(n) + +#### DFS, backtracking +- 尽管思考方式是对的, 但是 times out +- 可以重复使用数字的时候, 比如用1 来拼出 999, 这里用1就可以走999 dfs level, 不efficient + + + +--- + diff --git a/review/Backtracking.md b/review/Backtracking.md new file mode 100644 index 0000000..f69b28f --- /dev/null +++ b/review/Backtracking.md @@ -0,0 +1,845 @@ + + + +## Backtracking (32) +**0. [Gray Code.java](https://github.com/awangdev/LintCode/blob/master/Java/Gray%20Code.java)** Level: Medium Tags: [Backtracking] + + +TODO: +1. backtracking, using set to perform contains() +2. BFS: use queue to keep the mutations + +题目蛋疼,目前只接受一种结果。 + +BackTracking + DFS: + Recursive helper里每次flip一个 自己/左边/右边. Flip过后还要恢复原样.遍历所有. + +曾用法(未仔细验证): +基本想法就是从一个点开始往一个方向走,每次flip一个bit, 碰壁的时候就回头走。 + + + +--- + +**1. [Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)** Level: Medium Tags: [Backtracking, Permutation] + + +TODO: need to review permutation + +permutation的综合题: +1. validate Input 是不是可以做palindromic permutation. 这个就是(Palindrome Permutation I) +2. 顺便存一下permutation string的前半部分和中间的single character(if any) +3. DFS 做unique permutation: given input有duplicate characters。 + + + +--- + +**2. [Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)** Level: Medium Tags: [Backtracking, Math] + + +TODO: what about regular DFS/backtracking to compute the kth? dfs(rst, list, candiate list, k) + +k是permutation的一个数位。而permutation是有规律的。 + +也就是说,可以根据k的大小来判断每一个数位的字符(从最大数位开始,因为默认factorio从最大数位开始变化)。 + +于是先求出n!, 然后 k/n!就可以推算出当下这一个数位的字符。然后分别把factorio 和 k减小。 + +另外, 用一个boolean[] visited来确保每个数字只出现一次。 + +这个方法比计算出每个permutation要efficient许多。 + + + + +--- + +**3. [Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)** Level: Medium Tags: [Backtracking, String] + + +方法1: Iterative with BFS using queue. + +方法2: Recursively adding chars per digit + + + +--- + +**4. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**5. [Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)** Level: Easy Tags: [Backtracking, Binary Tree, DFS] + + +给一个binary tree, 返回所有root-to-leaf path + +#### DFS, backtracking +- Find all paths, bfs/dfs all works. dfs will be simplier to write +- Recursive:分叉. dfs. +- top->bottom: enumerate current node into the list, carry to next level, and backtrack +- top->bottom is trivial to consider: path flows from top->bottom + +#### DFS, bottom->up +- We can also take current node.left or node.right to generate list of results from the subproblem +- let dfs return list of string candidates, and we can run pair the list with currenet node, once they come back. +- TODO: can write code to practice + +#### Iterative +- Iterative, 非递归练习了一下 +- 因为要每次切短list, 所以再加了一个Stack 来存level +- 单这道题用dfs更简单, 因为找的就是从头到尾的path, 是dfs的pattern + + + + +--- + +**6. [Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +Trie结构, prefix tree的变形: '.'可以代替任何字符,那么就要iterate这个node所有的children. + +节点里面有char, isEnd, HashMap +Build trie = Insert word:没node就加,有node就移动。 +Search word:没有node就报错. 到结尾return true + +这题因为'.'可以代替任何possible的字符,没一种都是一个新的path,所以recursive做比较好些。 +(iterative就要queue了,麻烦点) + + + +--- + +**7. [Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)** Level: Hard Tags: [Backtracking, DFS, Trie] + + +给一串words, 还有一个2D character matrix. 找到所有可以形成的words. 条件: 2D matrix 只可以相邻走位. + +#### Trie, DFS +- 相比之前的implementation, 有一些地方可以优化: +- 1. Backtracking时候, 在board[][] 上面mark就可以, 不需要开一个visited[][] +- 2. 不需要implement trie的所有方程, 用不到: 这里只需要insert. +- 普通的trie题目会让你search a word, 但是这里是用一个board, 看board的每一个字母能不能走出个Word. +- 也就是: 这里的search是自己手动写, 不是传统的trie search() funcombination +- 3. TrieNode里面存在 end的时候存string word, 表示到底. 用完了 word = null, 刚好截断重复查找的问题. + +##### 关于Trie +- Build Trie with target words: insert, search, startWith. Sometimes, just: `buildTree(words)` and return root. +- 依然要对board matrix做DFS。 +- no for loop on words. 直接对board DFS: +- 每一层,都会有个up-to-this-point的string. 在Trie里面check它是不是存在。以此判断。 +- 若不存在,就不必继续DFS下去了。 +- Trie solution time complexity, much better: +- build Trie: n * wordMaxLength +- search: boardWidth * boardHeight * (4^wordMaxLength + wordMaxLength[Trie Search]) + + +#### Regular DFS +- for loop on words: inside, do board DFS based on each word. +- Time cpmplexity: word[].length * boardWidth * boardHeight * (4^wordMaxLength) + +#### Previous Notes +- Big improvement: use boolean visited on TrieNode! +- 不要用rst.contains(...), 因为这个是O(n) 在leetcode还是会timeout(lintcode竟然可以pass)! +- 在Trie search() method 里面,凡是visit过的,mark一下。 + + + + +--- + +**8. [Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)** Level: Medium Tags: [Array, Backtracking, DFS] + + +#### DFS, Backtracking: +- 找到开头的字母, 然后recursively DFS 去把word串到底: +- 每到一个字母, 朝四个方向走, 之中一个true就可以. +- Note:每次到一个字母,mark一下'#'. 4个path recurse回来后,mark it back. + +#### Note: other ways of marking visited: +- 用一个boolean visited[][] +- Use hash map, key = x@y + + + + +--- + +**9. [Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)** Level: Hard Tags: [Backtracking, Trie] + + +可以开Trie class, 里面用到TrieNode. 开Trie(words) 可以直接initalize with for loop +TrieNode 里面可以有一个 List startWith: 记录可以到达这个点的所有string: 有点像树形, ancestor形状的存储. + +神操作: +根据square的性质, 如果选中了list of words, 设定 int prefixIndex = list.size(). +取出list里面的所有word[prefixedIndex], 并且加在一起, 就是下一个word candidate的 prefix. + +形象一点: +list = ["ball", "area"]; +prefixIndex = list.size(); ball[prefixIndex] = 'l'; area[prefixIndex] = 'e'; +//then +candidatePrefix = ball[prefixIndex] + area[prefixIndex] = "le"; +这里就可以用到Trie的那个 findByPrefix function, 在每个点, 都存有所有这个点能产生的candidate. +这时, 试一试所有candidate: dfs + +能想到这种倒转的结构来存prefix candidates 在 Trie里面, 这个想法非常值得思考. + + + +--- + +**10. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**11. [Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations%20II.java)** Level: Medium Tags: [Backtracking] + + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +#### Backtracking +- 排序, +- Mark visited. 通过permutation规律查看是否排出了重复结果 +- 并且要检查上一层recursive时有没有略过重复element +- time O(n!) + +##### 背景1 +- 在recursive call里面有for loop, 每次从i=0开始, 试着在当下list上加上nums里面的每一个。 +- 从i=0开始,所以会依次recursive每一个nums: +- 因此,例如i=2,肯定比i=3先被访问。也就是:取i=2的那个list permutation肯定先排出来。 + +##### 背景2 +- 重复的例子:给出Input[x, y1, y2], 假设y的值是一样的。那么,{x,y1,y2}和{x,y2,y1}是相同结果。 + +##### Note +- 综上,y1肯定比y2先被访问,{x,y1,y2}先出。 紧随其后,在另一个recursive循环里,{x,y2...}y2被先访问,跳过了y1。 +- 重点:规律在此,如果跳过y1,也就是visited[y1] == false, 而num[y2] == num[y1],那么这就是一个重复的结果,没必要做,越过。 +- 结果:那么,我们需要input像{x,y1,y2}这样数值放一起,那么必须排序。 + +#### Non-recursive, manuall swap +- Idea from: https://www.sigmainfy.com/blog/leetcode-permutations-i-and-ii.html +- 用到 sublist sort +- 用 swap function, 在原数组上调节出来新的permutation +- 注意: 每次拿到新的candidate, 都要把没有permutate的数位sort, 然后再开始swap. +- 这是为了确保, [j]和[j-1]在重复时候, 不用重新记录. + +#### Queue +- 给一个visited queue +- 和queue在所有的地方一同populate. +- 然后visited里面存得时visited indexes。 (Not efficient code. check again) + + + +--- + +**12. [N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens.java)** Level: Hard Tags: [Backtracking] + + +N-Queen 问题, 给数字n, 和 nxn board, 找到所有N-queens的答案. + +#### Backtracking +- 用dfs找所有情况, 每一个iteration, 从找一行里挑合适的点, dfs +- 选中的点加进candidate list 里面, 记得要backtracking. +- 每一个candidate都需要validation, 检查 row, col, 2 diagnal 有没有queen + +#### validate n queue at certain (x, y) +- 1. array 里面不能有 target row# +- 2. diagnal. 记得公式: +- row1 - row2 == col1 - col2. Diagnal elelment.fail +- row1 - row2 == - (col1 - col2). Diagnal element. fail +- Draw a 3x3 board to test the 2 scanarios: +- (0,0) and (3,3) are diagnal +- (0,2) and (2,0) are diagnal + + + + +--- + +**13. [N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens%20II.java)** Level: Hard Tags: [Backtracking] + + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + + + +--- + +**14. [Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)** Level: Easy Tags: [Backtracking, DFS, Tree] + + +给一个inputSum, 然后dfs, 找到所有path, 满足: path sum 跟 inputSum 一样. + +#### DFS, Backtracking +- 用remaining sum 来检测是否满足 input path sum 条件 +- 满足的时候add to result list +- 两种backtracking: +- 1. backtrack 当下node, 加进list, 然后dfs. dfs结束后删掉之前加进去的元素. 非常clean. +- 2. backtrack 下一个dfs level增加的value. dfs return 之后, 删掉list里面的末尾元素: 但是删掉的dfs余下的value. +- 第一种backtrack更加好掌握. + +#### Previous Notes: +- Binary Tree的一个基本题: 找到所有满足条件的path +- 遍历到底,比较sum vs. target +- 注意divide的情况。要把遍历的例子写写 + + + +--- + +**15. [Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)** Level: Medium Tags: [Backtracking, Combination, DFS] + + +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +#### DFS, Backtracking +- for loop, recursive (dfs). +- 每个item用一次, 下一个level dfs(index + 1) +- Combination DFS. 画个图想想. 每次从1~n里面pick一个数字i +- 因为下一层不能重新回去 [0~i]选,所以下一层recursive要从i+1开始选。 + + + +--- + +**16. [Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)** Level: Medium Tags: [Backtracking, DFS, String] + + +给一串数字, 检查是否是valid IP, 如果合理, 给出所有valid 的IP组合方式. + +#### Backtracking +- 递归的终点:list.zie() == 3, 解决最后一段 +- 递归在一个index上面, pass s.toCharArray() +- validate string要注意leading '0' +- 注意: 递归的时候可以用一个start/level/index来跑路 +- 但是尽量不要去改变Input source, 会变得非常confusing. +- note: code有点messy, 因为要考虑IP的valid情况 +- 那个'remainValid', 其实是一个对于remain substring的判断优化, 不成立的就不dfs了 + + + +--- + +**17. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**18. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**19. [Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)** Level: Medium Tags: [Backtracking, DFS, DP] + + +String 只包含 + , - 两个符号. 两个人轮流把consecutive连续的`++`, 翻转成 `--`. + +如果其中一个人再无法翻转了, 另一个人就赢. 求: 给出string, 先手是否能赢. + +#### Backtracking +- curr player 每走一步, 就生成一种新的局面, dfs on this +- 等到dfs结束, 不论成功与否, 都要backtracking +- curr level: 把"++" 改成 "--"; backtrack的时候, 改回 '--' +- 换成boolean[] 比 string/stringBuilder要快很多, 因为不需要重新生成string. +- ++ 可以走 (n - 1)个位置: +- T(N) = (N - 2) * T(N - 2) = (N - 4) * (N - 2) * T(N - 4) ... = O(N!) + +##### iterate based on "++" +- 做一个String s的 replica: string or stringBuilder +- 每次dfs后, 然后更替里面的字符 "+" => "-" +- 目的只是Mark已经用过的index +- 真正的dfs 还是在 original input string s 身上展开 +- 每次都重新生成substring, 并不是很efficient + +##### Game theory +- 保证p1能胜利,就必须保持所有p2的move都不能赢 +- 或者说, 在知道棋的所有情况时, 只要p2有一种路子会输, p1就一定能走对路能赢. +- 同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +- p1: player1, p2: player2 + +#### O(N^2) 的 DP +- 需要Game Theory的功底, Nim game. https://www.jiuzhang.com/qa/941/ +- http://www.1point3acres.com/bbs/thread-137953-1-1.html +- TODO: https://leetcode.com/problems/flip-game-ii/discuss/73954/Theory-matters-from-Backtracking(128ms)-to-DP-(0ms) + + + +--- + +**20. [Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning.java)** Level: Medium Tags: [Backtracking, DFS] + + +给个string s, partition(分段)后, 要确保每个partition都是palindrome. + +求所有partition palindrome组合. `list>` + +#### DFS +- 可以top->bottom: 遍历str, validate substring(start, i); if valid, add as candidate, and dfs; backtrack by remove candidate. +- 也可以bottom->up: 遍历str, validate substring(start, i); if valid, dfs(remaining str), return list of suffix; cross match with curr candidate. + +#### DFS Top->Bottom +- 在遍历str的时候,考虑从每个curr spot 到 str 结尾,是能有多少种palindorme? +- 那就从curr spot当个字符开始算,开始backtracking. +- 如果所选不是palindrome, 那move on. +- 若所选的确是palindrome, 加到path里面,DFS去下个level,等遍历到了结尾,这就产生了一种分割成palindrome的串。 +- 每次DFS结尾,要把这一层加的所选palindrome删掉,backtracking嘛 + +#### Optimization +- 可以再每一个dfs level 算 isPalindrome(S), 但是可以先把 boolean[][] isPalin 算出来, 每次O(1) 来用 +- 注意: isPalin[i][j] 是 inclusive的, 所以用的时候要认准坐标 +- Calculate isPalin[i][j]: pick mid point [0 ~ n] +- expand and validate palindrome at these indexes: `[mid, mid+1]` or `[mid-1][mid+1]` + +#### Complexity +- Overall Space O(n^2): 存 isPlain[][] +- Time O(2^n), 每一层都在做 pick/not pick index i 的选择, 所以worst case 2^n. +- 因为我们的isPalin[][]优化了palindrome的判断O(1), 所以overall Time: O(2^n) + + + +--- + +**21. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**22. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**23. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**24. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**25. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + +**26. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**27. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**28. [Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)** Level: Medium Tags: [Backtracking, DFS, Permutation] + + +#### Recursive: Backtracking +- Given a remaining list: 取, 或者不取 +- always iterate over full `nums[]`, use list.contains() to check if item has been added. +- Improvement: maintain list (add/remove elements) instead of 'list.contains' +- time O(n!): visit all possible outcome +- T(n) = n * T(n-1) + O(1) + +#### Iterative: Insertion +- 插入法: +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- 但是比dfs要快, 因该是因为 # of checks 少: 不需要check list.size(), 不需要maintain remaining list. + +#### Previous Notes +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- A bit slower, possibly because of the polling and saving the entire list every time + + + + +--- + +**29. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**30. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + +**31. [Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)** Level: Hard Tags: [Backtracking, DFS] + + +#### DFS +- Different from regular dfs to visit all, the robot move() function need to be called, backtrack needs to move() manually and backtracking path shold not be blocked by visited positions +- IMPORTANT: Mark on the way in using set, but `backtrack directly without re-check against set` +- Mark coordinate 'x@y' +- Backtrack: turn 2 times to revert, move 1 step, and turn 2 times to revert back. +- Direction has to be up, right, down, left. +- `int [] dx = {-1, 0, 1, 0};`, `int[] dy = {0, 1, 0, -1};` + + + +--- + diff --git a/review/Basic Implementation.md b/review/Basic Implementation.md new file mode 100644 index 0000000..99ae1c0 --- /dev/null +++ b/review/Basic Implementation.md @@ -0,0 +1,41 @@ + + + +## Basic Implementation (3) +**0. [Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)** Level: Easy Tags: [Basic Implementation] + + +根据 Cosine Similarity 的公式, basic implementation + + + +--- + +**1. [Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)** Level: Easy Tags: [Basic Implementation, String] + + +介绍一种count数字的方法, 然后每一行读出上一行的结果, 一行一行推算. 问nth行是啥样? + +#### Basic Implementation +- 主要是题意很难理解, 非常misleading, 等到看明白题目, 其实没有什么算法要求. +- Count duplicates and print + + + +--- + +**2. [Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)** Level: Medium Tags: [Basic Implementation, Enumeration, String] + + +给一个时间string"12:09", 用里面的4个integer组合成其他时间string, 目标找最小的next time. + +如果组合出的time string 在input time之前, 默认 + 24 hours. + +#### String +- enumerate all candidates and filter to keep the correct ones +- String.compareTo(string) -> gives lexicographical comparision + + + +--- + diff --git a/review/Binary Indexed Tree.md b/review/Binary Indexed Tree.md new file mode 100644 index 0000000..47836e8 --- /dev/null +++ b/review/Binary Indexed Tree.md @@ -0,0 +1,110 @@ + + + +## Binary Indexed Tree (3) +**0. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**1. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**2. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + diff --git a/review/Binary Search Tree.md b/review/Binary Search Tree.md new file mode 100644 index 0000000..8f04e81 --- /dev/null +++ b/review/Binary Search Tree.md @@ -0,0 +1,46 @@ + + + +## Binary Search Tree (1) +**0. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + diff --git a/review/Binary Search.md b/review/Binary Search.md new file mode 100644 index 0000000..b533873 --- /dev/null +++ b/review/Binary Search.md @@ -0,0 +1,748 @@ + + + +## Binary Search (40) +**0. [Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +Allow duplicates之后: +因为最终binary search的结果也是O(n) +所以这道题要记得: 既然是O(n), 那来个简单的for loop 也就好了。 + +当然,要跟面试官提起来原因。别一上来就只有for。。。 + + +--- + +**1. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + + +--- + +**2. [Guess Number Higher or Lower.java](https://github.com/awangdev/LintCode/blob/master/Java/Guess%20Number%20Higher%20or%20Lower.java)** Level: Easy Tags: [Binary Search] + + +binary search 公式 + + + +--- + +**3. [Two Sum II - Input array is sorted.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20II%20-%20Input%20array%20is%20sorted.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +升序array, 找2SUM. + +#### Two pointers +- 排序好的array. Two pointer移动start和end,核查sum. +- 注意sum用long. +- O(n) time + +#### Binary Search, 因为已经排好序了啊 +- 定住一个valueA, 然后在剩下的里面 binary serach 找 (target - valueB) +- for loop O(n), binary search O(logn) +- overall time: O(nLogN), 就不写了 + + + +--- + +**4. [2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +与 2sum II - input array is sorted类似. 都是sort array, 然后two pointer. + +LintCode的题. 注意找的是greater/bigger than target。 + +由于给定条件允许O(nLogn): + sort + two pointer + +while里面two pointer移动。每次如果num[left]+num[right] > target,那么其中所有num[left++]的加上num[right]都>target. +也就是,num[right]不动,计算加入挪动left能有多少组,那就是: right-left这么多。 全部加到count上去。 +然后right--.换个right去和前面的left部分作比较。 + + + +--- + +**5. [Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)** Level: Hard Tags: [Binary Search, DP, Partition DP] + + +给一串书pages[i], k个人, pages[i] 代表每本书的页数. k个人从不同的点同时开始抄书. + +问, 最快什么时候可以抄完? + +#### Partition DP +- 第一步, 理解题目要求的问题: 前k个人copy完n本书, 找到最少的用时; 也可以翻译成: `n本书, 让k个人来copy, 也就是分割成k段`. +- 最后需要求出 dp[n][k]. 开: int[n+1][k+1]. +- 原理: +- 1. 考虑最后一步: 在[0 ~ n - 1]本书里, 最后一个人可以选择copy 1 本, 2 本....n本, 每一种切割的方法的结果都不一样 +- 2. 讨论第k个人的情况, 在 j = [0 ~ i] 循环. 而循环j时候最慢的情况决定 第k个人的结果(木桶原理): `Math.max(dp[j][k - 1], sum)`. +- 3. 其中: `dp[j][k-1]` 是 [k-1]个人读完j本书的结果, 也就是著名的`上一步`. 这里循环考虑的是第k个人不同的j种上一步 : ) +- 4. 循环的结果, 是要存在 dp[i][k] = Math.min(Math.max(dp[j][k - 1], sum[j, i]), loop over i, k, j = [i ~ 0]) +- Time: O(kn^2), space O(nk) + +##### Init +- Init: dp[0][0] = 0, 0个人0本书 +- Integer.MAX_VALUE的运用: +- 当 i = 1, k = 1, 表达式: dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); +- 唯一可行的情况就只有一种: i=0, k=0, 刚好 0 个人 copy 0 本书, dp[0][0] = 0. +- 其他情况, i = 1, k = 0, 0 个人读 1本书, 不可能发生: 所以用Integer.MAX_VALUE来冲破 Math.max, 维持荒谬值. +- 当 i=0, k=0 的情况被讨论时候, 上面的方程式才会按照实际情况计算出 dp[i][k] +- 这道题的init是非常重要而tricky的 + +##### 计算顺序 +- k个人, 需要一个for loop; +- k个人, 从copy1本书开始, 2, 3, ... n-1,所以 i=[1, n], 需要第二个for loop +- 在每一个i上, 切割的方式可以有[0 ~ i] 中, 我们要计算每一种的worst time + +##### 滚动数组 +- [k] 只有和 [k - 1] 相关 +- Space: O(n) + +#### Binary Search +- 根据: 每个人花的多少时间(time)来做binary search: 每个人花多久时间, 可以在K个人之内, 用最少的时间完成? +- time variable的范围不是index, 也不是page大小. 而是[minPage, pageSum] +- validation 的时候注意3种情况: 人够用 k>=0, 人不够所以结尾减成k<0, 还有一种是time(每个人最多花的时间)小于当下的页面, return -1 +- O(nLogM). n = pages.length; m = sum of pages. + + + + +--- + +**6. [Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Perfect%20Square.java)** Level: Review Tags: [Binary Search, Math] + + +Binary找sqrt. 基本 mid+1, mid-1 template. +注意: define index as long. + + + +--- + +**7. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**8. [Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)** Level: Medium Tags: [Array, Binary Search] + + +binary search. +Goal: find peak, where both sides are descending +最左边, 最右边是Integer.MIN_VALUE时候, 也能构成中间数mid是peak的条件. + +Note: +没有必要特别check (mid-1)<0或者(mid+1)>=n. +证明: +1. 最左端: 当start=0, end = 2 => mid = 1, mid-1 = 0; +2. 最右端: 当end = n - 1, start = n - 3; mid = (start+end)/2 = n - 2; +那么mid + 1 = n - 2 + 1 = n - 1 < n 是理所当然的 + + + +--- + +**9. [Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)** Level: Medium Tags: [Binary Search, Math] + + +傻做就O(n), 要更好就考虑O(logN). +减少重复计算, 一切两半. + +注意: +- n/2的奇数偶数 +- n的正负 +- n == 0的情况, x == 0, x == 1 的情况. + + +--- + +**10. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**11. [Kth Smallest Element in a Sorted Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20Sorted%20Matrix.java)** Level: Medium Tags: [Binary Search, Heap] + +time: O(n + klogn) +space: O(n) + +给一个sorted matrix, 找kth smallest number (not distinct) + +Related: `Kth Largest Element in an Array` + +#### PriorityQueue +- 和Merge K sorted Array/ List 类似:使用PriorityQueue。 +- 因为Array的element无法直接找到next,所以用一个class Node 存value, x,y positions. +- Initial O(n) time, also find k O(k), sort O(logn) => O(n + klogn) +- 变型: Kth Largest in N Arrays + +#### Binary Search +- we know where the boundary is start/end are the min/max value. +- locate the kth smallest item (x, y) by cutt off partition in binary fasion: +- find mid-value, and count # of items < mid-value based on the ascending matrix +- O(nlogn) + + + + +--- + +**12. [Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +画图, 最小值被rotate之后, 变成array中间的最低谷. +并且, 左边山坡的最小值, 大于右边山坡的最大值. +以此来给binary search做判断. + +O(nlogn) + + + +--- + +**13. [Find Minimum in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Hard Tags: [Array, Binary Search] + + +一个需要严谨思考的题目. 因为有duplicate, 会导致不断平移, 所以最终time complexity是O(n) +所以不如直接扫一遍, 给出答案. + +但是还是写一个Binary Search的样子, 只不过worst结果是O(n) + + + +--- + +**14. [Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)** Level: Hard Tags: [Binary Search, DFS, Divide and Conquer] + + +2Dmatrix, 里面的value有一些递增, 递减的特点(细节比较长, 看原题). 目标是找到peak element + +peak: 比周围4个方向的点value大 + +#### DFS + +##### 基本原理 +- 我们不可能一口气准确定位(x,y), 但是我们可以再一个row/col里面, 找到1D array的 peak. +- 根据这个点, 再往剩下两个方向移动 +- 1. 在中间的一行i=midX, 找到peak所在的y. +- 2. 在中间的一列j=midY, 找到peak所在的x. (有可能强势override之前找到的y, 也就是放弃那一行的peak, 在midY上找peak) +- 3. 根据 (x,y) 的4个neighbor check (x,y)是不是 peak, 如果不是, 像更高的位置移动一格 +- 4. 根据之前算的 midX, midY 把board分成4个象限, 在每一份里面再继续找 +- 这个题目LintCode不给做了, 所以思路对的, 但是解答还没有再次验证. + +##### 剪枝/切分象限 +- 每次只是找到一个row/col里面的peak而已! +- 找到这个点, 就等于把board切成了两半. +- 然后, 再跟剩下的相邻的两个位置比较, 就知道了哪里更大, 就去哪里找peak, 也就是又切了第二刀. +- 切第二刀的时候, 也要把(x, y) 移到需要取的象限. 进行DFS +- 根据mid row 切割: +- http://www.jiuzhang.com/solution/find-peak-element-ii/#tag-highlight-lang-java +- http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf + +##### 时间复杂度 +- 每一个level都减一半 +- T(n) = n + T(n/2) = n + n/2 + n/4 + ... + 1 = n(1 + 1/2 + .... + 1/n) = 2n = O(n) + +#### Binary Search +- TODO +- O(nLogN) + + + +--- + +**15. [Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + + +#### s- qrt(int x) +- 理解题意, 从[0, x]找一个可以m*m=x的值. +- 注意, 如果找不到, 最后问考官该return一个什么值:按道理,因为return int, 会取整,那么return一个平方最close to x就可以. +- 注意 mid 用 long, 因为很可能超过最大int. + +#### sqrt(double x) +- 二分float number, 应该用精度来定义结尾. +- 还是二分, 但是判断条件变成: while ( end - start > eps) +- eps = 1e-12,也就是精度到1e-12 + + + +--- + +**16. [First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Bad%20Version.java)** Level: Easy Tags: [Binary Search] + + +Binary Search + +根据isBadVersion的性质,判断还如何end=mid or start=mid. +isBadVersion 是有方向的嘛,一个点错了,后面全错。 + + + +--- + +**17. [Wood Cut.java](https://github.com/awangdev/LintCode/blob/master/Java/Wood%20Cut.java)** Level: Medium Tags: [Binary Search] + + +二分的思想: 判断的是一个 validate() function, 而不是简单的'==' + +不需要sort! 为什么呢? 因为我们不是在given array上面二分, 我们是根据最大值在[0, max]上二分. + +Overall time: O(nLogM), where M = largest wood length + + + +--- + +**18. [Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +- 注意不要思维定式: 以为mid是index +- 这里mid其实是binary search on value [1, n] 的一个value. +- 再次用到validate() function + +Time: O(nLogN) + + + +--- + +**19. [Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)** Level: Review Tags: [Array, Binary Search, PreSum] + + +给int[] nums 和 window min size k. window size可以大于K. 找最大的连续数列average value. + +- Binary Search的思想, 用在所要找的这个 average sum 上面. 大小是在[min, max]之中 +- 找k的时候, 是>=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + + + +--- + +**20. [Classical Binary Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Classical%20Binary%20Search.java)** Level: Easy Tags: [Binary Search] + + +#### Binary Search Template +- while: start + 1 < end +- mid = start + (end - start) / 2; +- 根据mid作比较 +- 末尾double check start, end. + + + + +--- + +**21. [Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +#### Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, +- 找到 node.val == target, 或者走位走完, return closest + + + +--- + +**22. [Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样, 如果一样, 直接 2 ^ h - 1就好 +- 不一样的话, 再DFS + +##### Trick +- 直接DFS会timeout, O(n), 其实可以optimize +- to pass the test with O(h^2), 位运算: Math.pow(2, h) = 2 << (h - 1). 神奇! +- 2 << 1就是把所有bits往左移动一位, 也就是 * 2 + +#### Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + + + +--- + +**23. [Closest Number in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Number%20in%20Sorted%20Array.java)** Level: Easy Tags: [Binary Search] + + +- Binary Search 的一种变型, LintCode无法再跑一边. +- 考虑mid-1, mid+1. +- 一旦没有mid = target.index。 那么target最终就narrow down在(mid-1,mid) 或者(mid,mid+1) + + + +--- + +**24. [Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)** Level: Hard Tags: [Binary Search, Coordinate DP, DP] + + +俄罗斯套娃, 这里用envelope来表现. 给一串array, 每一个[x, y] 是envelope 长宽. [[5,4],[6,4],[6,7],[2,3]]. + +看用这些套娃, 可以最多套几个. + +#### DP: 1D Coordinate +- envelopes没有顺序, 先排序 (主要根据第一个index排序) +- 然后观察: 排序过后, 就变成了1D的坐标动态规划. +- max number 取决于上一个成功Russian doll的 max value + 1 +- 上一个index不知道, 所以遍历找上一个index. +- 当下index i 的状态, 取决于前面index j 的状态, 所以遍历两个index. +- O(n^2)的DP, n = envelopes.length; + +#### DP: 2D Coordinate +- 这个方法是自己想出来的, 但是时间复杂度太大, timeout +- 把envelop标记在2D grid上面, 然后像走机器人一样, 求到最右下角的最大 count max. +- count 当下能存在多少Russian doll +- 两种情况: 当下coordinate 没有target, 当下coordinate有target +- 当下coordinate 没有target: 如同机器人走法, Math.max(dp[i - 1][j], dp[i][j - 1]) +- 当下coordinate 有target: dp[i - 1][j - 1] + dp[i][j] +- timeout: O(n^2), n = largest coordinate. + + + + +--- + +**25. [Last Position of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Last%20Position%20of%20Target.java)** Level: Easy Tags: [Binary Search] + + +给一个sorted integer array, 找target出现的最后的index. array 里有重复数字 + +有重复,不是末尾点,继续binary search + + + +--- + +**26. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**27. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**28. [Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的sum. + +#### Segment Tree + Binary Search +- 其实是segment tree 每个node上面加个sum +- 记得Segment Tree methods: Build, Query +- Note: 存在SegmentTreeNode里面的是sum. 其他题目可能是min,max,count ... or something else. + + + +--- + +**29. [Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)** Level: Medium Tags: [Array, Binary Search] + + +给2D matrix, 每行sorted, 每行的首位都大于上一行的末尾. goal: find target from matrix + +#### 2D matrix 转1D array +- 一行一行是从小到大, sorted, 连续的, 可以看做1D sorted array +- Binary Search + + + +--- + +**30. [Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)** Level: Medium Tags: [Binary Search, Divide and Conquer] + + +给matrix, 每一行sorted, 每一列从上往下sorted, 找target是否存在 + +#### Binary Search +- 根据给定的性质, 其实点选的极端一点: x = 最下面的row, y = 当下一行里面最小的left position. +- (x,y)在左下角 +- 在此情况下, 只能往一个方向运行: 如果小于target, y++; 如果大于target, 那么只能x-- +- 每次操作, 都是删掉一行, 或者一列, 再也不需要回头看 +- `while (x >= 0 && y < col) {}` 确保不会跑脱 +- 同样的方式: 可以从右上角(0, col - 1) 开始, 代码稍微改一改 + +#### Divide and Conquer? +- TODO + + + +--- + +**31. [Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)** Level: Medium Tags: [Array, Binary Search] + + +给sorted array, 有重复数字, 找跟target重合所在的range. + +#### Binary Search +- 2个while loop +- 找first/last occurance +- TODO: Can the code be simplified? + + + + +--- + +**32. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**33. [H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index%20II.java)** Level: Medium Tags: [Binary Search] + + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + + + +--- + +**34. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**35. [Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + +time: log(n) +space: O(1) + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 +- 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` +- `start mid`: start = mid; +- 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- `mid < target < end`: start = mid; +- `target < mid`: end = mid; + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**36. [Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)** Level: Hard Tags: [Binary Search, Lint, Segment Tree] + + +SegmentTree大集合. Methods: `build, query, modify`. 不难。只是要都记得不犯错. + +#### Segment Tree +- build: recursively build children based on index-mid and link to curr node +- query: recursively try to find `node.start == targetStart && node.end == targetEnd`. Compare with node.mid +- modify: recursively try to find `node.start == targetStart && node.end == targetEnd`; modify and recursively assign upper interval with updated interval property. + + + +--- + +**37. [Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字, array size = n. 给一串query: 每个query是一个数, 目的找 count# items smaller than query element. + +#### Segment Tree +- 和平时的segment tree问题不同。 [0 ~ n] 代表实际数字: based on real value的segment tree. +- Modify时,把array里面的value带进去,找到特定的位子, 然后count + 1. +- 最终在SegmentTree leaf上面全是array里面实际的数字。 +- node.count: 在node range里面的有多少个数字 + +##### right use of modify() +- build() 只是 empty segment tree, 没有property +- modify() 需要: 1. 找到left, update count+=1; 2. aggregate all parent when after returning +- 所以每一个modify 都是在整个path上所有的node上 + count + +##### query trick +- 在query前,给进去的start和end是: 0 ~ value-1. +- `value-1`: 找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. + +##### About other basic segment tree setup +- [那么其他做过的SegmentTree是怎么样呢?] +- 那些构成好的SegmentTree(找min,max,sum)也有一个Array。但是构成Tree时候,随Array的index而构架。 +- 也就是说,假如有Array[x,y,....]:在leaf,会有[0,0] with value = x. [1,1] with value = y. +- [但是这题] +- 构成时,是用actual value.也就是比如Array[x,y,....]会产生leaf:[x,x]with value = ..; [y,y]with value =... +- 其实很容易看穿: +- 若给出一个固定的array构成 SegmentTree,那估计很简单:按照index从0~array.lengh,leaf上就是[0,0] with value = x. +- 若题目让构造一个空心SegmentTree, `based on value 0 ~ n-1 (n <= 10000)`, 然后把一个Array的value modify 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**38. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**39. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + diff --git a/review/Binary Tree.md b/review/Binary Tree.md new file mode 100644 index 0000000..708eb41 --- /dev/null +++ b/review/Binary Tree.md @@ -0,0 +1,239 @@ + + + +## Binary Tree (13) +**0. [Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### DFS +- 分析题意后, 按照题意: Flatten the tree, no extra space. +- 1. reserve right child: `reservedRightNode` +- 2. Connect `root.right = root.left`, DFS flatten(root.right) +- 3. 移花接木, coneect end of list -> reservedRightNode +- 4. flatten 剩下的. root.right... + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**1. [Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)** Level: Easy Tags: [Backtracking, Binary Tree, DFS] + + +给一个binary tree, 返回所有root-to-leaf path + +#### DFS, backtracking +- Find all paths, bfs/dfs all works. dfs will be simplier to write +- Recursive:分叉. dfs. +- top->bottom: enumerate current node into the list, carry to next level, and backtrack +- top->bottom is trivial to consider: path flows from top->bottom + +#### DFS, bottom->up +- We can also take current node.left or node.right to generate list of results from the subproblem +- let dfs return list of string candidates, and we can run pair the list with currenet node, once they come back. +- TODO: can write code to practice + +#### Iterative +- Iterative, 非递归练习了一下 +- 因为要每次切短list, 所以再加了一个Stack 来存level +- 单这道题用dfs更简单, 因为找的就是从头到尾的path, 是dfs的pattern + + + + +--- + +**2. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**3. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**4. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**5. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**6. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + +**7. [Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**8. [Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 + + + +--- + +**9. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**10. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**11. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**12. [Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)** Level: Medium Tags: [BST, Binary Tree] + + +给一个BST, integer range (k1, k2), 找range 里面所有的integer. + +#### BST +- 等于dfs遍历了所有k1<= x <= k2的x node。 +- dfs left, process root, then dfs right +- 这里, 把 left/right/match的情况全部cover了,然后把k1,k2的边框限制好,中间就全部遍历了。 + + + +--- + diff --git a/review/Bit Manipulation.md b/review/Bit Manipulation.md new file mode 100644 index 0000000..392dd53 --- /dev/null +++ b/review/Bit Manipulation.md @@ -0,0 +1,303 @@ + + + +## Bit Manipulation (17) +**0. [O(1) Check Power of 2.java](https://github.com/awangdev/LintCode/blob/master/Java/O(1)%20Check%20Power%20of%202.java)** Level: Easy Tags: [Bit Manipulation] + + + + +--- + +**1. [Single Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20II.java)** Level: Medium Tags: [Bit Manipulation] + + +一串数字里面, 所有数字都重复三次, 除了一个数字. 找到这个数字, linear time, without extrace space (constant space) + +TODO: bits + + + +--- + +**2. [Single Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20III.java)** Level: Medium Tags: [Bit Manipulation] + + +TODO: wut? + + +--- + +**3. [Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)** Level: Easy Tags: [Bit Manipulation, Math] + + +跟powerOfThree一样: 可以loop, check mod; 也可以用binary search找合适的数字. + + + +--- + +**4. [Sum of Two Integers.java](https://github.com/awangdev/LintCode/blob/master/Java/Sum%20of%20Two%20Integers.java)** Level: Easy Tags: [Bit Manipulation] + + +a^b 是: 不完全加法. +a&b 是: 所有可能的进位. a&b<<1是向左边进位的形态. + +Goal: 先a^b裸加, 算出进位; 再把结果和进位裸加, 再算出这一轮的进位; 再..裸价, 算进位....直到进位数==0. + +那么就,首先记录好进位的数字:carry. 然后 a^b 不完全加法一次。然后b用来放剩下的carry, 每次移动一位,继续加,知道b循环为0为止。 + +在第一回 a ^ b 之后, b 的本身意义就消失. 接下去应该给parameter重新命名. +sum = a ^ b; // sum without adding carries +nextCarry = (a & b) << 1; + +用其他variable name 取代 a, b 会更好理解一点. + +Bit Operation +Steps: + a & b: 每bit可能出现的进位数 + a ^ b: 每bit在此次操作可能留下的值,XOR 操作 + 每次左移余数1位,然后存到b, 再去跟a做第一步。loop until b == 0 + +(http://www.meetqun.com/thread-6580-1-1.html) + + + +--- + +**5. [Swap Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Bits.java)** Level: Easy Tags: [Bit Manipulation] + + +简单, 但是很多知识点: +1. Hex 0xaaaaaaaa 是1010101....1010; 0x55555555 是01010101....0101 +2. 可以用这两个hex取单数和负数. 如果需要取其他的pattern, 也可以做. +3. x很可能是negative number, 所以right-shift 要用logic shift, >>> 避免leading负数补位. + + + +--- + +**6. [Update Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Update%20Bits.java)** Level: Medium Tags: [Bit Manipulation] + + +熟悉bits的一些trick: +- ~0 = -1 = 111111...11111111 (32-bit) +- Create mask by shifting right >>>, and shifting left +- Reverse to get 0000...11110000 format mask +- & 0000 = clean up; | ABC = assign ABC + + + +--- + +**7. [Maximum XOR of Two Numbers in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20XOR%20of%20Two%20Numbers%20in%20an%20Array.java)** Level: Medium Tags: [Bit Manipulation, Trie] + + +比较难想到. 利用到XOR性质A^B=C, then A=B^C. +1. 枚举可能的A, 2. 然后一个个猜. + +1. 枚举A: 因为求MAX肯定是找leading-1最多的数字, 那么枚举A从(1000000...000)开始, +每次多一位取1或者0 +2. 因为枚举A的时候是按照每个bit来, 那么B和C也要以同样数位出现. +这里吧B和C变成了prefix的形式, 放在了set里面. +跟2sum用hashmap的思想类似, 每次用枚举的 A^B=C, 看看结果C是否已经在set里面. +如果在, 证明枚举的A可能被B^C得出, 那么就找到了一种情况. + +还用到一些技巧: +mask = (1 << i); // i位mask +mask = mask | (1 << i); // prefix mask + + + +--- + +**8. [Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer] + + +#### Vote 计数 +- vote++, vote--到最后剩下的就是winner. Time O(n), Space O(1) +- Majority Number是指超半数. 超半数的数字, 最后都会至少有vote>=1: match current majority number,vote++;if not, vote--. +- 注意:assume valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 + +#### HashMap count occurance +- Time, Space: O(n) + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**9. [Convert Integer A to Integer B.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Integer%20A%20to%20Integer%20B.java)** Level: Easy Tags: [Bit Manipulation] + + +把Integer A 转换成 Integer B 需要改变多少bits? + +#### Bit Manipulation +- a^b 显示出bit format里面有不同binary code的数位. +- 每次 (a^b)>>i 移动i位之后, 再 & 1时其实是指留下这一位的数字. +- count +- 其实用到了 ^ 找不同的bit, >> 移位, &1 mask + + + +--- + +**10. [Count 1 in Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%201%20in%20Binary.java)** Level: Easy Tags: [Bit Manipulation] + + +count 一个 32-bit number binary format 里面有多少1 + +#### Bit Manipulation +- shift >> i +- apply mask & 1 + +#### Convert to string O(n) space +可以把integer -> string -> char array. + + + +--- + +**11. [Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)** Level: Medium Tags: [Bit Manipulation, Bitwise DP, DP] + + +给一个数组, 算里面有多少bit 1. + +#### Bitwise DP +- 对于每一个数字, 其实很简单就能算出来: 每次 >>1, 然后 & 1 就可以count 1s. Time: 一个数字可以 >>1 O(logN) 次 +- 现在要对[0 ~ num] 都计算, 也就是N个数字, 时间复杂度: O(nLogN). +- 用DP来优化, 查找过的number的1s count, 存下来在 dp[number]里面. +- 计算你顺序从 0 -> num, count过的数字就可以重复利用. +- Bit题目 用num的数值本身表示DP的状态. +- 这里, dp[i] 并不是和 dp[i-1]有逻辑关系; 而是dp[i] 和dp[i>>1], 从binary representation看出有直接关系. + + + +--- + +**12. [Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)** Level: Easy Tags: [Array, Bit Manipulation, Math] + + +给一串unique数字, 数字取自 [0 ~ n], 无序, 找第一个skipped的数字. + +#### Swap +- 跟First Missing Positive 非常像, 只有一行代码的区别. +- swap 所有的数字, 到自己的correct position +- 最后一个for loop找到错位的index, 也就是缺的数字. + +#### Bit Manipulation +- XOR will only retain bits that are different 1 ^ 0 = 1, but 0^0, 1^1 == 0 +- Use that feature, 把所有value都和index XOR了 +- 剩下的多余的数字, 其实是那个index无法被XOR消掉, 也就是那个缺的number value. +- 注意: 题目告诉数字是 [0 ~ n], 然而缺一个数字, 那么在[0 ~ n - 1] 里面, 最大的数字(不管缺没缺), 一定是 n = nums.length. + +#### HastSet +- 全存, 找missing +- O(n) space, 不合题意 + +#### sorting +- sort, 找1st missing +- O(n log n) 太慢, 不合题意 + + + +--- + +**13. [Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)** Level: Hard Tags: [Bit Manipulation, String] + + +#### String +- 首先要分两半解决,断点是'.': str.split("\\."); +- Integer那一半好弄,whie loop里: num%2, num/2. 做一个 `parseInteger()` function +- Decimal那边复杂点. 做一个 `parseDecimal()` function: +- bit == 1的数学条件: 当下num * 2 >= 1。 更新: num = num * 2 - 1; +- bit == 0的数学条件: num * 2 < 1. 更新: num = num * 2 + +#### 注意 +- num是 double, 小数在 `num = num * 2 - 1` 的公式下可能无限循环 +- 因此check: num重复性,以及binary code < 32 bit. +- 所以题目也才有了32BIT的要求! + + + +--- + +**14. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**15. [Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)** Level: Medium Tags: [Bit Manipulation] + +time: O(n) +space: O(1), 32-bit array + +给出Hamming Distance定义(bit format时候有多少binary diff), 求一串数字的hamming distance总和. + +#### Bit Manipulation +- Bit题: 考验 bit >>, mask & 1, 还有对题目的理解能力 +- Put integers in binary, and compare each column: +- for each `1`, ask: how many are different from me? all the `0` +- `# of diffs at each bit-column = #ofZero * #ofOne ` +- 1. countZero[], countOne[]; 2. loop over nums and populate the two array + +##### 注意雷点 +- 问清楚: 10^9 < 2^31, we are okay with 32 bits +- `最终的hamming distance 要从 [1 ~ 32] 哪个bit开始算起`? 取决于 `最长`的那个binary format: 但不用先去找bit length +- 在做countZero, countOne时候, 都做32-bit; 最终做乘积的时候, 如果 `1` 或者 `0` 个数为零, 乘积自然为0. + + + + +--- + +**16. [Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)** Level: Easy Tags: [Bit Manipulation] + +time: O(n), n = # of bits +space: O(1) + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + + + +--- + diff --git a/review/Bitwise DP.md b/review/Bitwise DP.md new file mode 100644 index 0000000..5f0b161 --- /dev/null +++ b/review/Bitwise DP.md @@ -0,0 +1,21 @@ + + + +## Bitwise DP (1) +**0. [Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)** Level: Medium Tags: [Bit Manipulation, Bitwise DP, DP] + + +给一个数组, 算里面有多少bit 1. + +#### Bitwise DP +- 对于每一个数字, 其实很简单就能算出来: 每次 >>1, 然后 & 1 就可以count 1s. Time: 一个数字可以 >>1 O(logN) 次 +- 现在要对[0 ~ num] 都计算, 也就是N个数字, 时间复杂度: O(nLogN). +- 用DP来优化, 查找过的number的1s count, 存下来在 dp[number]里面. +- 计算你顺序从 0 -> num, count过的数字就可以重复利用. +- Bit题目 用num的数值本身表示DP的状态. +- 这里, dp[i] 并不是和 dp[i-1]有逻辑关系; 而是dp[i] 和dp[i>>1], 从binary representation看出有直接关系. + + + +--- + diff --git a/review/Brainteaser.md b/review/Brainteaser.md new file mode 100644 index 0000000..3158a8d --- /dev/null +++ b/review/Brainteaser.md @@ -0,0 +1,21 @@ + + + +## Brainteaser (1) +**0. [Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)** Level: Easy Tags: [Brainteaser, DP, Game Theory] + + +#### Brainteaser +- 著名Nim游戏 +- 写一些,发现n=4,5,6,7,8...etc之后的情况有规律性: 谁先手拿到4就输了. +- 最终很简单n%4!=0就可以了, time, space O(1) + +#### DP +- 正规地找规律做, 就跟 coins in a line 一样, 按照先手后手来做 +- 可以rolling array 优化空间 +- Time O(n), 当然啦, 这个题目这样会timeout, 可以使用brainteaser的做法写出结果. + + + +--- + diff --git a/review/Bucket Sort.md b/review/Bucket Sort.md new file mode 100644 index 0000000..7a378ce --- /dev/null +++ b/review/Bucket Sort.md @@ -0,0 +1,36 @@ + + + +## Bucket Sort (1) +**0. [H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)** Level: Medium Tags: [Bucket Sort, Hash Table, Sort] + + +找到h-index, 给的citation int[] 并不是sorted. h-index 的definition 具体看题目. + +#### Sort, find h from end +- 例子写出来,发现可以sort以后按照定义搜索一遍。 nlogn. +- 搜索一遍时候可以优化,用binary search. 但是没意义,因为array.sort已经用了nlogn +- 题目给的规则, 从小到大排序后: 剩下的 paper `n-h`, 全部要 <= h 个 citation. +- time O(nlogn), search O(n) + +##### 正向思考 +- 从i = 0 开始找第一个 `citations[i] >= h`, 就是第一个符合 h-index 规则的paper, return h + +##### 反向思考 +- 如果从 h = n, 每次h--; 那么 `x = n - h` 就是从 `[0 ~ n)` 开始找第一个 `dictations[x] >= h`, 就是结果 +- 同时, `dictations[x-1]` 就是最后一个(dictation最多的)其余的paper. + +#### Bucket count / Bucket Sort +- O(n) +- Bucket sort的思想(更像是counting sort?): 过一遍 input, 把dictation value 作为 index, 分布在bucket[index]上++ +- bucket[x] 是 count when # of citation == x. +- 如果 x 大于 n的时候, 就超出了index范围, 但是刚好这个问题可以包容, 把这样的情况记位在bucket[n]就可以 +- 巧妙: `sum += bucket[h]` where `h = [n ~ 0]` 利用了h-index的definition: +- #of papers (sum of bucket[n]...bucket[0]) has more than h cidations +- 这里运用到了bucket sort的思想, 但是并不是sorting, 而h-index的定义运用的很巧妙. +- Read more about actual bucket sort: https://en.wikipedia.org/wiki/Bucket_sort + + + +--- + diff --git a/review/Combination.md b/review/Combination.md new file mode 100644 index 0000000..a9e6c6c --- /dev/null +++ b/review/Combination.md @@ -0,0 +1,108 @@ + + + +## Combination (4) +**0. [Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)** Level: Medium Tags: [Backtracking, Combination, DFS] + + +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +#### DFS, Backtracking +- for loop, recursive (dfs). +- 每个item用一次, 下一个level dfs(index + 1) +- Combination DFS. 画个图想想. 每次从1~n里面pick一个数字i +- 因为下一层不能重新回去 [0~i]选,所以下一层recursive要从i+1开始选。 + + + +--- + +**1. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**2. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**3. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + diff --git a/review/Coordinate DP.md b/review/Coordinate DP.md new file mode 100644 index 0000000..e8ae168 --- /dev/null +++ b/review/Coordinate DP.md @@ -0,0 +1,403 @@ + + + +## Coordinate DP (17) +**0. [Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +跟unique path的grid一样, 目标走到右下角, 但是grid里面可能有obstacle, 不能跨越. 求unique path 的count. + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- 考虑最终结尾需要的状态:如何组成,写出公式. +- 公式中注意处理能跳掉的block, marked as 1. '到不了', 即为 0 path in dp[i][j]. + + + +--- + +**1. [Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Corrdinate DP +- Space, Time: O(MN) +- dp[i][j] 就是(i, j)上最多能炸掉的enemy数量 +- dp[i][j] 需要从4个方向加起来, 也就是4个方向都要走一遍, 所以分割成 UP/Down/Left/Right 4个 int[][] +- 最后一步的时候求max +- 分方向考虑的方法很容易想到,但是四个方向移动的代码比较繁琐. +- 往上炸: 要从顶向下考虑 +- 往下炸: 要从下向上考虑 +- 熟练写2D array index 的变换. + +似乎还有一个更简洁的方法, 用col count array: http://www.cnblogs.com/grandyang/p/5599289.html + + + +--- + +**2. [Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +##### DP +- 计数DP.注意方程式前两位置加在一起: 前两种情况没有overlap, 也不会缺情况. +- 注意initialization, 归1. +- 需要initialize的原因是,也是一个reminder: 在方程中会出现-1index +- Of course, row i = 0, or col j = 0, there is only 1 way to access +- time O(mn), space O(mn) + +##### 滚动数组 +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + + + + +--- + +**3. [Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Square.java)** Level: Medium Tags: [Coordinate DP, DP] + + +只能往右边,下面走, 找面积最大的 square. 也就是找到变最长的 square. + +#### DP +- 正方形, 需要每条边都是一样长度. +- 以右下角为考虑点, 必须满足条件: left/up/diagonal的点都是1 +- 并且, 如果三个点分别都衍生向三个方向, 那么最长的 square 边就是他们之中的最短边 (受最短边限制) +- dp[i][j]: max square length when reached at (i, j), from the 3 possible directions +- dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; +- Space, time O(mn) + +##### init +每个点都可能是边长1, 如果 matrix[i][j] == '1' + +##### 滚动数组 +[i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + +--- + +**4. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**5. [Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +找连续的持续上升子序列的长度. + +#### Coordinate DP +- 1D coordinate, dp 的角标, 就是代表 index i 的状态 +- 求最值, dp[i] = 在index i位置的最长子序列 +- 如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1 +- 如果没有持续上升, 那么dp[i] = 1, 重头来过 +- maintain max + +#### Basic +- 用一个数存current count, maintain max + + + +--- + +**6. [Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +#### DP +- Time, Space O(MN) +- 往右下角走, 计算最短的 path sum. 典型的坐标型. +- 注意: init 第一行的时候, 要accumulate dp[0][j - 1] + grid[i][j], 而不是单纯assign grid[i][j] + +#### Rolling Array +- Time O(MN), Space O(1) +- 需要在同一个for loop里面完成initialization, 和使用dp[i][j] +- 原因: dp[i % 2][j] 在被计算出来的时候, 是几乎马上在下一轮是要被用的; 被覆盖前不备用,就白算 +- 如果按照第一种方法, 在开始initialize dp, 看起来固然简单, 但是不方便空间优化 + + + +--- + +**7. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**8. [Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)** Level: Hard Tags: [Binary Search, Coordinate DP, DP] + + +俄罗斯套娃, 这里用envelope来表现. 给一串array, 每一个[x, y] 是envelope 长宽. [[5,4],[6,4],[6,7],[2,3]]. + +看用这些套娃, 可以最多套几个. + +#### DP: 1D Coordinate +- envelopes没有顺序, 先排序 (主要根据第一个index排序) +- 然后观察: 排序过后, 就变成了1D的坐标动态规划. +- max number 取决于上一个成功Russian doll的 max value + 1 +- 上一个index不知道, 所以遍历找上一个index. +- 当下index i 的状态, 取决于前面index j 的状态, 所以遍历两个index. +- O(n^2)的DP, n = envelopes.length; + +#### DP: 2D Coordinate +- 这个方法是自己想出来的, 但是时间复杂度太大, timeout +- 把envelop标记在2D grid上面, 然后像走机器人一样, 求到最右下角的最大 count max. +- count 当下能存在多少Russian doll +- 两种情况: 当下coordinate 没有target, 当下coordinate有target +- 当下coordinate 没有target: 如同机器人走法, Math.max(dp[i - 1][j], dp[i][j - 1]) +- 当下coordinate 有target: dp[i - 1][j - 1] + dp[i][j] +- timeout: O(n^2), n = largest coordinate. + + + + +--- + +**9. [Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + +O(n)跑2遍for. +O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. +特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 + + + +--- + +**10. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**11. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**12. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**13. [Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)** Level: Hard Tags: [Coordinate DP, Stack, String] + + +给一串string, 里面只有`(`, `)`. 找最长valid parentheses 的长度. + +#### 1D Coordinate DP +- use dp[i] track local max, maintain global max +- int[] dp. dp[i]: longest valid string that ends on i. +- 结尾是 ')', 2种情况: 1. 刚好s[i-1]是'('; 2. s[i]的')'更前面的一个起始'(' 对应 +- 注意, 结尾如果是'('属于不合理情况, 忽略. +- init: dp[0] = 0, 单个char不可能成型. +- 计算顺序: 从左到右, 找local max, maintain global max +- O(n) space, O(n) runtime + +#### Stack +- Stack 里面存所有的open/close parentheses. +- 如果遇到stack.top()刚好开合结掉, 就stack.pop(). +- 剩下的都是不合理的elements. +- 有点像negatively找 solution: `endIndex - 最后一个failedIndex(stack.pop()) - 1`, 应该就是最后一个succeeded string的长度 +- 每次更新 endIndex 为stack.top(), 然后从stack继续找下一个failedIndex +- 所有的length作比较, 就可以找出最长length +- O(n) stack space, O(n) runtime. 应该比dp慢一点, 因为做了2遍O(n) + + + + +--- + +**14. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**15. [Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Coordinate DP, DP] + +time: O(n^2) +time: O(n) + +给一串 unsorted sequence, 找到长 increasing subsequence 的个数! + +#### Coordinate DP +- 需要能够判断综合题, 分清楚情况和套路: combination of `longest subsequence` and `ways to do`, as well as global variable. +- len[i] (我们平时的dp[i]): 在前i个元素中, 最长的 increasing subsequence length; +- count[i]: 在前i个元素中, 并且以 len[i]这个长度为准的 subsequence的 count. 或者: 在前i个元素中, ways to reach longest increasing subsequence. +- `len[i] == len[j] + 1`: same length, but different sequence, so add all `count[i] += count[j]` +- `len[i] < len[j] + 1`: 这就是更长的情况找到了, 那么有多少次 count[j] 有多少, count[i] 就有多少. 仔细想sequence: 长度增长了, 但是ways to reach i 没有增长. +- 同样的判断需要用在 maxLen 和 maxFreq上: +- 如果没有增长 maxLen 不变, maxFreq上面需要 +=count[i] (同一种长度, 多了更多的做法) +- 如果maxLen 变长, maxFreq 也就是采用了 count[i] = count[j] +- TODO: Is rolling array possible? + +#### 相关 +- 都是 Coordiate DP, DP的鼻祖家族: +- Longest Increasing Subsequence (跟这道题的一部分一模一样) +- Longest Continuous Increasing Subsequence (连续, 只check dp[i - 1]) +- Longest Increasing Continuous Subsequence I, II (Lintcode, II 是matrix) + + + +--- + +**16. [Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)** Level: Medium Tags: [Coordinate DP, DP, Status DP] + + + +#### DP +- 特点: 上一步可能是swaped也可能是fixed +- 考虑A,B之间的现状: `A[i] > A[i - 1] && B[i] > B[i - 1]` 或者 `A[i] > B[i - 1] && B[i] > A[i - 1]` +- 问题: 如何把这个状态变成合理的strick-increasing状态? +- `A[i] > A[i - 1] && B[i] > B[i - 1]`: 1. 已经合理, 也不动. 2. [i], [i-1] 全部都swap +- `A[i] > B[i - 1] && B[i] > A[i - 1]`, 交错开来, 所以调换[i], 或者[i-1]: 1. 换[i-1]. 2. 换[i] +- 注意因为求min, 所以init value应该是 Integer.MAX_VALUE; + + + +--- + diff --git a/review/DFS.md b/review/DFS.md new file mode 100644 index 0000000..a2de7bf --- /dev/null +++ b/review/DFS.md @@ -0,0 +1,2189 @@ + + + +## DFS (95) +**0. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**1. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**2. [Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS] + + +给一串integers, list里面可能有nest list. 算总的sum. 规则, 如果是nested list, 每深一个depth, sum要乘以depth. + +#### DFS +- New interface to understand: object contains integer or object +- Visit all && sum, consider dfs. +- bottom->up is easier: pick nested object and execute dfs, which returns sum of it, add with (level value * weight). +- 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) +- Note1: not multiplying on overall level sum. Only multiply level with single value at this level. +- Note2:top->bottom is not necessary: there is not need of passing added object into next level. + +#### BFS +- bfs, queue, 处理queue.size(). +- use a level variable to track levels + + + +--- + +**3. [Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Same%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### BFS +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**4. [Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +如题, build balanced BST from sorted array + +#### DFS +- Binary Search Tree特点: 左边的node都比右边的node小. +- height balance, subtree height 相差<1, 必须左右sub tree均分. 做DFS(num, start, end) +- 在每一个level, 找到中间点, 然后分割2半, 继续dfs +- Divide and Conquer +- time/space: O(n), visit all nodes, no redundant visits. + + + +--- + +**5. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**6. [Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Balanced%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个binary tree, 看是否是height-balanced + +#### DFS +- DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. +- 一旦有 <0 或者差值大于1, 就全部返回Integer.MIN_VALUE. Integer.MIN_VALUE比较极端, 确保结果的正确性。 +- 最后比较返回结果是不是<0. 是<0,那就false. +- Traverse 整个tree, O(n) + + +#### DFS, maxDepth function +- Same concept as in 1, but cost more traversal efforts. + + + +--- + +**7. [Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +给一个特殊的binary tree, treeNode里面有一个 next pointer. + +写一个function, 把所有node都更同level的node 连在一起. 最右边的node.next = NULL + +#### DFS + Divide and Conquer +- 题目要求DFS. 想清楚了如何在DFS level把几种情况都考虑了, 写起来很简单. NOT BFS, because requires O(1) space +- 对于一个root来说, 只有几个点可以顾忌到: root.left, root.right, root.next. +- 想办法把这三个方向的点, 能连起来的都连起来: +- 1. `node.left.next = node.right` +- 2. If `node.next != null`, link `node.right.next = node.next.left`; +- 然后在dfs(root.left), dfs(root.right) +- Time: visit && connect all nodes, O(n) + +#### BFS +- 不和题意,用了queue space,与Input成正比。太大。 +- BFS over Tree。 用Queue 和 queue.size(),老规矩。 +- process每层queue时, 注意把next pointer加上去就好. + + + +--- + +**8. [Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +如题, 验证是否是BST. + +#### DFS +- 查看每个parent-child关系: leftchild < root < rightChild; +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest +- imagine we know the two extreme border: Integer.MIN_VALUE, Integer.MAX_VALUE; pass node around and compare node vs. node.parent. +- 方法: 把root.val 传下来作为 max 或者 min, 然后检查children +- + +##### Note: +- min/max需要时long type. +- 如果题目真的给node.val = Integer.MAX_VALUE, 我们需要能够与之比较, long就可以. + + + +--- + +**9. [Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List] + + +如题, 把一个sorted singly linked list 转换成一个 height balanced BST + +#### DFS +- Divide and Conquer +- 找到mid node +- 然后分割两半, 分别dfs做各自两个subtree: node.left,node.right +- 用长度来定位mid, 每次找中间点做root, 然后前半段, 后半段分别dfs with length. +- 用快慢pointer 找到mid. Better: 不用traverse entire linked list + +#### Details +- slowPointer = node; +- fastPointer = node.next; +- 然后把root = mid.next +- 然后开始sortedListToBST(mid.next.next); //后半段 +- mid.next = null;//非常重要,要把后面拍过序的断掉 +- sortedListToBST(head); //从头开始的前半段 +- 最后root.left, root.right merge一下。 + + + +--- + +**10. [Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### DFS +- 分析题意后, 按照题意: Flatten the tree, no extra space. +- 1. reserve right child: `reservedRightNode` +- 2. Connect `root.right = root.left`, DFS flatten(root.right) +- 3. 移花接木, coneect end of list -> reservedRightNode +- 4. flatten 剩下的. root.right... + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**11. [Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)** Level: Easy Tags: [Backtracking, Binary Tree, DFS] + + +给一个binary tree, 返回所有root-to-leaf path + +#### DFS, backtracking +- Find all paths, bfs/dfs all works. dfs will be simplier to write +- Recursive:分叉. dfs. +- top->bottom: enumerate current node into the list, carry to next level, and backtrack +- top->bottom is trivial to consider: path flows from top->bottom + +#### DFS, bottom->up +- We can also take current node.left or node.right to generate list of results from the subproblem +- let dfs return list of string candidates, and we can run pair the list with currenet node, once they come back. +- TODO: can write code to practice + +#### Iterative +- Iterative, 非递归练习了一下 +- 因为要每次切短list, 所以再加了一个Stack 来存level +- 单这道题用dfs更简单, 因为找的就是从头到尾的path, 是dfs的pattern + + + + +--- + +**12. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**13. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + +**14. [Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)** Level: Hard Tags: [Backtracking, DFS, Trie] + + +给一串words, 还有一个2D character matrix. 找到所有可以形成的words. 条件: 2D matrix 只可以相邻走位. + +#### Trie, DFS +- 相比之前的implementation, 有一些地方可以优化: +- 1. Backtracking时候, 在board[][] 上面mark就可以, 不需要开一个visited[][] +- 2. 不需要implement trie的所有方程, 用不到: 这里只需要insert. +- 普通的trie题目会让你search a word, 但是这里是用一个board, 看board的每一个字母能不能走出个Word. +- 也就是: 这里的search是自己手动写, 不是传统的trie search() funcombination +- 3. TrieNode里面存在 end的时候存string word, 表示到底. 用完了 word = null, 刚好截断重复查找的问题. + +##### 关于Trie +- Build Trie with target words: insert, search, startWith. Sometimes, just: `buildTree(words)` and return root. +- 依然要对board matrix做DFS。 +- no for loop on words. 直接对board DFS: +- 每一层,都会有个up-to-this-point的string. 在Trie里面check它是不是存在。以此判断。 +- 若不存在,就不必继续DFS下去了。 +- Trie solution time complexity, much better: +- build Trie: n * wordMaxLength +- search: boardWidth * boardHeight * (4^wordMaxLength + wordMaxLength[Trie Search]) + + +#### Regular DFS +- for loop on words: inside, do board DFS based on each word. +- Time cpmplexity: word[].length * boardWidth * boardHeight * (4^wordMaxLength) + +#### Previous Notes +- Big improvement: use boolean visited on TrieNode! +- 不要用rst.contains(...), 因为这个是O(n) 在leetcode还是会timeout(lintcode竟然可以pass)! +- 在Trie search() method 里面,凡是visit过的,mark一下。 + + + + +--- + +**15. [Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)** Level: Medium Tags: [Array, Backtracking, DFS] + + +#### DFS, Backtracking: +- 找到开头的字母, 然后recursively DFS 去把word串到底: +- 每到一个字母, 朝四个方向走, 之中一个true就可以. +- Note:每次到一个字母,mark一下'#'. 4个path recurse回来后,mark it back. + +#### Note: other ways of marking visited: +- 用一个boolean visited[][] +- Use hash map, key = x@y + + + + +--- + +**16. [Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)** Level: Medium Tags: [DFS, Divide and Conquer, Stack] + + +给一个expression string. 里面包括数字, 字母, 括号. 其中数字代表括号里面的内容重复几次. + +括号里面可以是String, 也可能是expression. + +目的: 把expression展开成一个正常的String. + + +#### Stack, Iteratively +- Process inner item first: last come, first serve, use stack. +- Record number globally and only use it when '[' is met. +- Stack存 [ ] 里面的内容, detect 括号开头结尾: 结尾时process inner string +- 有很多需要注意的细节才能做对: +- Stack 也可以用, 每个地方要注意 cast. 存进去的需要是Object: String, Integer +- 几个 type check: instanceof String, Character.isDigit(x), Integer.valueOf(int num) +- 出结果时候: `sb.insert(0, stack.pop())` + + +#### DFS +- Bottom->up: find deepest inner string first and expand from inside of `[ ]` +- 与Stack时需要考虑的一些function类似. 特别之处: **检查`[ ]`的结尾** +- 因为DFS时候, 括号里的substring会被保留着进入下一个level, 所以我们在base level要keep track of substring. +- 用int paren 来track 括号的开合, 当paren再次==0的时候 找到closure ']' +- 其他时候, 都要继续 append to substring + + + + +--- + +**17. [Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)** Level: Hard Tags: [Binary Search, DFS, Divide and Conquer] + + +2Dmatrix, 里面的value有一些递增, 递减的特点(细节比较长, 看原题). 目标是找到peak element + +peak: 比周围4个方向的点value大 + +#### DFS + +##### 基本原理 +- 我们不可能一口气准确定位(x,y), 但是我们可以再一个row/col里面, 找到1D array的 peak. +- 根据这个点, 再往剩下两个方向移动 +- 1. 在中间的一行i=midX, 找到peak所在的y. +- 2. 在中间的一列j=midY, 找到peak所在的x. (有可能强势override之前找到的y, 也就是放弃那一行的peak, 在midY上找peak) +- 3. 根据 (x,y) 的4个neighbor check (x,y)是不是 peak, 如果不是, 像更高的位置移动一格 +- 4. 根据之前算的 midX, midY 把board分成4个象限, 在每一份里面再继续找 +- 这个题目LintCode不给做了, 所以思路对的, 但是解答还没有再次验证. + +##### 剪枝/切分象限 +- 每次只是找到一个row/col里面的peak而已! +- 找到这个点, 就等于把board切成了两半. +- 然后, 再跟剩下的相邻的两个位置比较, 就知道了哪里更大, 就去哪里找peak, 也就是又切了第二刀. +- 切第二刀的时候, 也要把(x, y) 移到需要取的象限. 进行DFS +- 根据mid row 切割: +- http://www.jiuzhang.com/solution/find-peak-element-ii/#tag-highlight-lang-java +- http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf + +##### 时间复杂度 +- 每一个level都减一半 +- T(n) = n + T(n/2) = n + n/2 + n/4 + ... + 1 = n(1 + 1/2 + .... + 1/n) = 2n = O(n) + +#### Binary Search +- TODO +- O(nLogN) + + + +--- + +**18. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**19. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**20. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**21. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**22. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**23. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**24. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**25. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**26. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**27. [Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### DFS +- 简单处理swap +- recursively swap children + +#### BFS +- BFS with Queue +- 每次process一个node, swap children; 然后把child加进queue里面 +- 直到queue process完 + + + +--- + +**28. [Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree, 找最深depth + +#### DFS +- 这里要走过所有的node, 所以dfs非常合适 +- Divide and conquer. +- 维持一个最大值: Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +- 注意check root == null + +#### Note +- BFS is doable as well, but a bit more code to write: tracks largest level we reach + + + +--- + +**29. [Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### BFS +- Shortest path; minimum depth: 想到BFS, check level by level, BFS更能确保更快找到结果 +- depth definition: reach to a leaf node, where node.left == null && node.right == null +- BFS using queue, track level. + + +#### DFS +- Divide and Conquery一个最小值. +- 注意处理Leaf的null: null leaf 出现的时候, 就忽略这个leaf, 直接return算有leaf +- 另一种count的方法: 用Integer.MAX_VALUE代替 null leaf,这样可以避免错误counting. (不能直接recursive) +- 这个无论如何都要走所有node, 所以dfs应该比较适合. + + + + +--- + +**30. [Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**31. [Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +检查binary tree是否 identical. + +特点: subtree如果是有旋转的, 只要tree node value相等, 就可以算是identical + +#### DFS +- 在DFS的基础上, 比对左左,左右,右左,右右 + + + +--- + +**32. [Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)** Level: Easy Tags: [DFS, Tree] + + +#### DFS +- 基础binary tree traversal. 注意对null child的判断 + + + +--- + +**33. [Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree s, 和一个binary tree t, 检查t是不是s的subtree. + +#### DFS +- 跟 identical binary tree的写法很像 +- 只有 current s.val = t.val 的时候才需要compare same tree. +- 其他情况, 继续recursively isSubtree +- 注意:即使找到T1 == T2, 但很可能只是数字相同(这里不是binary search tree!!), 而children不同 +- 所以同时要继续recursively isSubtree(T1.left, T2) ...etc. + + + +--- + +**34. [Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个Binary Tree root, 以及两个node p, q. 找 p 和 q 的 lowest common ancestor + +#### DFS +- 因为是 binary tree, 所以直接盲目搜索搜索path不efficient, use extra space and waste time +- 巧用DFS来找每一个node的common ancestor. +- Need the assumption: 1. unique nodes across tree; 2. must have a solution +- 当root == null或者 p q 任何一个在findLCA底部被找到了(root== A || root == B),那么就return 这个root. +- 三种情况: +- 1. A,B都找到,那么这个level的node就是其中一层的ancestor: 其实,最先recursively return到的那个,就是最底的LCA parent. +- 2. A 或者 B 找到,那就还没有公共parent, return 非null得那个。 +- 3. A B 都null, 那就找错了没有呗, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time/space O(n) + + + +--- + +**35. [Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的list +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Brutly寻找p和q的common ancestor, 然后recursively drive left/right +- 非常巧妙, 但是也比较局限; 稍微变条件, 就很难recursive. +- 几种情况: +- 1. one of p, q 在leaf, 那么此时的root其实就是lowest common ancestor +- 2. 如果p, q 在root的左右两边, 这就是分叉口, 那么root就是lowest common ancestor +- 3. 如果p,q 在root的同一边 (左,右), 那么继续dfs +- O(1) extra space, O(logn) time + + + +--- + +**36. [Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**37. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**38. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + +**39. [Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum.java)** Level: Easy Tags: [DFS, Tree] + + +给一个inputSum, 然后dfs, 找到是否有一条path, 得出的path sum 跟 inputSum 一样. + +#### DFS +- 确定好结尾的条件: is leaf && val == sum +- 每一层减掉node.val, 然后dfs. +- 写一写: root == null => false 对parent nodes的影响. 这里发现没影响, 所以可以简化成用1个functionDFS. + + + + +--- + +**40. [Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)** Level: Easy Tags: [Backtracking, DFS, Tree] + + +给一个inputSum, 然后dfs, 找到所有path, 满足: path sum 跟 inputSum 一样. + +#### DFS, Backtracking +- 用remaining sum 来检测是否满足 input path sum 条件 +- 满足的时候add to result list +- 两种backtracking: +- 1. backtrack 当下node, 加进list, 然后dfs. dfs结束后删掉之前加进去的元素. 非常clean. +- 2. backtrack 下一个dfs level增加的value. dfs return 之后, 删掉list里面的末尾元素: 但是删掉的dfs余下的value. +- 第一种backtrack更加好掌握. + +#### Previous Notes: +- Binary Tree的一个基本题: 找到所有满足条件的path +- 遍历到底,比较sum vs. target +- 注意divide的情况。要把遍历的例子写写 + + + +--- + +**41. [Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)** Level: Easy Tags: [DFS, Double Recursive, Tree] + + +count所有存在的 path sum == target sum. 可以从任意点开始. 但是只能parent -> child . + +#### DFS +- 对所给的input sum 做减法, 知道 sum 达到一个目标值截止 +- 因为可以从任意点开始, 所以当sum达标时候, 需要继续recursive, 从而找到所有情况 (有正负数, sum可能继续增加/减少) +- 经典的 helper dfs recursive + self recursive +- 1. helper dfs recursive 处理包括root的情况 +- 2. self recursive 来引领 skip root的情况. + +#### 特点 +- 与 `Binary Tree Longest Consecutive Sequence II` 在recursive的做法上很相似: +- 利用dfs做包括root的recursive computation +- 利用这个function自己, 做`不包括root的recursive computation` + + + +--- + +**42. [Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)** Level: Medium Tags: [Backtracking, Combination, DFS] + + +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +#### DFS, Backtracking +- for loop, recursive (dfs). +- 每个item用一次, 下一个level dfs(index + 1) +- Combination DFS. 画个图想想. 每次从1~n里面pick一个数字i +- 因为下一层不能重新回去 [0~i]选,所以下一层recursive要从i+1开始选。 + + + +--- + +**43. [Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右:即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list + +#### DFS +- Use Map 来存每一个level的结果 +- dfs function 里, 如果 input depth 不存在, 就add to map. +- dfs function 里面先: dfs(node.right), 然后 dfs(node.left) +- 由于always depth search on right side, 所以map会被right branch populate; 然后才是 leftChild.right + + + + +--- + +**44. [Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)** Level: Medium Tags: [DFS, Tree] + + +找到从max path sum from root. 条件: 至少有一个node. + +#### DFS +- 比Binary Tree Maximum Path Sum I 简单许多. 因为条件给的更多:at least 1 node + have to start from root +- root一定用到 +- 3种情况: curr node, curr+left, curr+right +- 因为一定包括root, 说以从 `dfs(root, sum=0)` 开始, 每个level先加root, sum += root.val + + + +--- + +**45. [Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +找到binary tree 里的最长 consecutive sequence. + +#### DFS +- Divide and Conquer. dfs +- 分开 看左边/右边 +- 如果左边满足连续递增的规则, dfs (depth + 1), 如果不满足规则, dfs(depth = 1) +- 右边也是一样 +- 对结果跟max作比较, return + + + +--- + +**46. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**47. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**48. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**49. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**50. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**51. [Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)** Level: Medium Tags: [DFS, Divide and Conquer] + + +如题: Calculate the a^n % b where a, b and n are all 32bit integers. + +#### Divide and Conquer +- a^n可以被拆解成(a*a*a*a....*a), 是乘机形式,而%是可以把每一项都mod一下的。所以就拆开来take mod. +- 这里用个二分的方法,recursively二分下去,直到n/2为0或者1,然后分别对待. +- 注意1: 二分后要conquer,乘积可能大于Integer.MAX_VALUE, 所以用个long. +- 注意2: 要处理n%2==1的情况,二分时候自动省掉了一份 a,要乘一下。 + + + + +--- + +**52. [Find the Connected Component in the Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Connected%20Component%20in%20the%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS] + + +给一个undirected graph, return 所有的component. (这道题找不到了) + +#### BFS +- BFS遍历,把每个node的neighbor都加进来. +- 一定注意要把visit过的node Mark一下。因为curr node也会是别人的neighbor,会无限循环。 +- Component的定义:所有Component内的node必须被串联起来via path (反正这里是undirected, 只要链接上就好) +- 这道题:其实component在input里面都已经给好了,所有能一口气visit到的,全部加进queue里面,他们就是一个component里面的了。 +- 而我们这里不需要判断他们是不是Component + +#### DFS +- DFS 应该也可以 visit all nodes, mark visited. + + + +--- + +**53. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**54. [Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)** Level: Medium Tags: [Backtracking, DFS, String] + + +给一串数字, 检查是否是valid IP, 如果合理, 给出所有valid 的IP组合方式. + +#### Backtracking +- 递归的终点:list.zie() == 3, 解决最后一段 +- 递归在一个index上面, pass s.toCharArray() +- validate string要注意leading '0' +- 注意: 递归的时候可以用一个start/level/index来跑路 +- 但是尽量不要去改变Input source, 会变得非常confusing. +- note: code有点messy, 因为要考虑IP的valid情况 +- 那个'remainValid', 其实是一个对于remain substring的判断优化, 不成立的就不dfs了 + + + +--- + +**55. [Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Topological Sort] + + +#### Topological Sort BFS +- indegree tracking: Track all neighbors/childrens. 把所有的children都存在 inDegree里面 +- Process with a queue: 先把所有的root加一遍(indegree == 0),可能多个root。并且全部加到queue里面。 +- BFS with Queue: +- Only when map.get(label) == 0, add into queue && rst. (indegree剪完了, 就是root啦) +- inDegree在这里就 count down indegree, 确保在后面出现的node, 一定最后process. + + +#### Basics about graph +- 几个graph的condition: +- 1. 可能有多个root +- 2. directed node, 可以direct backwards. + +TODO: +- build`Map inDegree = new HashMap<>();` and include the root itself +- that is more traditional indegree building + + + +--- + +**56. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**57. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**58. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**59. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + +**60. [Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)** Level: Medium Tags: [Backtracking, DFS, DP] + + +String 只包含 + , - 两个符号. 两个人轮流把consecutive连续的`++`, 翻转成 `--`. + +如果其中一个人再无法翻转了, 另一个人就赢. 求: 给出string, 先手是否能赢. + +#### Backtracking +- curr player 每走一步, 就生成一种新的局面, dfs on this +- 等到dfs结束, 不论成功与否, 都要backtracking +- curr level: 把"++" 改成 "--"; backtrack的时候, 改回 '--' +- 换成boolean[] 比 string/stringBuilder要快很多, 因为不需要重新生成string. +- ++ 可以走 (n - 1)个位置: +- T(N) = (N - 2) * T(N - 2) = (N - 4) * (N - 2) * T(N - 4) ... = O(N!) + +##### iterate based on "++" +- 做一个String s的 replica: string or stringBuilder +- 每次dfs后, 然后更替里面的字符 "+" => "-" +- 目的只是Mark已经用过的index +- 真正的dfs 还是在 original input string s 身上展开 +- 每次都重新生成substring, 并不是很efficient + +##### Game theory +- 保证p1能胜利,就必须保持所有p2的move都不能赢 +- 或者说, 在知道棋的所有情况时, 只要p2有一种路子会输, p1就一定能走对路能赢. +- 同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +- p1: player1, p2: player2 + +#### O(N^2) 的 DP +- 需要Game Theory的功底, Nim game. https://www.jiuzhang.com/qa/941/ +- http://www.1point3acres.com/bbs/thread-137953-1-1.html +- TODO: https://leetcode.com/problems/flip-game-ii/discuss/73954/Theory-matters-from-Backtracking(128ms)-to-DP-(0ms) + + + +--- + +**61. [Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)** Level: Easy Tags: [Array, DFS] + + +#### DFS +- 虽然Easy, 但用到DFS最基本的想法. +- 1. dive deep +- 2. mark VISITED +- 3. sum it up +- Time: worst O(mn), traverse all possible nodes + +- 更要注意, 要从符合条件value==1的地方开始dfs. +- 对于什么island都没有的情况,area应该位0, 而不是Integer.MIN_VALUE, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**62. [Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning.java)** Level: Medium Tags: [Backtracking, DFS] + + +给个string s, partition(分段)后, 要确保每个partition都是palindrome. + +求所有partition palindrome组合. `list>` + +#### DFS +- 可以top->bottom: 遍历str, validate substring(start, i); if valid, add as candidate, and dfs; backtrack by remove candidate. +- 也可以bottom->up: 遍历str, validate substring(start, i); if valid, dfs(remaining str), return list of suffix; cross match with curr candidate. + +#### DFS Top->Bottom +- 在遍历str的时候,考虑从每个curr spot 到 str 结尾,是能有多少种palindorme? +- 那就从curr spot当个字符开始算,开始backtracking. +- 如果所选不是palindrome, 那move on. +- 若所选的确是palindrome, 加到path里面,DFS去下个level,等遍历到了结尾,这就产生了一种分割成palindrome的串。 +- 每次DFS结尾,要把这一层加的所选palindrome删掉,backtracking嘛 + +#### Optimization +- 可以再每一个dfs level 算 isPalindrome(S), 但是可以先把 boolean[][] isPalin 算出来, 每次O(1) 来用 +- 注意: isPalin[i][j] 是 inclusive的, 所以用的时候要认准坐标 +- Calculate isPalin[i][j]: pick mid point [0 ~ n] +- expand and validate palindrome at these indexes: `[mid, mid+1]` or `[mid-1][mid+1]` + +#### Complexity +- Overall Space O(n^2): 存 isPlain[][] +- Time O(2^n), 每一层都在做 pick/not pick index i 的选择, 所以worst case 2^n. +- 因为我们的isPalin[][]优化了palindrome的判断O(1), 所以overall Time: O(2^n) + + + +--- + +**63. [Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST, DFS, Tree] + + +BST里面有2个node misplace, 要归为. 要求: O(1) extra space + +#### Observation +- BST inorder traversal should give small -> large sequence +- misplaced means: a **large**->small item would occur, and later a large>**small** would occur. +- The first large && second small item are the 2 candidates. Example +- [1, 5, 7, 10, 12, 15, 18] +- [1, 5, `15, 10`, `12, 7`, 18] + +#### dfs, O(1) extra space +- traverse, and take note of the candidate +- at the end, swap value of the 2 candidates + +#### O(n) space +- inorder traversal the nodes and save in array, find the 2 items misplanced and swap them +- But O(n) space should not be allowed + + + + +--- + +**64. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + +**65. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**66. [Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)** Level: Review Tags: [BFS, DFS, DP] + + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- in dfs: remove the incorrect parentheses one at a time +- detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` +- once detected, remove the char from middle of s, and dfs on the rest of the s that has not been tested yet. + +##### Core concept: reverse test +- `if a parenthese string is valid, the reverse of it should also be valid` +- Test s with open='(', close=')' first; **reverse s**, and test it with open=')', close='(' + +##### Minor details +- only procceed to remove invalid parenthese when `count<0`, and also break && return dfs after the recursive calls. +- The above 2 facts eliminates all the redundant results. +- Reverse string before alternating open and close parentheses, so when returning final result, it will return the correct order. +- Open questions: how does it guarantee minimum removals? + +##### Backtracking +- 如果用stringbuffer, 那么久不会每次create new string, 但是需要maintain这个string buffer, 就会backtracking + +##### Complexity +- Seems to be O(n), but need to derive + +#### BFS +TODO + +#### DP + + + +--- + +**67. [Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)** Level: Medium Tags: [DFS, Hash Table, Tree] + + +给一串3-digit 的数组. 每个数字的表达一个TreeNode, 3 digit分别代表: depth.position.value + +这串数字已经从小到大排列. 求: 所有可能的 root->leaf path 的所有可能的 path sum 总和. + +#### DFS, Hash Table +- 因为`前两个digit可以uniquely identify`一个node, 所以可以把前两个digit作为key, 定位node. +- 特点: 比如考虑root, 有 n 个leaf, 就会加 n 遍root, 因为有 n 个 unique path嘛. +- 实现: 每个node, 上来先把curr value加进sum; 只要有child, 到这个node位置的以上path sum 就要被重加一次. +- format: depth.position.value. (on same level, position may not be continuous) +- approach: map each number into: , and dfs. +- Start from dfs(map, rootKey, sum): +- 1. add node value to sum +- 2. compute potential child. +- 3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +- 4. also, if child exist, dfs into next level +- Space, time O(n) + + + +--- + +**68. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**69. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**70. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**71. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**72. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + +**73. [Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + +time: O(n) +space: O(n) + +给一个tree, 按照题意找最一个node满足: +1. 这个node的subtree涵盖最深level的所有leaves. +2. 这个node必须是能找到的最deep那个 + +条件2的需求是因为: root本身就是满足条件1的node, 还有很多Higher-level node也是如此, 所以要找那个deepest. + + +#### DFS on tree +- 分析题目, 思想是: 看到tree里面所有的leaves, 找到他们最deep的 common ancestor +- Maintain a map +- Recursively dfs: return deepest node that has all leaves by these comparisons: +- 1. If left,right child same depth, return root: they need common ancestor +- 2. If not same depth, return the one with larger depth +- 被传送去上一个level的, 永远都是subtree里面符合题意的: the node containing all leaf nodes +- Visit all nodes once O(n), space O(n) + +#### BFS +- Find all leaves at deepest level +- Use map to track each node-parent +- Backtrack all nodes to find common ancestor + + + +--- + +**74. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + + +// 如何想到从中间initialize + + + +--- + +**75. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**76. [Populating Next Right Pointers in Each Node II.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node%20II.java)** Level: Medium Tags: [DFS, Tree] + +time: O(n) +space: O(1) + +给一个binary tree, 用constant space link 所有所有node.next to same level next node. + +#### DFS +- 用constant space 也就是不可以BFS, 但是mention了用dfs stack space没问题 (提示啊!) +- 1. link leftChild -> rightChild +- 2. resolve root.rightMost child -> first possible root.next.left/right child +- 3. dfs connect(rightChild), connect(leftChild) +- Each level should be fully linked from left side, so every reach to parent will have valid path or end. + +#### Trick +- 1. 处理 nextNode -> next -> next ...的case: 找到第一个有child的next node才可以罢休. 这个case很容易miss +- 2. 我们的假设是, 上一个level的所有node都应该是linked, 那么在dfs时候, 就应该先connect(root.right). 右孩子的全处理完毕, 那么trick1才可以施行. + + + +--- + +**77. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**78. [Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +#### Tree +- Traverse tree: left, right +- Concept of partial compare vs. whole compare + + + +--- + +**79. [Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)** Level: Medium Tags: [BFS, DFS] + + +给一个room 2D grid. 里面有墙-1, 门0, 还有empty space INF(Math.MAX_VALUE). + +对每个empty space而言, fill it with dist to nearest gate. + +#### DFS +- Form empty room: it can reach different gate, but each shortest length will be determined by the 4 directions. +- Option1(NOT applicable). DFS on INF, mark visited, summerize results of 4 directions. +- hard to resue: we do not know the direction in cached result dist[i][j] +- Option2. DFS on gate, and each step taken to each direction will +1 on the spot: distance from one '0'; +- Through dfs from all zeros, update each spot with shorter dist +- Worst time: O(mn), where entre rooms[][] are gates. It takes O(mn) to complete the iteration. Other gates be skipped by `if (rooms[x][y] <= dist) return;` + +#### BFS +- Exact same concept. Init with `Queue queue = new LinkedList()` + + + +--- + +**80. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**81. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + +**82. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**83. [Flood Fill.java](https://github.com/awangdev/LintCode/blob/master/Java/Flood%20Fill.java)** Level: Easy Tags: [DFS] + + +Same as MS Paint + +#### DFS +- track `boolean[][] visited`, validate before dfs + + + +--- + +**84. [Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph] + + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes. +- 先能复制多少Node复制多少. 然后把neighbor 加上 +- Use `map` to mark visited + +#### DFS +- Given graph node obj `{val, list of neighbor}`: copy the node and all neighbors +- Mark visited using map +- for loop on the each one of the neighbors: map copy, record in map, and further dfs +- once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +- 主要思想是: 一旦复制过了, 不必要重新复制 + +#### BFS +- Copy the root node, then copy all the neighbors. +- Mark copied node in map. +- Use queue to contain the newly added neighbors. Need to work on them in the future. + + + +--- + +**85. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + +**86. [Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)** Level: Medium Tags: [Backtracking, DFS, Permutation] + + +#### Recursive: Backtracking +- Given a remaining list: 取, 或者不取 +- always iterate over full `nums[]`, use list.contains() to check if item has been added. +- Improvement: maintain list (add/remove elements) instead of 'list.contains' +- time O(n!): visit all possible outcome +- T(n) = n * T(n-1) + O(1) + +#### Iterative: Insertion +- 插入法: +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- 但是比dfs要快, 因该是因为 # of checks 少: 不需要check list.size(), 不需要maintain remaining list. + +#### Previous Notes +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- A bit slower, possibly because of the polling and saving the entire list every time + + + + +--- + +**87. [Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)** Level: Hard Tags: [DFS, Greedy, Math] + + +#### Greedy, Iterative +- For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +- We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +- Track the newly formed string; if new, add the new char to overall result +- Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] +- Same concept as dfs + +#### DFS +- Same concept: use window to cut out tail, and append with new candidate +- do this for k^n = Math.pow(k, n) times + + + +--- + +**88. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**89. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**90. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + +**91. [The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)** Level: Medium Tags: [BFS, DFS] + + +#### BFS +- BFS on coordinates +- always attempt to move to end of border +- use boolean[][] visited to alingn with BFS solution in Maze II, III, where it uses Node[][] to store state on each item. + + + +--- + +**92. [The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)** Level: Medium Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- if already found a good/shorter route, skip +- `if (distMap[node.x][node.y] <= node.dist) continue;` +- This always terminates the possibility to go return to original route, because the dist will be double/higher + + + +--- + +**93. [The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)** Level: Hard Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- 跟 Maze I, II 类似, 用一个 Node[][] 来存每一个(x,y)的state. +- Different from traditional BFS(shortest path): `it terminates BFS when good solution exists (distance), but will finish all possible routes` +- 1. `Termination condition`: if we already have a good/better solution on nodeMap[x][y], no need to add a new one +- 2. Always cache the node if passed the test in step1 +- 3. Always offer the moved position as a new node to queue (as long as it fits condition) +- 4. Finally the item at nodeMap[target.x][target.y] will have the best solution. + + + +--- + +**94. [Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)** Level: Hard Tags: [Backtracking, DFS] + + +#### DFS +- Different from regular dfs to visit all, the robot move() function need to be called, backtrack needs to move() manually and backtracking path shold not be blocked by visited positions +- IMPORTANT: Mark on the way in using set, but `backtrack directly without re-check against set` +- Mark coordinate 'x@y' +- Backtrack: turn 2 times to revert, move 1 step, and turn 2 times to revert back. +- Direction has to be up, right, down, left. +- `int [] dx = {-1, 0, 1, 0};`, `int[] dy = {0, 1, 0, -1};` + + + +--- + diff --git a/review/DP.md b/review/DP.md new file mode 100644 index 0000000..5800ea3 --- /dev/null +++ b/review/DP.md @@ -0,0 +1,2122 @@ + + + +## DP (85) +**0. [Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)** Level: Medium Tags: [DP] + + +这个DP有点诡异. 需要斟酌。 +NOT DONE YET + + +--- + +**1. [Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + +#### DP +- 找对方程dp[x], 积累到amount x最少用多少个coin: #coin是value, index是 [0~x]. +- 子问题的关系是: 如果用了一个coin, 那么就应该是f[x - coinValue]那个位置的#coins + 1 + +##### initialization +- 处理边界, 一开始0index的时候, 用value0. +- 中间利用Integer.MAX_VALUE来作比较, initialize dp[x] +- 注意, 一旦 Integer.MAX_VALUE + 1 就会变成负数. 这种情况会在coin=0的时候发生. + +##### Optimization +- 方法1: 直接用Integer.MAX_VALUE +- 方法2: 用-1, 稍微简洁一点, 每次比较dp[i]和 dp[i - coin] + 1, 然后save. 不必要做多次min比较. + +#### Memoization +- dp[i] 依然表示: min # of coints to make amount i +- initialize dp[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录dp[amount] 如果已经给过value, 不要重复计算, 直接return. +- 但是这道题没必要强行做memoization, 普通DP的状态和方程相对来说很好找到 + + + +--- + +**2. [Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### DP +- 求最值, 想到DP. Time/Space O (n) +- 两个特别处: +- 1. 正负数情况, 需要用两个DP array. +- 2. continuous prodct 这个条件决定了在Math.min, Math.max的时候, +- 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. +- 这也就注定了需要一个global variable 来hold result. + +#### Space optimization, rolling array +- maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins +- Time: O(n), space: O(1) + + + +--- + +**3. [k Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/k%20Sum.java)** Level: Hard Tags: [DP] + + +DP. 公式如何想到, 还需要重新理解. + +dp[i][j][m]: # of possibilities such that from j elements, pick m elements and sum up to i. +i: [0~target] + +dp[i][j][m] = dp[i][j-1][m] + dp[i - A[j - 1]][j-1][m-1] + (i not included) (i included) + + + +--- + +**4. [Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DP, Tree] + + +Not quite clear. +根据左右分割而总结出了原理, 每次分割, 左右两边都会有一定数量的permutation, 总体上的情况数量当然是相乘. +然后每一个不同的分割点都加一遍: +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +然后把数学公式转换成DP的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**5. [Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +跟unique path的grid一样, 目标走到右下角, 但是grid里面可能有obstacle, 不能跨越. 求unique path 的count. + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- 考虑最终结尾需要的状态:如何组成,写出公式. +- 公式中注意处理能跳掉的block, marked as 1. '到不了', 即为 0 path in dp[i][j]. + + + +--- + +**6. [Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Corrdinate DP +- Space, Time: O(MN) +- dp[i][j] 就是(i, j)上最多能炸掉的enemy数量 +- dp[i][j] 需要从4个方向加起来, 也就是4个方向都要走一遍, 所以分割成 UP/Down/Left/Right 4个 int[][] +- 最后一步的时候求max +- 分方向考虑的方法很容易想到,但是四个方向移动的代码比较繁琐. +- 往上炸: 要从顶向下考虑 +- 往下炸: 要从下向上考虑 +- 熟练写2D array index 的变换. + +似乎还有一个更简洁的方法, 用col count array: http://www.cnblogs.com/grandyang/p/5599289.html + + + +--- + +**7. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**8. [Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)** Level: Medium Tags: [Backpack DP, DP] + + +给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法. + +nums 里的数字, 可以重复使用. 不同的order可以算作不同的拼法. + +#### Backpack DP +- dp[i] 表示: # of ways to fill weight i +- 1维: dp[w]: fill weigth w 有多少种方法. 前面有多少种可能性, 就sum多少个: +- dp[w] = sum{dp[w - nums[i]]}, i = 0~n + +##### 分析 +- 拼背包时, 可以有重复item, 所以考虑'最后被放入的哪个unique item' 就没有意义了. +- 背包问题, 永远和weight分不开关系. +- 这里很像coin chagne: 考虑最后被放入的东西的value/weigth, 而不考虑是哪个. + + + + + + +--- + +**9. [Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)** Level: Hard Tags: [Binary Search, DP, Partition DP] + + +给一串书pages[i], k个人, pages[i] 代表每本书的页数. k个人从不同的点同时开始抄书. + +问, 最快什么时候可以抄完? + +#### Partition DP +- 第一步, 理解题目要求的问题: 前k个人copy完n本书, 找到最少的用时; 也可以翻译成: `n本书, 让k个人来copy, 也就是分割成k段`. +- 最后需要求出 dp[n][k]. 开: int[n+1][k+1]. +- 原理: +- 1. 考虑最后一步: 在[0 ~ n - 1]本书里, 最后一个人可以选择copy 1 本, 2 本....n本, 每一种切割的方法的结果都不一样 +- 2. 讨论第k个人的情况, 在 j = [0 ~ i] 循环. 而循环j时候最慢的情况决定 第k个人的结果(木桶原理): `Math.max(dp[j][k - 1], sum)`. +- 3. 其中: `dp[j][k-1]` 是 [k-1]个人读完j本书的结果, 也就是著名的`上一步`. 这里循环考虑的是第k个人不同的j种上一步 : ) +- 4. 循环的结果, 是要存在 dp[i][k] = Math.min(Math.max(dp[j][k - 1], sum[j, i]), loop over i, k, j = [i ~ 0]) +- Time: O(kn^2), space O(nk) + +##### Init +- Init: dp[0][0] = 0, 0个人0本书 +- Integer.MAX_VALUE的运用: +- 当 i = 1, k = 1, 表达式: dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); +- 唯一可行的情况就只有一种: i=0, k=0, 刚好 0 个人 copy 0 本书, dp[0][0] = 0. +- 其他情况, i = 1, k = 0, 0 个人读 1本书, 不可能发生: 所以用Integer.MAX_VALUE来冲破 Math.max, 维持荒谬值. +- 当 i=0, k=0 的情况被讨论时候, 上面的方程式才会按照实际情况计算出 dp[i][k] +- 这道题的init是非常重要而tricky的 + +##### 计算顺序 +- k个人, 需要一个for loop; +- k个人, 从copy1本书开始, 2, 3, ... n-1,所以 i=[1, n], 需要第二个for loop +- 在每一个i上, 切割的方式可以有[0 ~ i] 中, 我们要计算每一种的worst time + +##### 滚动数组 +- [k] 只有和 [k - 1] 相关 +- Space: O(n) + +#### Binary Search +- 根据: 每个人花的多少时间(time)来做binary search: 每个人花多久时间, 可以在K个人之内, 用最少的时间完成? +- time variable的范围不是index, 也不是page大小. 而是[minPage, pageSum] +- validation 的时候注意3种情况: 人够用 k>=0, 人不够所以结尾减成k<0, 还有一种是time(每个人最多花的时间)小于当下的页面, return -1 +- O(nLogM). n = pages.length; m = sum of pages. + + + + +--- + +**10. [Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)** Level: Hard Tags: [DP, Interval DP, String] + + +- 给两个string S, T. 检验他们是不是scramble string. +- scramble string 定义: string可以被分拆成binary tree的形式, 也就是切割成substring; +- 旋转了不是leaf的node之后, 形成新的substring, 这就是原来string的 scramble. + + +#### Interval DP 区间型 +- 降维打击, 分割, 按照长度来dp. +- dp[i][j][k]: 数组S从index i 开始, T从index j 开始, 长度为k的子串, 是否为scramble string + +##### Break down +- 一切两半以后, 看两种情况: , 或者不rotate这两半. 对于这些substring, 各自验证他们是否scramble. +- 不rotate分割的两半: S[part1] 对应 T[part1] && S[part2] 对应 T[part2]. +- rotate分割的两半: S[part1] 对应 T[part2] && S[part2] 对应 T[part1]. + +##### Initialization +- len == 1的时候, 其实无法旋转, 也就是看S,T的相对应的index是否字符相等. +- initialization非常非常重要. 很神奇, 这个initailization 打好了DP的基础, 后面一蹴而就, 用数学表达式就算出了结果. +- input s1, s2 在整个题目的主要内容里面, 几乎没有用到, 只是用在initialization时候. +- More details, 看解答 + + + +--- + +**11. [Best Time to Buy and Sell Stock with Cooldown.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Cooldown.java)** Level: Medium Tags: [DP] + + +Sequence DP +跟StockIII很像. 分析好HaveStock && NoStock的状态, 然后看最后一步. + + + +--- + +**12. [Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP] + + +给两个string, A, B. 找这两个string里面的LCS: 最长公共字符长度 (不需要是continuous substring) + +#### Double Sequence DP +- 设定dp长度为(n+1), 因为dp[i]要用来表示前i个(ith)时候的状态, 所以长度需要时i+1才可以在i位置, hold住i. +- 双序列: 两个sequence之间的关系, 都是从末尾字符看起, 分析2种情况: +- 1. A最后字符不在common sequence 或者 B最后字符不在common sequence. +- 2. A/B最后字符都在common sequence. 总体count + 1. + + + +--- + +**13. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**14. [Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + +time: O(MN) +Space: O(N) + +两个字符串, A要变成B, 可以 insert/delete/replace, 找最小变化operation count + +#### Double Sequence +- 考虑两个字符串的末尾index s[i], t[j]: 如果需要让这两个字符一样, 可能使用题目给出的三种operation: insert/delete/replace? +- 先calculate最坏的情况, 3种operation count + 1; 然后在比较match的情况. +- 注意, 在i或者j为0的时候, 变成另外一个数字的steps只能是全变. +- 第一步, 空间时间都是O(MN), O(MN) +- 滚动数组优化, 空间O(N) + +##### Detail analysis +- insert: assume insert on s, `#ofOperation = (s[0 ~ i] to t[0 ~ j-1]) + 1;` +- delete: assume delete on t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j]) + 1;` +- replace: replace both s and t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j - 1]) + 1;` +- dp[i][j]代表了两个 sequence 互相之间的性质: s[0 ~ i] 转换成 s[0~j] 所需要的最少 operation count +- init: 当i==0, dp[0][j] = j; 每次都要 + j 个character; 同理, 当j==0, dp[i][0] = i; +- 而dp[i][j]有两种情况处理: `s[i] == t[j]` or `s[i] != t[j]` + +##### 何时initialize +- 这种判断取决于经验: 如果知道initialization可以再 double for loop 里面一起做, 那么可以留着那么做 +- 这样属于 `需要什么, initialize什么` +- 事后在做space optimization的时候, 可以轻易在 1st dimension 上做rolling array + +#### Search +- 可以做, 但是不建议:这道题需要找 min count, 而不是search/find all solutions, 所以search会写的比较复杂, 牛刀杀鸡. + + + +--- + +**15. [Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)** Level: Hard Tags: [DP, String] + + +Double Sequence DP: +0. DP size (n+1): 找前nth的结果, 那么dp array就需要开n+1, 因为结尾要return dp[n][m] +1. 在for loop 里面initialize dp[0][j] dp[i][0] +2. Rolling array 优化成O(N): 如果dp[i][j]在for loop里面, 就很好替换 curr/prev + + + +--- + +**16. [Ones and Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Ones%20and%20Zeroes.java)** Level: Hard Tags: [DP] + + +还是Double Sequence, 但是考虑第三种状态: 给的string array的用量. +所以开了3维数组. + +如果用滚动数组优化空间, 需要把要滚动的那个for loop放在最外面, 而不是最里面. +当然, 这个第三位define在 dp[][][]的哪个位置, 问题都不大. + +另外, 注意在外面calcualte zeros and ones, 节约时间复杂度. + + + +--- + +**17. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**18. [Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +##### DP +- 计数DP.注意方程式前两位置加在一起: 前两种情况没有overlap, 也不会缺情况. +- 注意initialization, 归1. +- 需要initialize的原因是,也是一个reminder: 在方程中会出现-1index +- Of course, row i = 0, or col j = 0, there is only 1 way to access +- time O(mn), space O(mn) + +##### 滚动数组 +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + + + + +--- + +**19. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**20. [Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Square.java)** Level: Medium Tags: [Coordinate DP, DP] + + +只能往右边,下面走, 找面积最大的 square. 也就是找到变最长的 square. + +#### DP +- 正方形, 需要每条边都是一样长度. +- 以右下角为考虑点, 必须满足条件: left/up/diagonal的点都是1 +- 并且, 如果三个点分别都衍生向三个方向, 那么最长的 square 边就是他们之中的最短边 (受最短边限制) +- dp[i][j]: max square length when reached at (i, j), from the 3 possible directions +- dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; +- Space, time O(mn) + +##### init +每个点都可能是边长1, 如果 matrix[i][j] == '1' + +##### 滚动数组 +[i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + +--- + +**21. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**22. [Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)** Level: Medium Tags: [DP, Game Theory, Greedy] + + +拿棋子游戏, 每个人可以拿1个或者2个, 拿走最后一个子儿的输. 问: 根据给的棋子输, 是否能确定先手的输赢? + +Game Theory: 如果我要赢, 后手得到的局面一定要'有输的可能'. + +#### DP, Game Theory +- 要赢, 必须保证对手拿到棋盘时, 在所有他可走的情况中, '有可能败', 那就足够. +- 设计dp[i]:表示我面对i个coins的局面时是否能赢, 取决于我拿掉1个,或者2个时, 对手是不是会可能输? +- dp[i] = !dp[i - 1] || !dp[i-2] +- 时间: O(n), 空间O(n) +- 博弈问题, 常从'我的第一步'角度分析, 因为此时局面最简单. + +#### Rolling Array +空间优化O(1). Rolling array, %2 + + + +--- + +**23. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**24. [Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)** Level: Easy Tags: [DP, Memoization, Sequence DP] + + +每一步可以走1步或者2步, 求总共多少种方法爬完梯子. + +#### Recursive + Memoization +- 递归很好写, 但是重复计算, timeout. time: O(2^n) +- O(2^n): each n can spawn 2 dfs child, at next level, it will keep spawn. Total 2^n nodes will spawn. +- 用全局变量int[] memo 帮助减少重复计算 +- O(n) time, space + +#### DP +- 加法原理, 最后一步被前两种走法决定: dp[i] = dp[i - 1] + dp[i - 2] +- 基础sequence DP, int[] dp = int[n + 1]; +- DP[]存的是以 1-based index的状态 +- dp[i]: count # of ways to finish 前i个 台阶 +- 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1] +- dp[0]往往是有特殊状态的 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**25. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**26. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**27. [Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)** Level: Easy Tags: [Brainteaser, DP, Game Theory] + + +#### Brainteaser +- 著名Nim游戏 +- 写一些,发现n=4,5,6,7,8...etc之后的情况有规律性: 谁先手拿到4就输了. +- 最终很简单n%4!=0就可以了, time, space O(1) + +#### DP +- 正规地找规律做, 就跟 coins in a line 一样, 按照先手后手来做 +- 可以rolling array 优化空间 +- Time O(n), 当然啦, 这个题目这样会timeout, 可以使用brainteaser的做法写出结果. + + + +--- + +**28. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**29. [Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)** Level: Medium Tags: [Array, DP, Greedy] + + +给出步数,看能不能jump to end. + +#### Greedy - start from index = 0 +- Keep track of farest can go +- 一旦 farest >= nums.length - 1, 也就是到了头, 就可以停止, return true. +- 一旦 farest <= i, 也就是说, 在i点上, 已经走过了步数, 不能再往前跳, 于是 return false +- This can be done using DP. However, greedy algorithm is fast in this particular problem. + +#### Greedy - start from index = n - 1 +- greedy: start from end, and mark last index +- loop from i = [n - 2 -> 0], where i + nums[i] should always >= last index +- check if last == 0 when returning. It means: can we jump from index=0 to the end? +- Time: O(n), beat 100% + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (A[j] >= i - j), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- Time: O(n^2) + + + + +--- + +**30. [Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change%202.java)** Level: Medium Tags: [Backpack DP, DP] + + +给串数字, target amount, 求总共多少种方式可以reach the amount. + +#### DP +- O(MN): M, total target amount; N: size of coins +- 类似于: 网格dp, unique path 里面的2种走法: 从上到下, 从左到右 +- 状态: dp[i]: sum of ways that coins can add up to i. +- Function: dp[j] += dp[j - coins[i]]; +- Init: dp[0] = 1 for ease of calculation; other dp[i] = 0 by default +- note: 避免重复count, 所以 j = coins[i] as start +- 注意 coins 需要放在for loop 外面, 主导换coin的流程, 每个coin可以用无数次, 所以在每一个sum value上都尝试用一次每个coin + +#### knapsack problem: backpack problem + + + +--- + +**31. [Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + +time: O(nm), m = # of colors +space: O(nm) + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, 但是不知道最后一个房子选什么颜色, 那么就遍历最后一个房子(i - 1)的颜色 +- 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- 考虑DP最后一个位置的情况(颜色选择):需要附带颜色status在DP[i]上: 定义二维数组, 其中一位是status +- dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. +- dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- 然后选ith 房子的 color, 再选(i-1)th 房子的color. Double for loop, skip same color + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 + + + +--- + +**32. [Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Partition DP +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- 确定末尾的2种状态: single letter or combos. 然后计算出单个letter的情况, 和双数的情况 +- 定义`dp[i] = 前i个digits最多有多少种decode的方法`. new dp[n + 1]. +- 加法原理: 把不同的情况, single-digit, double-digit 的情况加起来 +- dp[i] += dp[i - x], where x = 1, 2 +- note: calculate number from characters, need to - '0' to get the correct integer mapping. +- 注意: check value != '0', 因为'0' 不在条件之中(A-Z) +- Space, Time O(n) + +#### 引申 +- 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + + + +--- + +**33. [Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +找连续的持续上升子序列的长度. + +#### Coordinate DP +- 1D coordinate, dp 的角标, 就是代表 index i 的状态 +- 求最值, dp[i] = 在index i位置的最长子序列 +- 如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1 +- 如果没有持续上升, 那么dp[i] = 1, 重头来过 +- maintain max + +#### Basic +- 用一个数存current count, maintain max + + + +--- + +**34. [Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +#### DP +- Time, Space O(MN) +- 往右下角走, 计算最短的 path sum. 典型的坐标型. +- 注意: init 第一行的时候, 要accumulate dp[0][j - 1] + grid[i][j], 而不是单纯assign grid[i][j] + +#### Rolling Array +- Time O(MN), Space O(1) +- 需要在同一个for loop里面完成initialization, 和使用dp[i][j] +- 原因: dp[i % 2][j] 在被计算出来的时候, 是几乎马上在下一轮是要被用的; 被覆盖前不备用,就白算 +- 如果按照第一种方法, 在开始initialize dp, 看起来固然简单, 但是不方便空间优化 + + + +--- + +**35. [Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)** Level: Medium Tags: [Bit Manipulation, Bitwise DP, DP] + + +给一个数组, 算里面有多少bit 1. + +#### Bitwise DP +- 对于每一个数字, 其实很简单就能算出来: 每次 >>1, 然后 & 1 就可以count 1s. Time: 一个数字可以 >>1 O(logN) 次 +- 现在要对[0 ~ num] 都计算, 也就是N个数字, 时间复杂度: O(nLogN). +- 用DP来优化, 查找过的number的1s count, 存下来在 dp[number]里面. +- 计算你顺序从 0 -> num, count过的数字就可以重复利用. +- Bit题目 用num的数值本身表示DP的状态. +- 这里, dp[i] 并不是和 dp[i-1]有逻辑关系; 而是dp[i] 和dp[i>>1], 从binary representation看出有直接关系. + + + +--- + +**36. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**37. [House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) or rolling array O(1) + +搜刮房子, 相邻的不能碰. 每个房子里有value, 求max. + +#### Sequence DP +- dp[i]: 前i个房子拿到的max gain +- 看最后结尾状态的前一个或前两个的情况,再综合考虑当下的 +- 搞清楚当下[i]的和之前[i-x]的情况的关系: 不可以连着house, 那么就直接考虑 dp[i-2]的情况 +- Sequence DP, new dp[n + 1]; + +#### Rolling Array +- [i]'只和前两个位子 [i-1], [i - 2]'相关 +- 用%2来标记 [i], [i - 1], [i - 2]三个位置. +- 其他滚动时惯用curr/prev来表示坐标, 这里%2虽然抽象, 但是更加实用. + + + + +--- + +**38. [House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)** Level: Medium Tags: [DP, Sequence DP, Status DP] + + +和House Robber I 类似, 搜刮房子, 相邻不能动. 特点是: 现在nums排成了圈, 首尾相连. + +#### Sequence DP +- dp[i][status]: 在 status=[0,1] 情况下, 前i个 房子拿到的 max rob gain. status=0, 1st house robbed; status=1, 1st house skipped +- 根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]); +- 特别的是,末尾的last house 和 first house相连. 这里就需要分别讨论两种情况: 第一个房子被搜刮, 或者第一个房子没被搜刮 +- be careful with edge case nums = [0], only with 1 element. +- Time,space: O(n) + +#### 两个状态 +- 是否搜刮了第一个房子, 分出两个branch, 可以看做两种状态. +- 可以考虑用两个DP array; 也可以加一dp维度, 补充这个状态. +- 连个维度表示的是2种状态(1st house being robbed or not); 这两种状态是平行世界的两种状态, 互不相关. + +#### Rolling array +- 与House Robber I一样, 可以用%2 来操作rolling array, space reduced to O(1) + + + +--- + +**39. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**40. [Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + +time: O(NK^2): +space: (NK) + +一排n个房子, 每个房子可涂成k种颜色, 涂每个房子的价钱不一样, 用costs[][]表示. + +costs[0][1]表示涂了index是0的房子, 用了color 1. + +规则: 相邻的两个房子不能使同一种颜色 + +求: 最少的cost + +#### DP +- 跟Paint House I 几乎一模一样, 只不过paint color更多了: k colors. +- 先考虑单纯地用dp[i]表示涂前 i 个房子的最小cost +- 但是 dp[i] 和 dp[i-1] 两个index选什么颜色会互相影响, 难讨论, 于是加状态: 序列DP被加了状态变成2D. +- 考虑最后位, 而前一位i-1又被i位的颜色限制, 于是在考虑 min dp[i] 时候, 又多了一层iteration. +- 做dp[i][j]: # cost for 前 i 个房子, 所以要先pick (i-1) 房子的cost, 然后在找出 (i-2)房子的cost +- K种颜色 => O(NK^2) +- 如果不优化, 跟Paint House I 几乎是一模一样的代码 +- Time O(NK^2), space(NK) +- Rolling array: reduce space to O(K) + +#### 注意 +- 序列型dp[i]表示'前i-1个'的结果. 所以dp最好设定为 int[n + 1] size. +- 然而, 颜色在这里是状态, 所以保留在 j: [ 0~k) +- [[8]] 这样的edge case. 跑不进for loop, 所以特殊handle. + +#### Optimization Solution +- Time: O(NK) +- 如果已知每次都要从cost里面选两个不同的最小cost,那么先把最小两个挑出来, 就不必有第三个for loop 找 min +- 每次在数列里面找: 除去自己之外的最小值, 利用最小值/次小值的思想 +- 维持2个最值: 最小值/次小值. +- 计算的时候, 如果除掉的不是最小值的index, 就给出最小值; 如果除掉的是最小值的index, 就给出次小值. +- Every loop: 1. calculate the two min vlaues for each i; 2. calcualte dp[i][j] +- 如何想到优化: 把表达式写出来, 然后看哪里可以优化 +- 另外, 还是可以rolling array, reduce space complexity to O(K) + + + +--- + +**41. [Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)** Level: Hard Tags: [Array, DP, Sequence DP] + + +比stock II 多了一个限制:只有2次卖出机会. + +#### DP加状态 +- 只卖2次, 把买卖分割成5个状态模块. +- 在状态index 0, 2, 4: 没有持有股票. 1. 一直在此状态, max profit不变; 2. 刚卖掉, dp[i][前状态] + profit +- 在状态index 1, 3: 持有股票. 1. 一直在此状态, daily profit. 2. 刚刚买进, 状态改变, 但是没有profit yet: dp[i][前状态] + +##### Partial profit +- 把每天的partial profit (diff)加在一起, 最终的overall profit是一样的. 唯一更好的是, 不需要记录中间买入的时间点. +- 什么时候会积累profit呢? +- 1. 原本就持有股票的, 如果毫无动作, 那么状态不变, 积累profit diff. +- 2. 卖出了股票, 状态改变, 积累profit diff. +- 注意: 只有在状态index: 0, 2, 4, 也就是卖掉股票的时候, 才可以积累profit + +##### Rolling Array +- [i] 只有和 [i-1] 打交道, reduce space +- O(1) space, O(n) time + +#### 找峰头 +- 找峰头;然后往下再找一个峰头。 +- 怎么样在才能Optimize两次巅峰呢?从两边同时开始找Max!(棒棒的想法) +- leftProfit是从左往右,每个i点上的最大Profit。 +- rightProfit是从i点开始到结尾,每个点上的最大profit. +- 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 +- 三个O(n),还是O(n) + + + +--- + +**42. [Best Time to Buy and Sell Stock IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20IV.java)** Level: Hard Tags: [DP, Sequence DP] + + +有int[] price of stock, 最多做 k transactions. 求最大profit. + +#### DP +- 根据StockIII, 不难发现StockIV就是把状态划分为2k+1份. 那么同样的代码, 移植. + +##### 注意1: +- 如果k很大, k>n/2, 那么长度为n的数组里面, 最多也只能n/2个transaction +- 那么题目简化为stockII, 给n数组, 无限次transaction. +- 注意, status的数量是 2k+1 +- Time O(NK), Space O(2k+1) to store the status + +##### 注意2: +- 最后状态是'没有stock'的都该考虑, 做一个 for 循环比较max. +- 当然, 来一个profit variable, 不断比较, 也是可以的. + +#### 方法2 +- (previous notes, 熟练第一种方法的思考就可以) +- 记得要理解:为什么 i-1天的卖了又买,可以和第 i 天的卖合成一次交易? +- 因为每天交易的price是定的。所以卖了又买,等于没卖!这就是可以合并的原因。要对价格敏感啊少年。 +- Inspired from here: http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html + +##### 局部最优解 vs. 全局最优解: +- local[i][j] = max(global[i – 1][j – 1] + diff, local[i – 1][j] + diff) +- global[i][j] = max(global[i – 1][j], local[i][j]) +- local[i][j]: 第i天,当天一定进行第j次交易的profit +- global[i][j]: 第i天,总共进行了j次交易的profit. + +- local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生。 +- 当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff; +- 当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。 +- (Note:在我下面这个solution里面没有省去 +diff) + +- global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况: +- 如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j]; +- 如果第i天有交易(卖出),那么global[i][j]=local[i][j]。 + + + + + +--- + +**43. [Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)** Level: Hard Tags: [Binary Search, Coordinate DP, DP] + + +俄罗斯套娃, 这里用envelope来表现. 给一串array, 每一个[x, y] 是envelope 长宽. [[5,4],[6,4],[6,7],[2,3]]. + +看用这些套娃, 可以最多套几个. + +#### DP: 1D Coordinate +- envelopes没有顺序, 先排序 (主要根据第一个index排序) +- 然后观察: 排序过后, 就变成了1D的坐标动态规划. +- max number 取决于上一个成功Russian doll的 max value + 1 +- 上一个index不知道, 所以遍历找上一个index. +- 当下index i 的状态, 取决于前面index j 的状态, 所以遍历两个index. +- O(n^2)的DP, n = envelopes.length; + +#### DP: 2D Coordinate +- 这个方法是自己想出来的, 但是时间复杂度太大, timeout +- 把envelop标记在2D grid上面, 然后像走机器人一样, 求到最右下角的最大 count max. +- count 当下能存在多少Russian doll +- 两种情况: 当下coordinate 没有target, 当下coordinate有target +- 当下coordinate 没有target: 如同机器人走法, Math.max(dp[i - 1][j], dp[i][j - 1]) +- 当下coordinate 有target: dp[i - 1][j - 1] + dp[i][j] +- timeout: O(n^2), n = largest coordinate. + + + + +--- + +**44. [Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 背包有自己的大小M, 看最多能放多少重量的书? + +#### Backpack DP 1 +- 简单直白的思考 dp[i][m]: 前i本书, 背包大小为M的时候, 最多能装多种的书? +- **注意**: 背包问题, 重量weight一定要是一维. +- dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + A[i - 1]); +- 每一步都track 最大值 +- 最后return dp[n][m] +- 时间空间 O(mn) +- Rolling array, 空间O(m) + +#### Backpack DP 2 +- true/false求解, 稍微曲线救国: 重点是, 最后, 按照weight从大到小遍历, 第一个遇到true的, index就是最大值. +- 考虑: 用i个item (可跳过地取), 是否能装到weight w? +- 需要从'可能性'的角度考虑, 不要搞成单一的最大值问题. +- 1. 背包可装的物品大小和总承重有关. +- 2. 不要去找dp[i]前i个物品的最大总重, 找的不是这个. + dp[i]及时找到可放的最大sum, 但是i+1可能有更好的值, 把dp[i+1]变得更大更合适. + +##### 做法 +- boolean[][] dp[i][j]表示: 有前i个item, 用他们可否组成size为j的背包? true/false. +- (反过来考虑了,不是想是否超过size j, 而是考虑是否能拼出exact size == j) +- **注意**: 虽然dp里面一直存在i的位置, 实际上考虑的是在i位置的时候, 看前i-1个item. + +##### 多项式规律 +- 1. picked A[i-1]: 就是A[i-1]被用过, weight j 应该减去A[i-1]. 那么dp[i][j]就取决于dp[i-1][j-A[i-1]]的结果. +- 2. did not pick A[i-1]: 那就是说, 没用过A[i-1], 那么dp[i][j]就取决于上一行d[i-1][j] +- dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]] + +##### 结尾 +- 跑一遍dp 最下面一个row. 从末尾开始找, 最末尾的一个j (能让dp[i][j] == true)的, 就是最多能装的大小 :) +- 时间,空间都是:O(mn) + + + + +--- + +**45. [Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 每本书有value int[] V + +背包有自己的大小M, 看最多能放多少value的书? + +#### Backpack DP +- 做了Backpack I, 这个就如出一辙, 只不过: dp存的不是max weight, 而是 value的最大值. +- 想法还是,选了A[i - 1] 或者没选A[i - 1]时候不同的value值. +- 时间空间O(mn) +- Rolling Array, 空间O(m) + +#### Previous DP Solution +- 如果无法达到的w, 应该mark as impossible. 一种简单做法是mark as -1 in dp. +- 如果有负数value, 就不能这样, 而是要开一个can[i][w]数组, 也就是backpack I 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**46. [Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)** Level: Medium Tags: [Backpack DP, DP] + + +#### Backpack DP +- 与背包1不同: 这里不是check可能性(OR)或者最多能装的size是多少; 而是计算有多少种正好fill的可能性. +- dp[i][w]: 用前i本书, 正好fill到 w weight的可能性. +- 对于末尾, 还是两种情况: +- 1. i-1位置没有加bag +- 2. i-1位置加了bag +- 两种情况可以fill满w的情况加起来, 就是我们要的结果. +- 如常: dp[n + 1][w + 1] +- 重点: dp[0][0] 表示0本书装满weight=0的包, 这里我们必须 dp[0][0] = 1, 给后面的 dp function 做base +- Space, time: O(MN) +- Rolling array, 空间优化, 滚动数组. Space: O(M) + +#### 降维打击, 终极优化 +- 分析row(i-1)的规律, 发现所有row(i)的值, 都跟row(i-1)的左边element相关, 而右边element是没用的. +- 所以可以被override. +- Space: O(M), 真*一维啊! +- Time: O(MN) + + + +--- + +**47. [Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)** Level: Hard Tags: [DP, Enumeration, Partition DP] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +其中字符可能是 "*", 可以代表 [1 - 9] + +#### DP +- 乘法原理 +- 跟decode way I 一样, 加法原理, 切割点时: 当下是取了 1 digit 还是 2 digits 来decode +- 定义dp[i] = 前i个digits最多有多少种decode的方法. new dp[n + 1]. +- 不同的情况是: 每一个partition里面, 如果有"*", 就会在自身延伸出很多不同的可能 +- 那么: dp[i] = dp[i - 1] * (#variations of ss[i]) + dp[i - 2] * (#variations of ss[i,i+1]) + +##### 特点 +- 枚举的能力: 具体分析 '*' 出现的位置, 枚举出数字, 基本功. +- 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) +- 理解取MOD的原因: 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. +- 枚举好以后, 其实这个题目的写法和思考过程都不难 + + + + +--- + +**48. [Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)** Level: Hard Tags: [DP, Partition DP] + + +给一个String s, 找出最少用多少cut, 使致 切割出的每一个substring, 都是palindrome + +#### Partition DP +- Find minimum cut: 分割型DP +- dp[i]: 最少cut多少刀, 使得前 i 长度的string, 割出来都是palindrome +- 最终要得到 dp[n], 所以 int[n + 1] +- 移动切刀, 看在哪里切, index j in [0 ~ i] +- 考虑[j, i - 1] 是否是回文串, 如果是, 那么: dp[i]= min(dp[i], d[j] + 1). +- note: 估计遍历 j 的时候, 反过来遍历也可以. + +#### 计算Palindrome的优化 +- 利用palindrome的性质, 可以算出 boolean palindrome[i, j]的情况. +- 找一个任意mid point: +- 1. 假设palindrome是奇数长度, 那么 mid 是单独的字符, 而两边的字符 [mid-1], [mid+1] 应该完全相等. +- 2. 假设palindrome是偶数长度, 那么 [mid] 和 [mid + 1] 这样位置的字符应该相等. +- 这样做出来 palindrome[i, j]: 从字符 i 到 字符 j 的 substring 是否是 palindrome +- 这样就给我们的问题合理降维, 目前是time: O(n^2). +- 不然求一次palindrome, 就是n, 会变成O(n^3) + +#### Previous Notes +- Double for loop 检查每种substring string (i~j). 若i,j相邻或者同点,那么肯定isPal;否则,i,j之间的(i+1, j-1)一定得isPal。 +- 看上去,在检查i,j的时候,中间按的(i+1, j-1)怎么可能先知道? 其实不然..在j慢慢长大的时候,所有的0~j的substring都检查过。所以isPal[i+1][j-1]一定是已经知道结果的。 +- okay.那么假如以上任意一种情况成立,也就是说isPal[i][j] == true。那就要判断,切到第一层循环参数j的末尾点时,有多少种切法? +- 想法很顺:我们naturally会想到,把i之前的cut加上i~j之间发生的不就好了。 +- 反正现在j不变,现在就看吧i定在哪里,cut[i - 1]是否更小/最小; 再在cut[i-1]基础上+1就完了。 +- 当然,如果i==0, 而 i~j又是isPal,那没啥好谈的,不必切,0刀。 +- 最终,刷到cut[s.length() - 1] 也就是最后一点。 return的理所应当。 + + + + +--- + +**49. [Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)** Level: Hard Tags: [Backpack DP, DP] + + +给n种不同的物品, int[] A weight, int[] V value, 每种物品可以用无限次 + +问最大多少value可以装进size是 m 的包? + +#### DP +- 可以无限使用物品, 就失去了last i, last unique item的意义: 因为可以重复使用. +- 所以可以转换一个角度: +- 1. 用i **种** 物品, 拼出w, 并且满足题目条件(max value). 这里因为item i可以无限次使用, 所以考虑使用了多少次K. +- 2. K虽然可以无限, 但是也被 k*A[i]所限制: 最大不能超过背包大小. +- dp[i][w]: 前i种物品, fill weight w 的背包, 最大价值是多少. +- dp[i][w] = max {dp[i - 1][w - k*A[i-1]] + kV[i-1]}, k >= 0 +- Time O(nmk) +- 如果k = 0 或者 1, 其实就是 Backpack II: 拿或者不拿 + +#### 优化 +- 优化时间复杂度, 画图发现: +- 所计算的 (dp[i - 1][j - k*A[i - 1]] + k * V[i - 1]) +- 其实跟同一行的 dp[i][j-A[i-1]] 那个格子, 就多出了 V[i-1] +- 所以没必要每次都 loop over k times +- 简化: dp[i][j] 其中一个可能就是: dp[i][j - A[i - 1]] + V[i - 1] +- Time O(mn) + +#### 空间优化到1维数组 +- 根据上一个优化的情况, 画出 2 rows 网格 +- 发现 dp[i][j] 取决于: 1. dp[i - 1][j], 2. dp[i][j - A[i - 1]] +- 其中: dp[i - 1][j] 是上一轮 (i-1) 的结算结果, 一定是已经算好, ready to be used 的 +- 然而, 当我们 i++,j++ 之后, 在之前 row = i - 1, col < j的格子, 全部不需要. +- 降维简化: 只需要留着 weigth 这个 dimension, 而i这个dimension 可以省略: +- (i - 1) row 不过是需要用到之前算出的旧value: 每一轮, j = [0 ~ m], 那么dp[j]本身就有记录旧值的功能. +- 变成1个一位数组 +- 降维优化的重点: 看双行的左右计算方向 +- Time(mn). Space(m) + + + +--- + +**50. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**51. [Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + +O(n)跑2遍for. +O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. +特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 + + + +--- + +**52. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**53. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**54. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**55. [Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)** Level: Easy Tags: [DP, Math, Memoization] + + +#### Memoization +- fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + +#### DP array. +- 滚动数组, 简化DP + +#### recursively calculate +- recursively calculate fib(n - 1) + fib(n - 2). 公式没问题, 但是时间太长, timeout. + + + + +--- + +**56. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + +**57. [Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)** Level: Medium Tags: [Array, Backpack DP, DP] + + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + +#### Backpack DP +- 计数问题, 可以想到DP. 其实就是Backpack VI. +- 从x个数字里面找candidate(可以重复用同一个数字), 来sum up to target. 找: # of ways to form the sequence. +- Backpack VI: 给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法 +- dp[i]: # of ways to build up to target i +- consider last step: 如果上一步取的是 candidate A, 那么就该加到dp[i]: +- dp[i] += dp[i - A] +- 要找overall dp[i], 就做一个for loop: dp[i] = sum{dp[i - num]}, where for (num: nums) +- Time: O(mn). m = size of nums, n = target +- If we optimize dp for loop, 需要Sort nums. O(mlogm). will efficient 如果m是constant或者relatively small. Overall: O(n) + +#### DFS, backtracking +- 尽管思考方式是对的, 但是 times out +- 可以重复使用数字的时候, 比如用1 来拼出 999, 这里用1就可以走999 dfs level, 不efficient + + + +--- + +**58. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**59. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**60. [Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)** Level: Medium Tags: [Backtracking, DFS, DP] + + +String 只包含 + , - 两个符号. 两个人轮流把consecutive连续的`++`, 翻转成 `--`. + +如果其中一个人再无法翻转了, 另一个人就赢. 求: 给出string, 先手是否能赢. + +#### Backtracking +- curr player 每走一步, 就生成一种新的局面, dfs on this +- 等到dfs结束, 不论成功与否, 都要backtracking +- curr level: 把"++" 改成 "--"; backtrack的时候, 改回 '--' +- 换成boolean[] 比 string/stringBuilder要快很多, 因为不需要重新生成string. +- ++ 可以走 (n - 1)个位置: +- T(N) = (N - 2) * T(N - 2) = (N - 4) * (N - 2) * T(N - 4) ... = O(N!) + +##### iterate based on "++" +- 做一个String s的 replica: string or stringBuilder +- 每次dfs后, 然后更替里面的字符 "+" => "-" +- 目的只是Mark已经用过的index +- 真正的dfs 还是在 original input string s 身上展开 +- 每次都重新生成substring, 并不是很efficient + +##### Game theory +- 保证p1能胜利,就必须保持所有p2的move都不能赢 +- 或者说, 在知道棋的所有情况时, 只要p2有一种路子会输, p1就一定能走对路能赢. +- 同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +- p1: player1, p2: player2 + +#### O(N^2) 的 DP +- 需要Game Theory的功底, Nim game. https://www.jiuzhang.com/qa/941/ +- http://www.1point3acres.com/bbs/thread-137953-1-1.html +- TODO: https://leetcode.com/problems/flip-game-ii/discuss/73954/Theory-matters-from-Backtracking(128ms)-to-DP-(0ms) + + + +--- + +**61. [Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Substring.java)** Level: Medium Tags: [DP, String] + + +给一个string, 找到最长的palindrome substring. + +Related: Longest Palindromic Subsequence, Palindrome Partioning II + +O(n^2) is not too hard to think of. How about O(n)? + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + +#### DP: isPalin[][] +- 穷举double for loop. O(n^2) +- boolean isPalin[i][j], 每次确认有palindrome就记录下来true / false +- 穷举的for loop计算顺序: end point j, and stat point i = [0, j] +- 在计算 isPalin[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- double for loop: O(n^2). slower, because it guarantees O(n^2) due to the for loop + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**62. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + +**63. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**64. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**65. [Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- 就是pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**66. [Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)** Level: Review Tags: [BFS, DFS, DP] + + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- in dfs: remove the incorrect parentheses one at a time +- detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` +- once detected, remove the char from middle of s, and dfs on the rest of the s that has not been tested yet. + +##### Core concept: reverse test +- `if a parenthese string is valid, the reverse of it should also be valid` +- Test s with open='(', close=')' first; **reverse s**, and test it with open=')', close='(' + +##### Minor details +- only procceed to remove invalid parenthese when `count<0`, and also break && return dfs after the recursive calls. +- The above 2 facts eliminates all the redundant results. +- Reverse string before alternating open and close parentheses, so when returning final result, it will return the correct order. +- Open questions: how does it guarantee minimum removals? + +##### Backtracking +- 如果用stringbuffer, 那么久不会每次create new string, 但是需要maintain这个string buffer, 就会backtracking + +##### Complexity +- Seems to be O(n), but need to derive + +#### BFS +TODO + +#### DP + + + +--- + +**67. [Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)** Level: Medium Tags: [DP, Math] + + +具体看题目: count # of valid rectangles (four corner are 1) in a grid[][]. + +#### basic thinking + Math +- Fix two rows and count matching columns +- Calculate number rectangles with `combination` concept: +- total number of combinations of pick 2 points randomly: count * (count - 1) / 2 + +#### DP +- TODO. HOW? + +#### Brutle +- O(m^2 * n^2), times out + + + +--- + +**68. [Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)** Level: Medium Tags: [DP, String] + + +根据题意, count # of palindromic substring. (不同index截取出来的substring算不同的情况) + +#### isPalin[][] +- build boolean[][] to check isPalin[i][j] with DP concept +- check all candidates isPalin[][] +- O(n^2) + +#### odd/even split check +https://leetcode.com/problems/palindromic-substrings/discuss/105689/Java-solution-8-lines-extendPalindrome + + + +--- + +**69. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) + +#### DP +- 最多2个fence 颜色相同 +- 假设i是和 i-1不同,那么结果就是 (k-1)*dp[i - 1] +- 假设i是何 i-1相同,那么根据条件,i-1和i-2肯定不同。那么所有的结果就是(k-1)*dp[i-2] +- dp[i]: count # of ways to paint 前i个 fence +- 加法原理 +- time, space: O(n) +- rolling array: space O(1) + +#### Previous Notes +- 这题目很有意思. 一开始分析的太复杂, 最后按照这个哥们的想法(http://yuanhsh.iteye.com/blog/2219891) 的来做,反而简单了许多。 +- 设定T(n)的做法,最后题目化简以后就跟Fibonacci number一样一样的。详细分析如下。 +- 做完,还是觉得如有神。本来是个Easy题,想不到,就是搞不出。 + + + + +--- + +**70. [Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- true/false problem, think about dp +- 子问题: 前i个字母, 是否可以有valid break +- 检查dp[j] && `if substring(j, i) valid`, for all j = [0 ~ i] +- dp = new boolean[n + 1]; dp[0] = true; +- goal: if there is a j, `dp[j] == true && word[j, n] in dict`. Need iterate over i = [0 ~ n], also j = [0, i] +- 注意, 用set代替list, 因为要用 contains(). + +#### Previous notes +##### 方法2(attempt4 code) +- 与Word BreakII用同样的DP。 +- valid[i]: 记录从i到valid array末尾是否valid. + +##### 方法1:(attempt3 code) +- state,rst[i]: 从[0~i] inclusive的string是否可以在dict中break开来找到? +- function: rst[i] = true if (rst[i - j] && set.contains(s.substring(i - j, i))); j in[0~i] +- 1. rst[i - j] 记录的是[0, i-j]这一段是否可以break后在dict找到。 +- 2. 若true,再加上剩下所有[i-j, i]都能在dict找到,那么rst[i] = rst[0, i - j] && rst[i-j, i] == true +- 优化:找dict里面最长string, 限制j的增大。 + + + + +--- + +**71. [Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### 理解意思是关键 +- 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出 +- 记录每天最小值Min是多少. O(n) +- 每天都算和当下的Min买卖,profit最大多少. + +#### DP +- Find min value for first i items, new dp[n + 1]. +- dp[i]: 前i天, prices最小的price是多少: min cost of first i days +- 然后用当天的price做减法dp[i]算max profit. +- Time, Space: O(n) +- 更进一步, 用一个min来表示min[i], 因为计算中只需要当下的min. + +#### Rolling array +- index i only depend on [i - 2] +- Space O(n) + +#### Brutle Failed +- 每天都试着买进,然后之后的每一天尝试卖出. double for loop, O(n^2). timeout. +- 其中很多都是没必要的计算:[7, 1, 5, 3, 6, 4]。 if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. + + + +--- + +**72. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**73. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**74. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**75. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + + +// 如何想到从中间initialize + + + +--- + +**76. [Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Coordinate DP, DP] + +time: O(n^2) +time: O(n) + +给一串 unsorted sequence, 找到长 increasing subsequence 的个数! + +#### Coordinate DP +- 需要能够判断综合题, 分清楚情况和套路: combination of `longest subsequence` and `ways to do`, as well as global variable. +- len[i] (我们平时的dp[i]): 在前i个元素中, 最长的 increasing subsequence length; +- count[i]: 在前i个元素中, 并且以 len[i]这个长度为准的 subsequence的 count. 或者: 在前i个元素中, ways to reach longest increasing subsequence. +- `len[i] == len[j] + 1`: same length, but different sequence, so add all `count[i] += count[j]` +- `len[i] < len[j] + 1`: 这就是更长的情况找到了, 那么有多少次 count[j] 有多少, count[i] 就有多少. 仔细想sequence: 长度增长了, 但是ways to reach i 没有增长. +- 同样的判断需要用在 maxLen 和 maxFreq上: +- 如果没有增长 maxLen 不变, maxFreq上面需要 +=count[i] (同一种长度, 多了更多的做法) +- 如果maxLen 变长, maxFreq 也就是采用了 count[i] = count[j] +- TODO: Is rolling array possible? + +#### 相关 +- 都是 Coordiate DP, DP的鼻祖家族: +- Longest Increasing Subsequence (跟这道题的一部分一模一样) +- Longest Continuous Increasing Subsequence (连续, 只check dp[i - 1]) +- Longest Increasing Continuous Subsequence I, II (Lintcode, II 是matrix) + + + +--- + +**77. [Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)** Level: Medium Tags: [Coordinate DP, DP, Status DP] + + + +#### DP +- 特点: 上一步可能是swaped也可能是fixed +- 考虑A,B之间的现状: `A[i] > A[i - 1] && B[i] > B[i - 1]` 或者 `A[i] > B[i - 1] && B[i] > A[i - 1]` +- 问题: 如何把这个状态变成合理的strick-increasing状态? +- `A[i] > A[i - 1] && B[i] > B[i - 1]`: 1. 已经合理, 也不动. 2. [i], [i-1] 全部都swap +- `A[i] > B[i - 1] && B[i] > A[i - 1]`, 交错开来, 所以调换[i], 或者[i-1]: 1. 换[i-1]. 2. 换[i] +- 注意因为求min, 所以init value应该是 Integer.MAX_VALUE; + + + +--- + +**78. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**79. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**80. [Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)** Level: Hard Tags: [DP, Hash Table] + + +Frog jump 的题目稍微需要理解: 每个格子可以 jump k-1, k, k+1 steps, 而k取决于上一步所跳的步数. 默认 0->1 一定是跳了1步. + +注意: int[] stones 里面是stone所在的unit (不是可以跳的步数, 不要理解错). + +#### DP +- 原本想按照corrdiante dp 来做, 但是发现很多问题, 需要track 不同的 possible previous starting spot. +- 根据jiuzhang答案: 按照定义, 用一个 map of > +- 每次在处理一个stone的时候, 都根据他自己的 set of , 来走下三步: k-1, k, or k+1 steps. +- 每次走一步, 查看 stone + step 是否存在; 如果存在, 就加进 next position: `stone+step`的 hash set 里面 + +##### 注意init +- `dp.put(stone, new HashSet<>())` mark 每个stone的存在 +- `dp.get(0).add(0)` init condition, 用来做 dp.put(1, 1) + +##### 思想 +- 最终做下来思考模式, 更像是BFS的模式: starting from (0,0), add all possible ways +- 然后again, try next stone with all possible future ways ... etc + + + +--- + +**81. [Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)** Level: Medium Tags: [DP, MiniMax] + + +Detailed in `Coins in a Line III` + + + +--- + +**82. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**83. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + +**84. [Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)** Level: Hard Tags: [DP] + + + + +--- + diff --git a/review/Deque.md b/review/Deque.md new file mode 100644 index 0000000..89742a0 --- /dev/null +++ b/review/Deque.md @@ -0,0 +1,52 @@ + + + +## Deque (2) +**0. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**1. [Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Deque, Monotonous queue +- 维持monotonuous queue: one end is always at max and the other end is min. Always need to return the max end of queue. +- when adding new elements x: start from small-end of the queue, drop all smaller elements and append to first element larger than x. +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`. +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! + + + +--- + diff --git a/review/Design.md b/review/Design.md new file mode 100644 index 0000000..b0602fe --- /dev/null +++ b/review/Design.md @@ -0,0 +1,434 @@ + + + +## Design (21) +**0. [LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)** Level: Hard Tags: [Design, Hash Table] + + +#### Hash Table +- 具体看thoughts, 几种不同的方式使用map +- `regular object map`: map of , where `item : {int val; int count}` +- Use a Map to track the frequency +- Track constant capacity, and minimum frequency +- Every get(): update all frequency map as well +- Every put(): update all frequency map as well, with optional removal (if over capacity) + +- Original post: http://www.cnblogs.com/grandyang/p/6258459.html +- TODO: one doubly linked list might be good enough to replace below: +- `frequency list map`: map of >, where the list preserves `recency` +- `item location in frequency map`: map of : +- index relative to the item in a particular list, not tracking which list here + + + +--- + +**1. [Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)** Level: Medium Tags: [BST, Design, Stack, Tree] + + +画一下, BST in order traversal. 用stack记录最小值, 放在top. O(h) space. +每次消耗TreeNode, 都看看rightNode(其实就是下一个最小的candidate), 并且一条龙stack叠上rightNode所有的left子孙. + +Previous Notes: +用O(h)空间的做法: + +理解binary search tree inorder traversal的规律: + 先找left.left.left ....left 到底,这里是加进stack. + 然后考虑parent,然后再right. + +例如这题: + stack里面top,也就是tree最左下角的node先考虑,取名rst. + 其实这个rst拿出来以后, 它也同时是最底层left null的parent,算考虑过了最底层的parent。 + 最后就考虑最底层的parent.right, 也就是rst.right. + +注意: + next()其实有个while loop, 很可能是O(h).题目要求average O(1),所以也是okay的. + + +用O(1)空间的做法:不存stack, 时刻update current为最小值。 + +找下一个最小值,如果current有right child: + 和用stack时的iteration类似,那么再找一遍current.right的left-most child,就是最小值了。 + +如果current没有right child: + 那么就要找current node的右上parent, search in BinarySearchTree from root. + +注意: + 一定要确保找到的parent满足parent.left == current. + 反而言之,如果current是parent的 right child, 那么下一轮就会重新process parent。 + 但是有错:binary search tree里面parent是小于right child的,也就是在之前一步肯定visit过,如此便会死循环。 + + + + +--- + +**2. [Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, Stack] + + +方法1: 用queue, 把需要的item全部打出来 +方法2: 用stack, 把需要的item先存一行, 每次打开子序列时候, 全部加回stack. + + + +--- + +**3. [Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- HashMap构建Trie. Trie三个Method: +- 1. Inset: 加 word +- 2. Search: 找word +- 3. StartWith: 找prefix + +##### 特点 +- 只有两条children的是binary tree. 那么多个children就是Trie +- 那么没有left/right pointer怎么找孩子? +- 用HashMap,以child的label为Key,value就是child node。 HashMap走位 + +##### 其他 +- node里的char在这是optional. 只要在每个TrieNode里面用map存储向下分布的children就好了. +- 另外有种题目,比如是跟其他种类的search相关,在结尾要return whole string,就可以在node里存一个up-to-this-point的String。 + +##### Previous Note +- 如果是遇到一个一个字查询的题,可以考虑一下。 +- 构建TrieNode的时候要注意:如何找孩子?如果是个map的话,其实就挺好走位的。 +- 而且,每个node里面的 char 或者string有时候用处不大, +- 可以为空。但是有些题目,比如在结尾要return一些什么String,就可以在end string那边存一个真的String。 + + + + + +--- + +**4. [Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +Trie结构, prefix tree的变形: '.'可以代替任何字符,那么就要iterate这个node所有的children. + +节点里面有char, isEnd, HashMap +Build trie = Insert word:没node就加,有node就移动。 +Search word:没有node就报错. 到结尾return true + +这题因为'.'可以代替任何possible的字符,没一种都是一个新的path,所以recursive做比较好些。 +(iterative就要queue了,麻烦点) + + + +--- + +**5. [Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)** Level: Easy Tags: [Design, Stack] + + +双Stack:一个正常stack,另一个minStack存当下level最小值. 注意维护minStack的变化 + +另外. 如果要maxStack,也是类似做法 + + + +--- + +**6. [Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)** Level: Easy Tags: [Design, Stack] + + +#### 双Stack +画图, 知道最后maintain的stack是那个 reverseStack: pop(), peek(), empty() 都在这个stack上, 无需变换. +push()里面做stack和reverseStack的来回倾倒. +相比老的code, 在PUSH里面做倾倒, 更容易读. + +#### Previous notes +双Stack. 一个是等于是queue,一个是backfillStack. +Tricky: 是在pop()和peek()的时候backfill, 并且要等到stack用完再backfill. +写一下例子就知道,如果提早backfill,stack.peek()就不是queue的head了. + + + + +--- + +**7. [Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)** Level: Easy Tags: [Design] + + +让一个class 是 singleton + + + +--- + +**8. [Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)** Level: Easy Tags: [Design, Stack] + + +如题. + +#### Queue, 倒水 +- 两个Queue,交互倒水 +- 用一个Temp做swap + +##### 做法1 +- 逻辑在push里面: +- 1. x 放q2。 +- 2. q1全部offer/append到q2. +- 3. 用一个Temp做swap q1, q2. +- q1的头,就一直是最后加进去的值. + + +##### 做法2 +- 逻辑在top()/pop()里, 每次换水,查看末尾项. + + + + +--- + +**9. [LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)** Level: Hard Tags: [Design, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度。 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) + +##### 巧妙点 +- 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +- 2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +- 直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法: +- moveToHead() +- insertHead() +- remove() + +#### O(n) 检查重复 +- timeout method, 天真的来了一个O(n) 的解法,结果果然timeout. +- 一个map存数值。一个queue来存排位。 +- 每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + + + + +--- + +**10. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**11. [Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)** Level: Medium Tags: [Design, Hash Table] + + + +给一个string[] dict, 和一个word. + +每个word都可以缩写成固定的abbreviation ``(详细看原题) + +检查input word是否满足unique + +#### HashMap +- 简单算出abbreviatioin +- 检查abbr是否存在; 如果存在, 是不是input word本身. + + + +--- + +**12. [ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)** Level: Medium Tags: [Design, Hash Table] + + +#### basic implementation +- 用HashMap, 理解题目规律,因为重复的计算可以被覆盖,所以是个优化题。没有什么太大的悬念和意义. +- 消灭重合点: +- 如果process当下col, 其实要减去过去所有加过的row的交接点。。。 +- 再分析,就是每次碰到row 取一个单点, sumRow += xxx。 +- 然后process当下col时候, sum += colValue * N - sumRow. 就等于把交叉所有row(曾经Process过的row)的点减去了。很方便。 +- 最后read in 是O(P), process也是O(P). + + + + +--- + +**13. [Peeking Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Peeking%20Iterator.java)** Level: Medium Tags: [Design] + + +#### Use concept pre cache +- 找一个cache来存next()的值, 也就是: next value的值提前存在cache里面 +- 因此peek()的时候, 就可以直接return cache, 而不用做 itt.next() +- 然后每次真的next()的时候, 里取下一个itt.next()维护这个cache + +#### Previous notes +- 再一次理解错题意. peek() 就是头顶,但是不一定是最大值啊。 +- 总是把PEEK想成了最大值,然后用2 STACK做了最大值的cache,练的一手好双stack,可惜错了。 + + + + +--- + +**14. [Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)** Level: Hard Tags: [Design, Geometry, Hash Table] + + +看的list of coordinates 是否能组成perfect rectangle, 并且不允许overlap area. + +#### 画图发现特点 +- 特点1: 所有给出的点(再找出没有specify的对角线点), 如果最后组成perfect rectangle, 都应该互相消除, 最后剩下4个corner +- 特点2: 找到所有点里面的min/max (x,y), 最后组成的 maxArea, 应该跟过程中accumulate的area相等 +- 特点1确保中间没有空心的部分, 保证所有的重合点都会互相消除, 最后剩下4个顶点 +- 特点2确保没有重合: 重合的area会最终超出maxArea + + + +--- + +**15. [Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + +time: O(1) avg +space: O(n) + +#### Hash Table +- 用`map 来track value->index`, 用`list track index->value` +- map查看value是否存在 +- list maintain 用来 insert/remove/random operations. +- 特点: 一旦remove, 换到list结尾然后 `list.remove(list.size() - 1)`, 这样remove的cost更低. +- list.remove(object) 应该是要O(logn) 做一个search的. + + + +--- + +**16. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**17. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**18. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + +**19. [Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison + + + +--- + +**20. [Flatten 2D Vector.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%202D%20Vector.java)** Level: Medium Tags: [Design] + + +Implement an iterator to flatten a 2d vector. + +Just move pointers carefully with next(), hashNext() + +#### Basic Implementation using x, y corrdinate +- 就是把2D list里面的element全部遍历一遍。 +- 跟一个nxn的matrix遍历,是没区别的拉; 所有来个x,y,把2d list跑一变。 + +#### Always return item at index 0, and remove from list? +- list 方便remove, 考虑吧reduce input vector (就像给的是linked list 一样) + + + +--- + diff --git a/review/Divide and Conquer.md b/review/Divide and Conquer.md new file mode 100644 index 0000000..1dc1140 --- /dev/null +++ b/review/Divide and Conquer.md @@ -0,0 +1,848 @@ + + + +## Divide and Conquer (35) +**0. [Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer] + + +#### Vote 计数 +- vote++, vote--到最后剩下的就是winner. Time O(n), Space O(1) +- Majority Number是指超半数. 超半数的数字, 最后都会至少有vote>=1: match current majority number,vote++;if not, vote--. +- 注意:assume valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 + +#### HashMap count occurance +- Time, Space: O(n) + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**1. [Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +如题, build balanced BST from sorted array + +#### DFS +- Binary Search Tree特点: 左边的node都比右边的node小. +- height balance, subtree height 相差<1, 必须左右sub tree均分. 做DFS(num, start, end) +- 在每一个level, 找到中间点, 然后分割2半, 继续dfs +- Divide and Conquer +- time/space: O(n), visit all nodes, no redundant visits. + + + +--- + +**2. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**3. [Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +给一个特殊的binary tree, treeNode里面有一个 next pointer. + +写一个function, 把所有node都更同level的node 连在一起. 最右边的node.next = NULL + +#### DFS + Divide and Conquer +- 题目要求DFS. 想清楚了如何在DFS level把几种情况都考虑了, 写起来很简单. NOT BFS, because requires O(1) space +- 对于一个root来说, 只有几个点可以顾忌到: root.left, root.right, root.next. +- 想办法把这三个方向的点, 能连起来的都连起来: +- 1. `node.left.next = node.right` +- 2. If `node.next != null`, link `node.right.next = node.next.left`; +- 然后在dfs(root.left), dfs(root.right) +- Time: visit && connect all nodes, O(n) + +#### BFS +- 不和题意,用了queue space,与Input成正比。太大。 +- BFS over Tree。 用Queue 和 queue.size(),老规矩。 +- process每层queue时, 注意把next pointer加上去就好. + + + +--- + +**4. [Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +如题, 验证是否是BST. + +#### DFS +- 查看每个parent-child关系: leftchild < root < rightChild; +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest +- imagine we know the two extreme border: Integer.MIN_VALUE, Integer.MAX_VALUE; pass node around and compare node vs. node.parent. +- 方法: 把root.val 传下来作为 max 或者 min, 然后检查children +- + +##### Note: +- min/max需要时long type. +- 如果题目真的给node.val = Integer.MAX_VALUE, 我们需要能够与之比较, long就可以. + + + +--- + +**5. [Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List] + + +如题, 把一个sorted singly linked list 转换成一个 height balanced BST + +#### DFS +- Divide and Conquer +- 找到mid node +- 然后分割两半, 分别dfs做各自两个subtree: node.left,node.right +- 用长度来定位mid, 每次找中间点做root, 然后前半段, 后半段分别dfs with length. +- 用快慢pointer 找到mid. Better: 不用traverse entire linked list + +#### Details +- slowPointer = node; +- fastPointer = node.next; +- 然后把root = mid.next +- 然后开始sortedListToBST(mid.next.next); //后半段 +- mid.next = null;//非常重要,要把后面拍过序的断掉 +- sortedListToBST(head); //从头开始的前半段 +- 最后root.left, root.right merge一下。 + + + +--- + +**6. [Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)** Level: Medium Tags: [DFS, Divide and Conquer, Stack] + + +给一个expression string. 里面包括数字, 字母, 括号. 其中数字代表括号里面的内容重复几次. + +括号里面可以是String, 也可能是expression. + +目的: 把expression展开成一个正常的String. + + +#### Stack, Iteratively +- Process inner item first: last come, first serve, use stack. +- Record number globally and only use it when '[' is met. +- Stack存 [ ] 里面的内容, detect 括号开头结尾: 结尾时process inner string +- 有很多需要注意的细节才能做对: +- Stack 也可以用, 每个地方要注意 cast. 存进去的需要是Object: String, Integer +- 几个 type check: instanceof String, Character.isDigit(x), Integer.valueOf(int num) +- 出结果时候: `sb.insert(0, stack.pop())` + + +#### DFS +- Bottom->up: find deepest inner string first and expand from inside of `[ ]` +- 与Stack时需要考虑的一些function类似. 特别之处: **检查`[ ]`的结尾** +- 因为DFS时候, 括号里的substring会被保留着进入下一个level, 所以我们在base level要keep track of substring. +- 用int paren 来track 括号的开合, 当paren再次==0的时候 找到closure ']' +- 其他时候, 都要继续 append to substring + + + + +--- + +**7. [Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)** Level: Hard Tags: [Binary Search, DFS, Divide and Conquer] + + +2Dmatrix, 里面的value有一些递增, 递减的特点(细节比较长, 看原题). 目标是找到peak element + +peak: 比周围4个方向的点value大 + +#### DFS + +##### 基本原理 +- 我们不可能一口气准确定位(x,y), 但是我们可以再一个row/col里面, 找到1D array的 peak. +- 根据这个点, 再往剩下两个方向移动 +- 1. 在中间的一行i=midX, 找到peak所在的y. +- 2. 在中间的一列j=midY, 找到peak所在的x. (有可能强势override之前找到的y, 也就是放弃那一行的peak, 在midY上找peak) +- 3. 根据 (x,y) 的4个neighbor check (x,y)是不是 peak, 如果不是, 像更高的位置移动一格 +- 4. 根据之前算的 midX, midY 把board分成4个象限, 在每一份里面再继续找 +- 这个题目LintCode不给做了, 所以思路对的, 但是解答还没有再次验证. + +##### 剪枝/切分象限 +- 每次只是找到一个row/col里面的peak而已! +- 找到这个点, 就等于把board切成了两半. +- 然后, 再跟剩下的相邻的两个位置比较, 就知道了哪里更大, 就去哪里找peak, 也就是又切了第二刀. +- 切第二刀的时候, 也要把(x, y) 移到需要取的象限. 进行DFS +- 根据mid row 切割: +- http://www.jiuzhang.com/solution/find-peak-element-ii/#tag-highlight-lang-java +- http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf + +##### 时间复杂度 +- 每一个level都减一半 +- T(n) = n + T(n/2) = n + n/2 + n/4 + ... + 1 = n(1 + 1/2 + .... + 1/n) = 2n = O(n) + +#### Binary Search +- TODO +- O(nLogN) + + + +--- + +**8. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**9. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**10. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**11. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**12. [Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +找到binary tree 里的最长 consecutive sequence. + +#### DFS +- Divide and Conquer. dfs +- 分开 看左边/右边 +- 如果左边满足连续递增的规则, dfs (depth + 1), 如果不满足规则, dfs(depth = 1) +- 右边也是一样 +- 对结果跟max作比较, return + + + +--- + +**13. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**14. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**15. [Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**16. [Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 + + + +--- + +**17. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**18. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**19. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**20. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**21. [Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)** Level: Medium Tags: [DFS, Divide and Conquer] + + +如题: Calculate the a^n % b where a, b and n are all 32bit integers. + +#### Divide and Conquer +- a^n可以被拆解成(a*a*a*a....*a), 是乘机形式,而%是可以把每一项都mod一下的。所以就拆开来take mod. +- 这里用个二分的方法,recursively二分下去,直到n/2为0或者1,然后分别对待. +- 注意1: 二分后要conquer,乘积可能大于Integer.MAX_VALUE, 所以用个long. +- 注意2: 要处理n%2==1的情况,二分时候自动省掉了一份 a,要乘一下。 + + + + +--- + +**22. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**23. [Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)** Level: Medium Tags: [Binary Search, Divide and Conquer] + + +给matrix, 每一行sorted, 每一列从上往下sorted, 找target是否存在 + +#### Binary Search +- 根据给定的性质, 其实点选的极端一点: x = 最下面的row, y = 当下一行里面最小的left position. +- (x,y)在左下角 +- 在此情况下, 只能往一个方向运行: 如果小于target, y++; 如果大于target, 那么只能x-- +- 每次操作, 都是删掉一行, 或者一列, 再也不需要回头看 +- `while (x >= 0 && y < col) {}` 确保不会跑脱 +- 同样的方式: 可以从右上角(0, col - 1) 开始, 代码稍微改一改 + +#### Divide and Conquer? +- TODO + + + +--- + +**24. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**25. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**26. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**27. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**28. [Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + +time: O(n) +space: O(n) + +给一个tree, 按照题意找最一个node满足: +1. 这个node的subtree涵盖最深level的所有leaves. +2. 这个node必须是能找到的最deep那个 + +条件2的需求是因为: root本身就是满足条件1的node, 还有很多Higher-level node也是如此, 所以要找那个deepest. + + +#### DFS on tree +- 分析题目, 思想是: 看到tree里面所有的leaves, 找到他们最deep的 common ancestor +- Maintain a map +- Recursively dfs: return deepest node that has all leaves by these comparisons: +- 1. If left,right child same depth, return root: they need common ancestor +- 2. If not same depth, return the one with larger depth +- 被传送去上一个level的, 永远都是subtree里面符合题意的: the node containing all leaf nodes +- Visit all nodes once O(n), space O(n) + +#### BFS +- Find all leaves at deepest level +- Use map to track each node-parent +- Backtrack all nodes to find common ancestor + + + +--- + +**29. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + +**30. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**32. [Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +#### Tree +- Traverse tree: left, right +- Concept of partial compare vs. whole compare + + + +--- + +**33. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + +**34. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + diff --git a/review/Double Recursive.md b/review/Double Recursive.md new file mode 100644 index 0000000..1427c9b --- /dev/null +++ b/review/Double Recursive.md @@ -0,0 +1,61 @@ + + + +## Double Recursive (2) +**0. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**1. [Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)** Level: Easy Tags: [DFS, Double Recursive, Tree] + + +count所有存在的 path sum == target sum. 可以从任意点开始. 但是只能parent -> child . + +#### DFS +- 对所给的input sum 做减法, 知道 sum 达到一个目标值截止 +- 因为可以从任意点开始, 所以当sum达标时候, 需要继续recursive, 从而找到所有情况 (有正负数, sum可能继续增加/减少) +- 经典的 helper dfs recursive + self recursive +- 1. helper dfs recursive 处理包括root的情况 +- 2. self recursive 来引领 skip root的情况. + +#### 特点 +- 与 `Binary Tree Longest Consecutive Sequence II` 在recursive的做法上很相似: +- 利用dfs做包括root的recursive computation +- 利用这个function自己, 做`不包括root的recursive computation` + + + +--- + diff --git a/review/Double Sequence DP.md b/review/Double Sequence DP.md new file mode 100644 index 0000000..55af0d7 --- /dev/null +++ b/review/Double Sequence DP.md @@ -0,0 +1,130 @@ + + + +## Double Sequence DP (6) +**0. [Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP] + + +给两个string, A, B. 找这两个string里面的LCS: 最长公共字符长度 (不需要是continuous substring) + +#### Double Sequence DP +- 设定dp长度为(n+1), 因为dp[i]要用来表示前i个(ith)时候的状态, 所以长度需要时i+1才可以在i位置, hold住i. +- 双序列: 两个sequence之间的关系, 都是从末尾字符看起, 分析2种情况: +- 1. A最后字符不在common sequence 或者 B最后字符不在common sequence. +- 2. A/B最后字符都在common sequence. 总体count + 1. + + + +--- + +**1. [Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + +time: O(MN) +Space: O(N) + +两个字符串, A要变成B, 可以 insert/delete/replace, 找最小变化operation count + +#### Double Sequence +- 考虑两个字符串的末尾index s[i], t[j]: 如果需要让这两个字符一样, 可能使用题目给出的三种operation: insert/delete/replace? +- 先calculate最坏的情况, 3种operation count + 1; 然后在比较match的情况. +- 注意, 在i或者j为0的时候, 变成另外一个数字的steps只能是全变. +- 第一步, 空间时间都是O(MN), O(MN) +- 滚动数组优化, 空间O(N) + +##### Detail analysis +- insert: assume insert on s, `#ofOperation = (s[0 ~ i] to t[0 ~ j-1]) + 1;` +- delete: assume delete on t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j]) + 1;` +- replace: replace both s and t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j - 1]) + 1;` +- dp[i][j]代表了两个 sequence 互相之间的性质: s[0 ~ i] 转换成 s[0~j] 所需要的最少 operation count +- init: 当i==0, dp[0][j] = j; 每次都要 + j 个character; 同理, 当j==0, dp[i][0] = i; +- 而dp[i][j]有两种情况处理: `s[i] == t[j]` or `s[i] != t[j]` + +##### 何时initialize +- 这种判断取决于经验: 如果知道initialization可以再 double for loop 里面一起做, 那么可以留着那么做 +- 这样属于 `需要什么, initialize什么` +- 事后在做space optimization的时候, 可以轻易在 1st dimension 上做rolling array + +#### Search +- 可以做, 但是不建议:这道题需要找 min count, 而不是search/find all solutions, 所以search会写的比较复杂, 牛刀杀鸡. + + + +--- + +**2. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**3. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**4. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**5. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + diff --git a/review/Enumeration.md b/review/Enumeration.md new file mode 100644 index 0000000..6f14266 --- /dev/null +++ b/review/Enumeration.md @@ -0,0 +1,284 @@ + + + +## Enumeration (15) +**0. [Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)** Level: Medium Tags: [Enumeration, Greedy] + + +#### Array +- 分三份:a b c考虑 +- 若a: countA++; 或b: countB++ +- 或c:countA--, countB-- +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 +- 最后出现的两个count>0的a和b,自然是potentially大于1/3的。其中有一个大于1/3. +- 比较countA和countB哪个大,就return哪一个。 + + + +--- + +**1. [Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)** Level: Medium Tags: [Array, Enumeration] + + +#### 找公式规律 +- 找到个转角度的规律公式: r = c; c = (w - r) +- 用temp variable, swap in place. + + + +--- + +**2. [Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited value. +- 在nest for loop里面validate row,col,and block. +- validate block要利用i 和 j 增长的规律。 +- 说白了,i && j是按照0~n增长的index, 具体怎么用是可以flexible的。这个方法在同一个nest for loop解决所有运算。 +- `int c = 3 * (i % 3) + j % 3;` //make use of how i and j increases +- `int r = 3 * (i / 3) + j / 3;` + +#### A bit Slower approach +- 单独做block validation: validate block的时候虽然看到了4层for. 其实也就是n^2 +- 可能代码稍微复杂一点 + + + +--- + +**3. [Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)** Level: Hard Tags: [DP, Enumeration, Partition DP] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +其中字符可能是 "*", 可以代表 [1 - 9] + +#### DP +- 乘法原理 +- 跟decode way I 一样, 加法原理, 切割点时: 当下是取了 1 digit 还是 2 digits 来decode +- 定义dp[i] = 前i个digits最多有多少种decode的方法. new dp[n + 1]. +- 不同的情况是: 每一个partition里面, 如果有"*", 就会在自身延伸出很多不同的可能 +- 那么: dp[i] = dp[i - 1] * (#variations of ss[i]) + dp[i - 2] * (#variations of ss[i,i+1]) + +##### 特点 +- 枚举的能力: 具体分析 '*' 出现的位置, 枚举出数字, 基本功. +- 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) +- 理解取MOD的原因: 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. +- 枚举好以后, 其实这个题目的写法和思考过程都不难 + + + + +--- + +**4. [Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)** Level: Medium Tags: [Basic Implementation, Enumeration, String] + + +给一个时间string"12:09", 用里面的4个integer组合成其他时间string, 目标找最小的next time. + +如果组合出的time string 在input time之前, 默认 + 24 hours. + +#### String +- enumerate all candidates and filter to keep the correct ones +- String.compareTo(string) -> gives lexicographical comparision + + + +--- + +**5. [Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)** Level: Medium Tags: [Array, Enumeration] + + +从(0,0)坐标, 走完spiral matrix, 把结果存在list里. + +#### DX, DY +- Basic implementation, array, enumeration +- 写一下position前进的方向: RIGHT->DOWN->LEFT->UP +- 用一个direction status 确定方向 +- 写一个compute direction function 改变方向 `(direction + 1) % 4` +- `boolean[][] visited` 来track走过的地方 + + + +--- + +**6. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + +**7. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**8. [Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + +time: O(n) + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**9. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**10. [Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4.java)** Level: Easy Tags: [Enumeration, String] + + +Read4 题目. 理解题目: 是有个input object buff, 会被populated with data. + +#### String in char[] format +- 理解题目: 其实就是track `可以read多少bytes by read4() response` +- 另外一个有用的function `System.arraycopy(src, srcIndex, dest, destIndex, length)` + + + +--- + +**11. [Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)** Level: Hard Tags: [Enumeration, Math, String] + + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 + +#### 注意 +- StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 +- 注意加 " " 的时候, 如果多余, 要`trim()` +- 注意, 小于20的数字, 有自己的特殊写法, 需要额外handle +- 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 + + + + +--- + +**12. [Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- Summing space = `width + (size-1)`. maintain: 1. list of candidates, 2. width of actual words +- calculate space in between: `remain/(size - 1)` +- overall for loop; clean up list: 1. over size; 2. last item +- 一点也不难, 但是要小心: deal with list of string的时候, 注意处理干净sum size of list, 就行了. +- `干净处理space`: 只处理 (n-1) items, 然后最后一个拿到for loop 外面, 特殊处理. + +#### Notes +- Clarification, observation: +- can start with greedy approach to stack as many words as possible +- once exceed the length, pop the top, and justify the added words (untouched words tracked by index) +- left justify: given list/stack of words with size t, overall remaining space length m, +- deal with last line with special care: just fill one space, and fill the rest of the row with space +- Does not seem very complicated, but need additional care of calculating the amount of space needed. +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**13. [Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)** Level: Hard Tags: [Enumeration, String] + + +Read N Character using `Read4(char[] buf)` 的加强版: 可以不断读 read(buf, n) + +#### String +- 注意String的index handle, 慢慢写edge case +- 理解题目意思: `read4(char[] buf)` 这样的 `populate input object` 的function稍微少一点. +- 遇到时候, 仔细理解function用法, 不要慌乱. 其实思考方式很简单, 仔细handle string 还有 edge case就好了. + + + +--- + +**14. [Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math] + + +根据题意枚举, 再根据规则basic implementation + +#### Alter input + +#### HashTable + Two Pointer + + + +--- + diff --git a/review/Expression Tree.md b/review/Expression Tree.md new file mode 100644 index 0000000..b4da365 --- /dev/null +++ b/review/Expression Tree.md @@ -0,0 +1,101 @@ + + + +## Expression Tree (5) +**0. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**1. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**2. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**3. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**4. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + diff --git a/review/Game Theory.md b/review/Game Theory.md new file mode 100644 index 0000000..23561eb --- /dev/null +++ b/review/Game Theory.md @@ -0,0 +1,142 @@ + + + +## Game Theory (4) +**0. [Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)** Level: Medium Tags: [DP, Game Theory, Greedy] + + +拿棋子游戏, 每个人可以拿1个或者2个, 拿走最后一个子儿的输. 问: 根据给的棋子输, 是否能确定先手的输赢? + +Game Theory: 如果我要赢, 后手得到的局面一定要'有输的可能'. + +#### DP, Game Theory +- 要赢, 必须保证对手拿到棋盘时, 在所有他可走的情况中, '有可能败', 那就足够. +- 设计dp[i]:表示我面对i个coins的局面时是否能赢, 取决于我拿掉1个,或者2个时, 对手是不是会可能输? +- dp[i] = !dp[i - 1] || !dp[i-2] +- 时间: O(n), 空间O(n) +- 博弈问题, 常从'我的第一步'角度分析, 因为此时局面最简单. + +#### Rolling Array +空间优化O(1). Rolling array, %2 + + + +--- + +**1. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**2. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**3. [Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)** Level: Easy Tags: [Brainteaser, DP, Game Theory] + + +#### Brainteaser +- 著名Nim游戏 +- 写一些,发现n=4,5,6,7,8...etc之后的情况有规律性: 谁先手拿到4就输了. +- 最终很简单n%4!=0就可以了, time, space O(1) + +#### DP +- 正规地找规律做, 就跟 coins in a line 一样, 按照先手后手来做 +- 可以rolling array 优化空间 +- Time O(n), 当然啦, 这个题目这样会timeout, 可以使用brainteaser的做法写出结果. + + + +--- + diff --git a/review/Geometry.md b/review/Geometry.md new file mode 100644 index 0000000..45c8aa0 --- /dev/null +++ b/review/Geometry.md @@ -0,0 +1,40 @@ + + + +## Geometry (2) +**0. [Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)** Level: Hard Tags: [Design, Geometry, Hash Table] + + +看的list of coordinates 是否能组成perfect rectangle, 并且不允许overlap area. + +#### 画图发现特点 +- 特点1: 所有给出的点(再找出没有specify的对角线点), 如果最后组成perfect rectangle, 都应该互相消除, 最后剩下4个corner +- 特点2: 找到所有点里面的min/max (x,y), 最后组成的 maxArea, 应该跟过程中accumulate的area相等 +- 特点1确保中间没有空心的部分, 保证所有的重合点都会互相消除, 最后剩下4个顶点 +- 特点2确保没有重合: 重合的area会最终超出maxArea + + + +--- + +**1. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + diff --git a/review/Graph.md b/review/Graph.md new file mode 100644 index 0000000..9ebc2f5 --- /dev/null +++ b/review/Graph.md @@ -0,0 +1,279 @@ + + + +## Graph (11) +**0. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**1. [Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)** Level: Medium Tags: [BFS, Graph] + + +#### Graph + BFS +- Build graph `map` +- BFS to find the shortest path: when the neibhbor has the curr node as the only one neighbor, it is leaf. +- record shortest path in Map> as result +- TODO: code it up. + +#### Previous Solution +- removing leaf && edge + + + +--- + +**2. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**3. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**4. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**5. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**6. [Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph] + + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes. +- 先能复制多少Node复制多少. 然后把neighbor 加上 +- Use `map` to mark visited + +#### DFS +- Given graph node obj `{val, list of neighbor}`: copy the node and all neighbors +- Mark visited using map +- for loop on the each one of the neighbors: map copy, record in map, and further dfs +- once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +- 主要思想是: 一旦复制过了, 不必要重新复制 + +#### BFS +- Copy the root node, then copy all the neighbors. +- Mark copied node in map. +- Use queue to contain the newly added neighbors. Need to work on them in the future. + + + +--- + +**7. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + + +--- + +**8. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**9. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**10. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + diff --git a/review/Greedy.md b/review/Greedy.md new file mode 100644 index 0000000..837cd6f --- /dev/null +++ b/review/Greedy.md @@ -0,0 +1,422 @@ + + + +## Greedy (18) +**0. [Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)** Level: Medium Tags: [Enumeration, Greedy] + + +#### Array +- 分三份:a b c考虑 +- 若a: countA++; 或b: countB++ +- 或c:countA--, countB-- +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 +- 最后出现的两个count>0的a和b,自然是potentially大于1/3的。其中有一个大于1/3. +- 比较countA和countB哪个大,就return哪一个。 + + + +--- + +**1. [Queue Reconstruction by Height.java](https://github.com/awangdev/LintCode/blob/master/Java/Queue%20Reconstruction%20by%20Height.java)** Level: Medium Tags: [Greedy] + + +别无他法, 只能写一遍例子, 找规律,然后greedy.  +需要写一遍发现的规律比如: 从h大的开始排列, 先放入k小的. 写comparator的时候要注意正确性. +如果要sort, 并且灵活insert:用arrayList. 自己做一个object. +最后做那个'matchCount'的地方要思路清晰, 找到最正确的spot, 然后greedy insert. + +O(n) space, O(nLog(n)) time, because of sorting. + +可能有简化的余地, 代码有点太长. +比如试一试不用额外空间? + + + +--- + +**2. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**3. [Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)** Level: Medium Tags: [DP, Game Theory, Greedy] + + +拿棋子游戏, 每个人可以拿1个或者2个, 拿走最后一个子儿的输. 问: 根据给的棋子输, 是否能确定先手的输赢? + +Game Theory: 如果我要赢, 后手得到的局面一定要'有输的可能'. + +#### DP, Game Theory +- 要赢, 必须保证对手拿到棋盘时, 在所有他可走的情况中, '有可能败', 那就足够. +- 设计dp[i]:表示我面对i个coins的局面时是否能赢, 取决于我拿掉1个,或者2个时, 对手是不是会可能输? +- dp[i] = !dp[i - 1] || !dp[i-2] +- 时间: O(n), 空间O(n) +- 博弈问题, 常从'我的第一步'角度分析, 因为此时局面最简单. + +#### Rolling Array +空间优化O(1). Rolling array, %2 + + + +--- + +**4. [Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)** Level: Medium Tags: [Array, DP, Greedy] + + +给出步数,看能不能jump to end. + +#### Greedy - start from index = 0 +- Keep track of farest can go +- 一旦 farest >= nums.length - 1, 也就是到了头, 就可以停止, return true. +- 一旦 farest <= i, 也就是说, 在i点上, 已经走过了步数, 不能再往前跳, 于是 return false +- This can be done using DP. However, greedy algorithm is fast in this particular problem. + +#### Greedy - start from index = n - 1 +- greedy: start from end, and mark last index +- loop from i = [n - 2 -> 0], where i + nums[i] should always >= last index +- check if last == 0 when returning. It means: can we jump from index=0 to the end? +- Time: O(n), beat 100% + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (A[j] >= i - j), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- Time: O(n^2) + + + + +--- + +**5. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**6. [Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)** Level: Hard Tags: [Greedy, Hash Table, Stack] + + +#### Hash Table, Greedy +- count[] = int[256], 不需要 `c-'a'` +- boolean visited[]: 一旦一个字母固定了位置后, 再次遇到时候, 直接跳过用过的character +- 如果tail字母可以变小, 那就delete掉tail, 重新接上新字母 (前提条件: 去掉的字母后面还会再出现, set visited[tail] = false) +- Space: O(1) count[], visited[]. +- Time: Go through all letters O(n) + +#### Stack +- Use stack instead of stringBuffer: keep append/remove last added item +- However, stringBuffer appears to be faster than stack. + + + +--- + +**7. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**8. [Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/Gas%20Station.java)** Level: Medium Tags: [Greedy] + + +给一串gas station array, 每个index里面有一定数量gas. + +给一串cost array, 每个index有一个值, 是reach下一个gas station的油耗. + +array的结尾地方, 再下一个点是开头, 形成一个circle route. + +找一个index, 作为starting point: 让车子从这个点, 拿上油, 开出去, 还能开回到这个starting point + +#### Greedy +- 不论从哪一个点开始, 都可以记录总油耗, `total = {gas[i] - cost[i]}`. 最后如果total < 0, 无论从哪开始, 必然都不能走回来 +- 可以记录每一步的油耗积累, `remain += gas[i] - cost[i]` +- 一旦 remain < 0, 说明之前的starting point 不合适, 也就是说, 初始点肯定在后面的index. 重设: start = i + 1 +- single for loop. Time: O(n) + +#### NOT DP +- 看似有点像 House Robber II, 但是问题要求的是: 一个起始点的index +- 而不是求: 最后点可否走完/最值/计数 + + + +--- + +**9. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**10. [Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)** Level: Hard Tags: [Greedy, Hash Table, Heap] + + +给一个string, 全是lowercase letter, 要求重新排列: 然后每个unique的character要有k distance apart. + +跟Task Scheduler有点像, 只不过Task那道题里面还可以用其他方法求count, 这道题要求出排列结果 + +#### PriorityQueue + HashTable +- PriorityQueue排序+分布排列的一个经典用法. +- Count frequency and store in pq. +- Consume element of pq for k rounds, each time pick one element from queue. +- Exception: if k still has content but queue is consumed: cannot complete valid string, return ""; +- space, O(n) extra space in sb, O(26) constant space with pq. +- time: O(n) to add all items + + + +--- + +**11. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**12. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**13. [Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Celebrity.java)** Level: Medium Tags: [Array, Greedy] + +time: O(n) +space: O(1) + +有n个人, 其中有个人是celebrity, 满足条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +#### Understand the property +- If brutly find celeb by comparing all possible pair: take complete O(n^2) handshakes. +- Instead, we can perform pruning, or like survival mode: +- 1. Assume a celeb = 0, and compare with all i = [1~ n-1] +- 2. If `celeb candidate know i, set celeb = i` as the next candidate (ex: prev canddiate invalid when he knows i) +- 3. For last standing celeb candidate: compare with all for validation +- Why performing the last run of validation? There could be someone dropped out before we execute `know(celeb, i)`. + +##### 思考逻辑 +- 先写出来[0 ~ n - 1], 最简单的方式 O(n^2) 检查, 记录每个人的状态. +- 逐渐发现, 因为 celeb 谁都不会认识, 那么当任何candidate knows anyone, 他自身就不是celeb. +- 我们可以greedy地, 一旦fail一个, 就立刻假设下一个是celeb candidate +- 最终还是要检查一遍, 避免错漏. +- 想一下happy case: 如果 celeb=0, 那么 know(celeb, i) 永远都是false, 然后 celeb一直保持0, 坚持到verify所有人. + + + +--- + +**14. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**15. [Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)** Level: Hard Tags: [DFS, Greedy, Math] + + +#### Greedy, Iterative +- For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +- We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +- Track the newly formed string; if new, add the new char to overall result +- Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] +- Same concept as dfs + +#### DFS +- Same concept: use window to cut out tail, and append with new candidate +- do this for k^n = Math.pow(k, n) times + + + +--- + +**16. [Delete Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Digits.java)** Level: Medium Tags: [Greedy, Priority Queue] + + +#### Priority Queue +- TODO: parse into node(index, digitValue) +- find the top k, and remove from char array +- O(nlogn) time + +#### Greedy +- 数位靠前的,权值更大. 所以硬来把靠前的相对更大的(跟following digit相比)去掉。 + + + +--- + +**17. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + diff --git a/review/Hash Table.md b/review/Hash Table.md new file mode 100644 index 0000000..3e7b126 --- /dev/null +++ b/review/Hash Table.md @@ -0,0 +1,1436 @@ + + + +## Hash Table (69) +**0. [Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)** Level: Medium Tags: [Hash Table, Math] + + +TODO: no need of hashMap, just use set to store the existing + +不难想到处理除法:考虑正负,考虑小数点前后。主要是小数点以后的要着重考虑。 +很容易忽略的是integer的益处。 + + +--- + +**1. [Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/Jewels%20and%20Stones.java)** Level: Easy Tags: [Hash Table] + +1524017454 + +给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 + +#### Basic HashSet + + + +--- + +**2. [LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)** Level: Hard Tags: [Design, Hash Table] + + +#### Hash Table +- 具体看thoughts, 几种不同的方式使用map +- `regular object map`: map of , where `item : {int val; int count}` +- Use a Map to track the frequency +- Track constant capacity, and minimum frequency +- Every get(): update all frequency map as well +- Every put(): update all frequency map as well, with optional removal (if over capacity) + +- Original post: http://www.cnblogs.com/grandyang/p/6258459.html +- TODO: one doubly linked list might be good enough to replace below: +- `frequency list map`: map of >, where the list preserves `recency` +- `item location in frequency map`: map of : +- index relative to the item in a particular list, not tracking which list here + + + +--- + +**3. [Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)** Level: Medium Tags: [Hash Table, Linked List] + + +TODO: +1. hash table solution not passing +2. Find O(n) solution + +#### Hash Table +- 与其他Majority Number一样。 +- 出现次数多余1/k,就要分成k份count occurance.用HashMap。 存在的+1;不存在map里的,分情况: +- 若map.size() == k,说明candidate都满了,要在map里把所有现存的都-1; +- 若map.size() < k, 说明该加新candidate,那么map.put(xxx, 1); +- 最后在HashMap里找出所留下的occurance最大的那个数。 +- 但这样的worst case是 O(nk) + + + +--- + +**4. [Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited value. +- 在nest for loop里面validate row,col,and block. +- validate block要利用i 和 j 增长的规律。 +- 说白了,i && j是按照0~n增长的index, 具体怎么用是可以flexible的。这个方法在同一个nest for loop解决所有运算。 +- `int c = 3 * (i % 3) + j % 3;` //make use of how i and j increases +- `int r = 3 * (i / 3) + j / 3;` + +#### A bit Slower approach +- 单独做block validation: validate block的时候虽然看到了4层for. 其实也就是n^2 +- 可能代码稍微复杂一点 + + + +--- + +**5. [Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Anagram%20Mappings.java)** Level: Easy Tags: [Hash Table] + + +比较简单, 用HashMap 存index list. 最后再遍历一遍数组A, 列举出所有元素. +O(n) + + + +--- + +**6. [Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/Island%20Perimeter.java)** Level: Easy Tags: [Hash Table] + + +#### Brutle +- 每个格子4个墙;每个shared的墙要-2 (墙是两面, -1 * 2) +- 最后合计结果就好. + +#### Hash Table +- 不必想太多用HashMap做.但是也可以思考一下: +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么久可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不打,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + + + +--- + +**7. [Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)** Level: Medium Tags: [Hash Table, Math] + + +其实想到了切入点, 是个可难可简单的题目. 这里的encode就是想办法把url存起来, 然后给个 key. +那么具体怎么做这个key, 简单就可以用一个map, 然后counting. 复杂一点就可以做random letter/number组成key. + + + +--- + +**8. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**9. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**10. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**11. [Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +HashMap + + + +--- + +**12. [Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)** Level: Medium Tags: [Hash Table, String, Two Pointers] + + +方法1: +Two Pointers +双指针: 从start开始遍历, 但是第一步是while loop来推进end; 直到推不动end, 然后start++ +巧妙点: 因为end是外围variable, 在start的loop上, end不会重置;[star ~ end] 中间不需要重复计算. +最终可以O(n); + +Previous verison of two pointers: +用两个pointer, head和i. +注意:head很可能被退回到很早的地方,比如abbbbbba,当遇到第二个a,head竟然变成了 head = 0+1 = 1. +当然这是不对的,所以head要确保一直增长,不回溯 + +方法2: + HashMap: + 一旦有重复, rest map. + 没有重复时候, 不断map.put(), 然后求max值 + +问题: 每次reset map之后就开始从新从一个最早的index计算, 最坏情况是O(n^2): +'abcdef....xyza' + + + + +--- + +**13. [Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, String, Two Pointers] + + +基本思想: 用个char[]存string的frequency. 然后2pointer, end走到底, 不断validate. +符合的就process as result candidate. + +HashMap的做法比char[]写起来要复杂一点, 但是更generic + + + +--- + +**14. [Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String] + + +大清洗 O(nk) +map.size一旦>k,要把longest string最开头(marked by pointer:start)的那个char抹掉 +一旦某一个char要被清除,所以在这个char 的1st and last appearance之间的char都要被清洗from map + + + + +--- + +**15. [Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)** Level: Hard Tags: [Hash Table, String, Trie] + + +Obvious的做法是全部试一遍, 判断, 变成 O(n^2) * O(m) = O(mn^2). O(m): isPalindrome() time. + +当然不行了, 那就看是O(nlogN), 还是O(n)? + +#### 方法1: Hash Table + Palindrome的性质. 复合型. +O(mn) + +##### 思路 +- 每一个word, 都可以拆分成 front + mid + end. 如果这个word + 其他word可以组成palindrome,那就是说 +- 砍掉 (mid+end), front.reverse() 应该存在 words[] 里面. +- 砍掉 (front+mid), end.reverse() 应该存在 words[] 里面. +- 我们用HashMap存所有的, 然后reverse, 找配对就好. + +##### Corner case +- 如果有 empty string "", 那么它跟任何一个palindrome word, 都可以配对, 并且根据位置前后变换, 凑成2份 distinct indexes. +- 这样就有了那个 `if (reverseEnd.equals("")) {...}` 的logic. +- 注意: 虽然在处理砍头/砍尾的两个 for loop里面都在根据 empty string 重复记录, + 但因为 "" 自己本身不能作为起点, 所以overall只会在被其他palindrome配对时记录一次. + + +#### 方法2: Trie +还要做一下那. + + + +--- + +**16. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**17. [Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Recursive +- 在自己的基础上recursive, 不用helper function +- Divide and Conquer, with helper(dfs) method +- O(n) time, no extra space + +#### Iterative: Stack +- Add left nodes all the way +- Print curr +- Move to right, add right if possible +- O(n) time, O(h) space + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. + +若不右移, 很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + +#### HashMap +? How? + + + +--- + +**18. [Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate.java)** Level: Easy Tags: [Array, Hash Table] + + +无序数组, 找是否有重复element, return true/false. + +#### HashSet +- No brain: HashSet. +- Time O(n), Space O(n) + +#### Sort, Binary Search +- Arrays.sort(x): Time O(nLogN), Space O(1) +- 排序后, 重复数会排在一起, 然后 binary search + + + +--- + +**19. [Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20II.java)** Level: Easy Tags: [Array, Hash Table] + + +Unsorted array, 找出是否有duplicate elemenets: 必要条件是, 这两个element的index i,j 的大小最多相差k. + +#### HashSet +- 很巧妙地根据k range地条件, 把HashSet里面的值控制在[i - k, i] +- 每次不断地往set里面加新元素, 从set里减去末尾index的元素 +- 而set.add(x)如果遇到重复, 会return false. +- 一旦在这个length k 的 range里面, 有重复, 就符合条件. +- Time O(n) + +#### HashTable +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k +- Time O(nm), m = # of duplicates + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**20. [Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Anagrams%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, Sliding Window] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### HashTable +- count character apperance 就想到hashtable +- 注意countS, countP 的技巧: 作比较只需要O(26) +- Overall timeO(n) +- 小心不要用一个int[] count 来技术 查0, 复杂度是O(n) + + + +--- + +**21. [Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Anagrams.java)** Level: Medium Tags: [Hash Table, String] + + +给一串string, return list of list, 把anagram 放在一起. + +#### Hash Table, key 是 character frequency +- 存anagram +- 用 character frequency 来做unique key +- 用固定长度的char[26] arr 存每个字母的frequency; 然后再 new string(arr). +- 因为每个位子上的frequency的变化,就能构建一个unique的string +- O(nk), k = max word length + + +#### Hash Table, key 是 sorted string +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**22. [Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Primes.java)** Level: Easy Tags: [Hash Table, Math] + + +计数: 所有小于n的prime number. + +#### prime number定义 +- >=2的没有除自己和1以外公约数的数。 +- 还有另外一个定义方法: 这个n,有没有小于n的一个i, 而达到: i * i + # of i = n. 如果有,那就不是 prime + +#### Steps +- 一个boolean长条,存isPrime[]。 然后从i=2, 全部变true. +- hash key: the number itself +- 然后利用这个因子的性质,非prime满足条件: self*self, self * self + self ... etc. +- 所以就check每一个j, j+i, j+i+i, 然后把所有non-prime全部mark成false. +- 最后,数一遍还剩下的true个数就好了 + + + +--- + +**23. [Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation.java)** Level: Easy Tags: [Hash Table] + + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count occurrance +- 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assum lower case letter. 应该至少是所有ASCII code + + + +--- + +**24. [Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Tree] + + +给一个Binary Tree root, 以及两个node A, B. 特点: node里面存了parent pointer. 找 lowest common ancestor + + +#### Hash Set +- 这个题有个奇葩的地方, 每个node还有一个parent, 所以可以自底向上. +- save visited in hashset. 第一个duplicate, 就是A B 的 lowest common ancestor + +#### Save in lists +- 自底向上。利用parent往root方向返回 +- 把所有parent存下来, 然后在两个list里面找最后一个 common node + +#### 注意 +- 无法从root去直接搜target node 而做成两个list. 因为根本不是Binary Search Tree! + + + + +--- + +**25. [Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/Hash%20Function.java)** Level: Easy Tags: [Hash Table] + + +#### Hash Function +- 解释Hash怎么做. +- Hash function例子: +- hashcode("abcd") = (ascii(a) * 33^3 + ascii(b) * 33^2 + ascii(c) *33^1 + ascii(d)*33^0) % HASH_SIZE +- 用到的参数比如: magic number 33, HASH_SIZE. + +- Hash的意义是:给一个string key, 转换成数字,从而把size变得更小。 +- 真实的implementation还要处理collision, 可能需要design hash function 等等。 + + +##### 每一步都%HASH_SIZE的原因 +- hashRst = hashRst * 33 + (int)(key[i]); +- hashRst = hashRst % HASH_SIZE; +- 原因是,hashRst会变得太大,所以不能算完和 再 %... + + + +--- + +**26. [LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)** Level: Hard Tags: [Design, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度。 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) + +##### 巧妙点 +- 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +- 2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +- 直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法: +- moveToHead() +- insertHead() +- remove() + +#### O(n) 检查重复 +- timeout method, 天真的来了一个O(n) 的解法,结果果然timeout. +- 一个map存数值。一个queue来存排位。 +- 每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + + + + +--- + +**27. [Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序, 排序以后才能逐个看是否partial string已经存在 +- 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 +- 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: 1. 长 string 排在前; 2. 相等length, 按照dictionary order 排序 +- 全部放入Trie. Trie.insert() +- 针对sorted words array, 从最长的开始找 Trie.startWith. +- 一旦找到, 就是符合题意的, 直接return. +- 注意: startWith 必须每一个node都是 isEnd, 才满足'逐个字母拼出' 的条件. +- Time: build Trie O(mn) + sort:O(nlogn) => O(nlogn) +- Space: O(mn) + +#### +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**28. [Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)** Level: Hard Tags: [Array, Hash Table, Union Find] + + +给一串数字, unsorted, 找这串数字里面的连续元素序列长度 (consecutive序列, 是数字连续, 并不是说要按照原order) + +#### HashSet +- 要想看连续元素, 必须要num++, num--这样搜索 +- 1. 需要O(1)找到元素 +- 2. 需要简单快速找到 num - 1, num + 1. +- 如果用min,max开array, 耗费空间 +- 用HashSet来存, 用set.contains() 来查找 num - 1, num + 1 存在与否 +- for loop. O(n) +- 里面的while loop 一般不会有O(n); 一旦O(n), 也意味着set 清零, for loop也不会有更多 inner while 的衍生. +- overall O(n) 时间复杂度 + + +#### Union Find +- 最终是要把相连的元素算一下总长, 其实也就是把元素group起来, 相连的group在一起, 于是想到UnionFind +- 这里用到了一个`int[] size` 来帮助处理 `合并的时候parent是哪个`的问题: 永远往group大的union里去 +- main function 里面, 有一个map来track, 每个元素, 只处理1遍. +- union的内容: current number - 1, current number + 1 +- https://www.jianshu.com/p/e6b955ca208f + +##### 特点 +- Union Find 在index上做好像更加容易 +- 其他union find function: `boolean connected(a,b){return find(a) == find(b)}` + + + +--- + +**29. [Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)** Level: Medium Tags: [Design, Hash Table] + + + +给一个string[] dict, 和一个word. + +每个word都可以缩写成固定的abbreviation ``(详细看原题) + +检查input word是否满足unique + +#### HashMap +- 简单算出abbreviatioin +- 检查abbr是否存在; 如果存在, 是不是input word本身. + + + +--- + +**30. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**31. [ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)** Level: Medium Tags: [Design, Hash Table] + + +#### basic implementation +- 用HashMap, 理解题目规律,因为重复的计算可以被覆盖,所以是个优化题。没有什么太大的悬念和意义. +- 消灭重合点: +- 如果process当下col, 其实要减去过去所有加过的row的交接点。。。 +- 再分析,就是每次碰到row 取一个单点, sumRow += xxx。 +- 然后process当下col时候, sum += colValue * N - sumRow. 就等于把交叉所有row(曾经Process过的row)的点减去了。很方便。 +- 最后read in 是O(P), process也是O(P). + + + + +--- + +**32. [Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + +time: O(n) +space: O(1) + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List +- Basic Implementation of copy linked list: +- use node and dummy to hold new list, 遍历head.next .... null. +- Map 在这里用来: 1. avoid creating same node; 2. return the item if existing +- map 的 key全部是old object, 新的key全部是 newly created object +- 每一步都check map里面有没有head. 没有? 加上 +- 每一步都check map里面有没有head.random. 没有? 加上 + + + +--- + +**33. [HashWithCustomizedClass(LinkedList).java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithCustomizedClass(LinkedList).java)** Level: Medium Tags: [Hash Table] + + +练习HashMap with customized class. functions: get(), put(), getRandom() + +#### Hash Table +- store map as array: `Entry[] table;` +- store entry as linked list: `public Entry(K key, V value, Entry next)` +- compute hashKey: `Math.abs(key.hashCode()) % this.capacity` +- Handle collision: +- 1. Check if duplicate (matching key), if so, replace and return +- 2. Check through the linked list, find find duplicate (matching key), replace and return. +- 3. If no duplicate, add the entry to the tail +- Find item: compute hashKey -> find linked list -> iterate over list to find a matching key. + + + +--- + +**34. [Rehashing.java](https://github.com/awangdev/LintCode/blob/master/Java/Rehashing.java)** Level: Medium Tags: [Hash Table] + + +给一个Hash Table, 是用 linked list 做的. 问题是: capacity太小, collision太多的情况下, 需要double capacity 然后rehash. + +#### Hash Table +- 明白hashCode() function的意义: 拿到hashKey的时候, 用hashKey%capacity 来做hash code +- hashcode就是hash map里面的index +- 明白collision handling 的方式, 和如何double capacity而rehashing +- 都是基本操作, 概念实现 + + + +--- + +**35. [Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)** Level: Hard Tags: [Greedy, Hash Table, Stack] + + +#### Hash Table, Greedy +- count[] = int[256], 不需要 `c-'a'` +- boolean visited[]: 一旦一个字母固定了位置后, 再次遇到时候, 直接跳过用过的character +- 如果tail字母可以变小, 那就delete掉tail, 重新接上新字母 (前提条件: 去掉的字母后面还会再出现, set visited[tail] = false) +- Space: O(1) count[], visited[]. +- Time: Go through all letters O(n) + +#### Stack +- Use stack instead of stringBuffer: keep append/remove last added item +- However, stringBuffer appears to be faster than stack. + + + +--- + +**36. [Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum.java)** Level: Easy Tags: [Array, Hash Table] + + +#### HashMap +- 相对暴力简洁: 找到一个value, 存一个index +- 若在HashMap里面 match 到结果, 就return HashMap里存的index. +- O(n) space && time. + +#### Sort array, two pointer +- 前后++, --搜索. Sort 用时O(nlogn). +- 1. 第一步 two pointer 找 value. +- 2. 注意,要利用额外的空间保留original array, 用来时候找index. (此处不能用HashMap,因为以value 为key,但value可能重复) +- O(n) space, O(nlogn) time. + + + + +--- + +**37. [Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)** Level: Hard Tags: [Design, Geometry, Hash Table] + + +看的list of coordinates 是否能组成perfect rectangle, 并且不允许overlap area. + +#### 画图发现特点 +- 特点1: 所有给出的点(再找出没有specify的对角线点), 如果最后组成perfect rectangle, 都应该互相消除, 最后剩下4个corner +- 特点2: 找到所有点里面的min/max (x,y), 最后组成的 maxArea, 应该跟过程中accumulate的area相等 +- 特点1确保中间没有空心的部分, 保证所有的重合点都会互相消除, 最后剩下4个顶点 +- 特点2确保没有重合: 重合的area会最终超出maxArea + + + +--- + +**38. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + +**39. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**40. [Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)** Level: Medium Tags: [Array, Hash Table, PreSum] + + +给一个int[][] matrix, 找一个sub matrix, where the sum == 0. + +#### PreSum的思想 +- 算出一个右下角点(i,j)到(0,0)的大小: 上一块 + 左一块 + curr node - overlap area +- preSum[i][j]: sum from (0,0) to (i-1,j-1) +- same approach as `subarray sum`: use hashmap to store diff->index; if diff re-appears, that means sum of 0 has occurred +- sequence of calculation: 1. iterate over start row. 2. iterate over end row. 3. iterate over col number (this is where hashmap is stored based on) +- the iteration over col is like a screening: find previous sum and determine result +- Note: 其实并没有真的去找 `== 0` 的解答,而是根据特性来判断 `剩下的/后来加上的一定是0` + + + +--- + +**41. [H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)** Level: Medium Tags: [Bucket Sort, Hash Table, Sort] + + +找到h-index, 给的citation int[] 并不是sorted. h-index 的definition 具体看题目. + +#### Sort, find h from end +- 例子写出来,发现可以sort以后按照定义搜索一遍。 nlogn. +- 搜索一遍时候可以优化,用binary search. 但是没意义,因为array.sort已经用了nlogn +- 题目给的规则, 从小到大排序后: 剩下的 paper `n-h`, 全部要 <= h 个 citation. +- time O(nlogn), search O(n) + +##### 正向思考 +- 从i = 0 开始找第一个 `citations[i] >= h`, 就是第一个符合 h-index 规则的paper, return h + +##### 反向思考 +- 如果从 h = n, 每次h--; 那么 `x = n - h` 就是从 `[0 ~ n)` 开始找第一个 `dictations[x] >= h`, 就是结果 +- 同时, `dictations[x-1]` 就是最后一个(dictation最多的)其余的paper. + +#### Bucket count / Bucket Sort +- O(n) +- Bucket sort的思想(更像是counting sort?): 过一遍 input, 把dictation value 作为 index, 分布在bucket[index]上++ +- bucket[x] 是 count when # of citation == x. +- 如果 x 大于 n的时候, 就超出了index范围, 但是刚好这个问题可以包容, 把这样的情况记位在bucket[n]就可以 +- 巧妙: `sum += bucket[h]` where `h = [n ~ 0]` 利用了h-index的definition: +- #of papers (sum of bucket[n]...bucket[0]) has more than h cidations +- 这里运用到了bucket sort的思想, 但是并不是sorting, 而h-index的定义运用的很巧妙. +- Read more about actual bucket sort: https://en.wikipedia.org/wiki/Bucket_sort + + + +--- + +**42. [Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)** Level: Hard Tags: [Greedy, Hash Table, Heap] + + +给一个string, 全是lowercase letter, 要求重新排列: 然后每个unique的character要有k distance apart. + +跟Task Scheduler有点像, 只不过Task那道题里面还可以用其他方法求count, 这道题要求出排列结果 + +#### PriorityQueue + HashTable +- PriorityQueue排序+分布排列的一个经典用法. +- Count frequency and store in pq. +- Consume element of pq for k rounds, each time pick one element from queue. +- Exception: if k still has content but queue is consumed: cannot complete valid string, return ""; +- space, O(n) extra space in sb, O(26) constant space with pq. +- time: O(n) to add all items + + + +--- + +**43. [Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Anagrams.java)** Level: Medium Tags: [Array, Hash Table] + + +把anagram找到并output + +#### HashMap +- 存在int[26], Arrays.toString(arr) 就是 string key: character frequency map +- anagram都有一样的key, 存进hashmap +- output anagrams + +#### HashMap + Sort +- HashMap 的做法. sort每个string, 存进HashMap, 重复的就是anagrams,最后输出。 +- toCharArray +- Arrays.sort +- Stirng.valueOf(char[]) +- 时间n*L*O(logL),L是最长string的长度。 + +#### Previous Notes +- Arrays.toString(arr)的做法。arr是int[26], assuming only have 26 lowercase letters. +- Count occurrance, 然后convert to String,作为map的key. +- Time complexity: nO(L) +- 另一种做法:http://www.jiuzhang.com/solutions/anagrams/ +- 1. take each string, count the occurrance of the 26 letters. save in int[]count. +- 2. hash the int[] count and output a unique hash value; hash = hash * a + num; a = a * b. +- 3. save to hashmap in the same way as we do. +- 这一步把for s: strs 里面的时间复杂度降到了O(L). L = s.length(). +- Need to work on the getHash() function. +- 时间变成n*O(L). Better. + + + + +--- + +**44. [Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)** Level: Medium Tags: [DFS, Hash Table, Tree] + + +给一串3-digit 的数组. 每个数字的表达一个TreeNode, 3 digit分别代表: depth.position.value + +这串数字已经从小到大排列. 求: 所有可能的 root->leaf path 的所有可能的 path sum 总和. + +#### DFS, Hash Table +- 因为`前两个digit可以uniquely identify`一个node, 所以可以把前两个digit作为key, 定位node. +- 特点: 比如考虑root, 有 n 个leaf, 就会加 n 遍root, 因为有 n 个 unique path嘛. +- 实现: 每个node, 上来先把curr value加进sum; 只要有child, 到这个node位置的以上path sum 就要被重加一次. +- format: depth.position.value. (on same level, position may not be continuous) +- approach: map each number into: , and dfs. +- Start from dfs(map, rootKey, sum): +- 1. add node value to sum +- 2. compute potential child. +- 3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +- 4. also, if child exist, dfs into next level +- Space, time O(n) + + + +--- + +**45. [Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Words.java)** Level: Easy Tags: [Hash Table, String] + + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**46. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**47. [Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- true/false problem, think about dp +- 子问题: 前i个字母, 是否可以有valid break +- 检查dp[j] && `if substring(j, i) valid`, for all j = [0 ~ i] +- dp = new boolean[n + 1]; dp[0] = true; +- goal: if there is a j, `dp[j] == true && word[j, n] in dict`. Need iterate over i = [0 ~ n], also j = [0, i] +- 注意, 用set代替list, 因为要用 contains(). + +#### Previous notes +##### 方法2(attempt4 code) +- 与Word BreakII用同样的DP。 +- valid[i]: 记录从i到valid array末尾是否valid. + +##### 方法1:(attempt3 code) +- state,rst[i]: 从[0~i] inclusive的string是否可以在dict中break开来找到? +- function: rst[i] = true if (rst[i - j] && set.contains(s.substring(i - j, i))); j in[0~i] +- 1. rst[i - j] 记录的是[0, i-j]这一段是否可以break后在dict找到。 +- 2. 若true,再加上剩下所有[i-j, i]都能在dict找到,那么rst[i] = rst[0, i - j] && rst[i-j, i] == true +- 优化:找dict里面最长string, 限制j的增大。 + + + + +--- + +**48. [Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/Sparse%20Matrix%20Multiplication.java)** Level: Medium Tags: [Hash Table] + +time: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` +space: O(1) extra + +给两个matrics, 做乘积. 注意, 是sparse matrix (特点: 很多0). + +#### Hash Table +- Recall matric multiplication rules: result[i][j] = sum(A-row[i] * B-col[j]) +- `sparse matric: lots positions are zero` +- 平白地写matric multiplication 没有意义, 重点就是optimization: +- `optimization`: for A-zero-row, and B-zero-col, there is no need to calculate, just return 0. +- 1. Find A-zero-rows and store in setA, same for setB +- 2. during multiplication, reduce time complexity. +- Base: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` + +#### Matrices +- 乘法规则: result[i][j] = sum(A-row[i] * B-col[j]) +- A column size == B row size. 并且: 计算顺序是iterate over A column size + + + +--- + +**49. [Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)** Level: Medium Tags: [Hash Table] + +time: O(mn) +space: O(X), X = max wall width + +给一面墙, 每一行是一行bricks. 用一条vertical line 扫描, 会vertically割开brink. 找到割开最少brick的那条线的x index. + +#### Hash Table +- Find the vertical line (x-coordinate of the grid), where most gaps are found. +- Each gap has (x,y) coordinate +- Create `map`, and maintain a max occurance. +- 计算: x-coordinate: `x = 0; x += brick[i] width` +- Eventually: min-crossed bricks = wall.lenght - maxOccurrance + +##### 思想 +- 分析题意, 找到题目的目标 +- 虽然Map自己不能有sort的规律, 但是可以maintain global variable + + + +--- + +**50. [Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)** Level: Medium Tags: [Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +#### Map +- use `Map` to store inline preSum and its index. +- 1. Build presum incline +- 2. Use map to cache current preSum value and its index: `Map` +- 3. Each iteration: calculate possible preSum candidate that prior target sequence. ex: `(preSum - k)` +- 4. Use the calculated preSum candidate to find index +- 5. Use found index to calculate for result. ex: calculate range. + + + +--- + +**51. [Contiguous Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Contiguous%20Array.java)** Level: Medium Tags: [Hash Table] + + +TODO: how aout without chaning the input nums? + + + +--- + +**52. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + +time: O(n) +space: O(n) + +给一串点, 找是否有一个所有点中间的, 跟y-axis平行的中线. + +#### Hash Table +- 1. store in `Map>`, 2. iterate over map, check head,tail against the mid point +- 很好的细节题目: +- 1. 除以2, 需要存double +- 2. (问面试官)可以有重复的点! 所以track `set` +- 3. 处理 left==right时候, 就当做两个点来处理. +- 4. 存进set里面没有sort, 但是最后做check的时候, 需要sort list +- 时间: visit all nodes 两遍, O(n) + + + +--- + +**53. [Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + +time: O(1) avg +space: O(n) + +#### Hash Table +- 用`map 来track value->index`, 用`list track index->value` +- map查看value是否存在 +- list maintain 用来 insert/remove/random operations. +- 特点: 一旦remove, 换到list结尾然后 `list.remove(list.size() - 1)`, 这样remove的cost更低. +- list.remove(object) 应该是要O(logn) 做一个search的. + + + +--- + +**54. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**55. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**56. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**57. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**58. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + +**59. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**60. [Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)** Level: Hard Tags: [DP, Hash Table] + + +Frog jump 的题目稍微需要理解: 每个格子可以 jump k-1, k, k+1 steps, 而k取决于上一步所跳的步数. 默认 0->1 一定是跳了1步. + +注意: int[] stones 里面是stone所在的unit (不是可以跳的步数, 不要理解错). + +#### DP +- 原本想按照corrdiante dp 来做, 但是发现很多问题, 需要track 不同的 possible previous starting spot. +- 根据jiuzhang答案: 按照定义, 用一个 map of > +- 每次在处理一个stone的时候, 都根据他自己的 set of , 来走下三步: k-1, k, or k+1 steps. +- 每次走一步, 查看 stone + step 是否存在; 如果存在, 就加进 next position: `stone+step`的 hash set 里面 + +##### 注意init +- `dp.put(stone, new HashSet<>())` mark 每个stone的存在 +- `dp.get(0).add(0)` init condition, 用来做 dp.put(1, 1) + +##### 思想 +- 最终做下来思考模式, 更像是BFS的模式: starting from (0,0), add all possible ways +- 然后again, try next stone with all possible future ways ... etc + + + +--- + +**61. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**62. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**63. [Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math] + + +根据题意枚举, 再根据规则basic implementation + +#### Alter input + +#### HashTable + Two Pointer + + + +--- + +**64. [First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +方法1: 按照题意, 找到第一个 first index == last index的字母. + +方法2: 用hashmap存字母的index, 有些重复字母的index就会是个list. 找到单一index, 结合成list, sort, return list.get(0) + + + +--- + +**65. [4Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/4Sum.java)** Level: Medium Tags: [Hash Table] + + +#### Based on 2sum +- 1. 利用2Sum的原理,把4Sum分为连个2Sum。左一个pair,右一个pair,每个pair里面放2个数字。 +- 2. 以一个点,i,作为分界口,也要列举出所有i之前的pair,作为基础。 +- 3. 再尝试从所有i+1后面,找合适的2nd pair。 +- Time: O(n^2 * x), where x = # of candidates, still slow +- 可以用HashSet, 可以直接比较list里面每一个元素, 保证set不重复. +- Previous Notes: 在造class Pair时候,要做@override的function: hashCode(), equals(Object d). 平时不太想得起来用。 +- 参见 http://lifexplorer.me/leetcode-3sum-4sum-and-k-sum/ + +#### Based on 3Sum +- 3Sum外面再加一层. 参考3Sum. 时间O(n^3)。 但此方法在k-sum时候,无疑过于费时间. O(n^k) + + + +--- + +**66. [Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Isomorphic%20Strings.java)** Level: Easy Tags: [Hash Table] + + +#### HashMap +- two failture cases: +- same key, value not matching +- two key maps to same value + +#### Previous note +1. Match. 就是map.containsKey, map.containsValue, and char1 == char2. Perfect. +2. Either Key not exist, or Value not exit. False; +3. Both key and Value exist, but map.get(char1) != char2. Miss-match. False. +4. None of Key or Value exist in HashMap. Then add the match. + + + +--- + +**67. [Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack? + + + +--- + +**68. [Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)** Level: Medium Tags: [Hash Table, String] + + + +#### Convert to orginal string +- shit by offset. `int offset = s.charAt(0) - 'a';` +- increase if less than 'a': `if (newChar < 'a') newChar += 26;` + +#### Previous notes +- 相同shift规则的string, 能被推算到同一个零起始点,就是共同减去一个char,最后就相等。以此作为key,用HashMap。一目了然。 +- 记得根据题目意思,一开始要String[] sort一下。 + + + +--- + diff --git a/review/HashHeap.md b/review/HashHeap.md new file mode 100644 index 0000000..bf0b275 --- /dev/null +++ b/review/HashHeap.md @@ -0,0 +1,13 @@ + + + +## HashHeap (1) +**0. [HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/HashHeap.java)** Level: Hard Tags: [HashHeap, Heap] + + +非题.是从九章找来的HashHeap implementation. + + + +--- + diff --git a/review/Heap.md b/review/Heap.md new file mode 100644 index 0000000..2ff9bfb --- /dev/null +++ b/review/Heap.md @@ -0,0 +1,382 @@ + + + +## Heap (16) +**0. [Kth Smallest Element in a Sorted Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20Sorted%20Matrix.java)** Level: Medium Tags: [Binary Search, Heap] + +time: O(n + klogn) +space: O(n) + +给一个sorted matrix, 找kth smallest number (not distinct) + +Related: `Kth Largest Element in an Array` + +#### PriorityQueue +- 和Merge K sorted Array/ List 类似:使用PriorityQueue。 +- 因为Array的element无法直接找到next,所以用一个class Node 存value, x,y positions. +- Initial O(n) time, also find k O(k), sort O(logn) => O(n + klogn) +- 变型: Kth Largest in N Arrays + +#### Binary Search +- we know where the boundary is start/end are the min/max value. +- locate the kth smallest item (x, y) by cutt off partition in binary fasion: +- find mid-value, and count # of items < mid-value based on the ascending matrix +- O(nlogn) + + + + +--- + +**1. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**2. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**3. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**4. [Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)** Level: Hard Tags: [Greedy, Hash Table, Heap] + + +给一个string, 全是lowercase letter, 要求重新排列: 然后每个unique的character要有k distance apart. + +跟Task Scheduler有点像, 只不过Task那道题里面还可以用其他方法求count, 这道题要求出排列结果 + +#### PriorityQueue + HashTable +- PriorityQueue排序+分布排列的一个经典用法. +- Count frequency and store in pq. +- Consume element of pq for k rounds, each time pick one element from queue. +- Exception: if k still has content but queue is consumed: cannot complete valid string, return ""; +- space, O(n) extra space in sb, O(26) constant space with pq. +- time: O(n) to add all items + + + +--- + +**5. [HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/HashHeap.java)** Level: Hard Tags: [HashHeap, Heap] + + +非题.是从九章找来的HashHeap implementation. + + + +--- + +**6. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**7. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**9. [Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)** Level: Medium Tags: [Heap, MinHeap, PriorityQueue] + + +Same as merge k sorted list, use priorityQueue + +#### Priority Queue +- 由Merge k sorted list启发。用PriorityQueue,存那k个首发element +- PriorityQueue需要存储单位: 自己建一个Class Node 存val, x, y index. +- 因为array里没有 'next' pointer,只能存x,y来推next element +- Not sure why `new PriorityQueue<>(Comparator.comparing(a -> a.val));` is slower + + + +--- + +**10. [Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/Heapify.java)** Level: Medium Tags: [Heap, MinHeap] + + +Turn unsorted array into a min-heap array, where for each A[i], + +A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i]. + +#### Heap +- Heap用的不多. 得用一下, 才好理解. 通常default 的PriorityQueue就是给了一个现成的min-heap: +- 所有后面的对应element都比curr element 小。 +- Heapify里面的**siftdown**的部分: +- 只能从for(i = n/2-1 ~ 0), 而不能从for(i = 0 ~ n/2 -1): 必须中间开花,向上跑的时候才能确保脚下是符合heap规则的 + +#### Heapify/SiftDown做了什么? +- 确保在heap datastructure里面curr node下面的两个孩子,以及下面所有的node都遵循一个规律 +- 比如在这里,若是min-heap,就是后面的两孩子都要比自己大。若不是,就要swap。 + +#### min-heap的判断规律: +- for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. +- siftdown时:在curr node和两个child里面小的比较。如果的确curr < child, 搞定,break while. +- 但若curr 并不比child小,那么就要换位子,而且继续从child的位子往下面盘查。 + + + +--- + +**11. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**12. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**13. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**14. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**15. [Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Deque, Monotonous queue +- 维持monotonuous queue: one end is always at max and the other end is min. Always need to return the max end of queue. +- when adding new elements x: start from small-end of the queue, drop all smaller elements and append to first element larger than x. +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`. +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! + + + +--- + diff --git a/review/Interval DP.md b/review/Interval DP.md new file mode 100644 index 0000000..b33e5d8 --- /dev/null +++ b/review/Interval DP.md @@ -0,0 +1,154 @@ + + + +## Interval DP (4) +**0. [Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)** Level: Hard Tags: [DP, Interval DP, String] + + +- 给两个string S, T. 检验他们是不是scramble string. +- scramble string 定义: string可以被分拆成binary tree的形式, 也就是切割成substring; +- 旋转了不是leaf的node之后, 形成新的substring, 这就是原来string的 scramble. + + +#### Interval DP 区间型 +- 降维打击, 分割, 按照长度来dp. +- dp[i][j][k]: 数组S从index i 开始, T从index j 开始, 长度为k的子串, 是否为scramble string + +##### Break down +- 一切两半以后, 看两种情况: , 或者不rotate这两半. 对于这些substring, 各自验证他们是否scramble. +- 不rotate分割的两半: S[part1] 对应 T[part1] && S[part2] 对应 T[part2]. +- rotate分割的两半: S[part1] 对应 T[part2] && S[part2] 对应 T[part1]. + +##### Initialization +- len == 1的时候, 其实无法旋转, 也就是看S,T的相对应的index是否字符相等. +- initialization非常非常重要. 很神奇, 这个initailization 打好了DP的基础, 后面一蹴而就, 用数学表达式就算出了结果. +- input s1, s2 在整个题目的主要内容里面, 几乎没有用到, 只是用在initialization时候. +- More details, 看解答 + + + +--- + +**1. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**2. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**3. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + diff --git a/review/Interval.md b/review/Interval.md new file mode 100644 index 0000000..a8d6365 --- /dev/null +++ b/review/Interval.md @@ -0,0 +1,22 @@ + + + +## Interval (1) +**0. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + diff --git a/review/KMP.md b/review/KMP.md new file mode 100644 index 0000000..f8144c1 --- /dev/null +++ b/review/KMP.md @@ -0,0 +1,21 @@ + + + +## KMP (1) +**0. [Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)** Level: Hard Tags: [KMP, String] + + +#### Divide by mid point, Brutle +- check (mid, mid+1), or (mid-1, mid+1). +- If the two position matches, that is a palindrome candidate +- 比较front string 是否是 end string 的substring +- O(n^2) +- timeout on last case: ["aaaaaa....aaaacdaaa...aaaaaa"] + +#### KMP +- TODO + + + +--- + diff --git a/review/Linked List.md b/review/Linked List.md new file mode 100644 index 0000000..bfff51e --- /dev/null +++ b/review/Linked List.md @@ -0,0 +1,563 @@ + + + +## Linked List (31) +**0. [Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)** Level: Medium Tags: [Hash Table, Linked List] + + +TODO: +1. hash table solution not passing +2. Find O(n) solution + +#### Hash Table +- 与其他Majority Number一样。 +- 出现次数多余1/k,就要分成k份count occurance.用HashMap。 存在的+1;不存在map里的,分情况: +- 若map.size() == k,说明candidate都满了,要在map里把所有现存的都-1; +- 若map.size() < k, 说明该加新candidate,那么map.put(xxx, 1); +- 最后在HashMap里找出所留下的occurance最大的那个数。 +- 但这样的worst case是 O(nk) + + + +--- + +**1. [Remove Duplicates from Unsorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Unsorted%20List.java)** Level: Medium Tags: [Linked List] + + +基本方法: O(n) sapce, time +遍历。 +遇到duplicate(可能多个), while直到node.next不是duplicate. +接下去,既然不是duplicate,那就add 进 set + + +如果不用extra memory, do it in place: +那就要sort linked list. 用merge sort. + +复习merge sort: +1. find middle. +2. recursively: right = sort(mid.next); left = sort(head). +3. within sort(), at the end call merge(left, right) + + +--- + +**2. [Two Lists Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Lists%20Sum.java)** Level: Medium Tags: [Linked List] + + +给两个Linked list, sum up and 合成新的list + + + +--- + +**3. [Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. +遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + +跟Add Binary的理解方式一模一样. + +注意: +Linked List 没有天然size. +用DummyNode(-1).next来hold住结果. + + + + +--- + +**4. [Add Two Numbers II.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers%20II.java)** Level: Medium Tags: [Linked List] + + +Singly-linked list需要reverse, 用stack. +最终结果要恢复成input list 那样的sequence方向, 用stack一个个pop()刚好就可以做到. + +加法都一样: + 1. sum = carry + 2. carry = sum / 10 + 3. sum = sum % 10; + + + +--- + +**5. [Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List] + + +如题, 把一个sorted singly linked list 转换成一个 height balanced BST + +#### DFS +- Divide and Conquer +- 找到mid node +- 然后分割两半, 分别dfs做各自两个subtree: node.left,node.right +- 用长度来定位mid, 每次找中间点做root, 然后前半段, 后半段分别dfs with length. +- 用快慢pointer 找到mid. Better: 不用traverse entire linked list + +#### Details +- slowPointer = node; +- fastPointer = node.next; +- 然后把root = mid.next +- 然后开始sortedListToBST(mid.next.next); //后半段 +- mid.next = null;//非常重要,要把后面拍过序的断掉 +- sortedListToBST(head); //从头开始的前半段 +- 最后root.left, root.right merge一下。 + + + +--- + +**6. [Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Two Pointer: Slow Fast Pointer +- O(1) sapce: 用快慢指针。一个跑.next, 一个跑.next.next。 总有一次,fast会因为cycle而追上slow。 +- 那个时候其实slow.val = fast.val. + +#### Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**7. [Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +O(n), one pace, no extra space +找到窗口, 然后平移, 最后pre 和 head之间 skip一个node就好. + + + +--- + +**8. [Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Linked List, Math, Two Pointers] + + +LinkedList 里面有 cycle, 找到cycle的起始点(第一个重复出现的element). + +#### Slow, fast Pointer +- 快慢指针, O(1)space. +- 1. 确认有cycle后 2. 数学问题:找到开头. +- 当head == slow.next时候, head就是cycle starting point. +- 也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +#### 证明 +- 1. 假设慢指针走t步, 快指针走快一倍, 也就是2t. +- 2. 我们假设cycle的长度是Y, 而进入cycle之前的长度为X. +- 3. 假设慢指针走了m圈cycle, 而快指针走了n圈cycle之后, 两个pointer相遇. +- 4. 最终在Y cycle里面的K点相遇, 也就是两个指针都在这最后一圈里面走了K 步. +- 那么: +- t = X + mY + K +- 2t = X + nY + K +- 整合公式: X + K = (n - 2m)Y +- 这里的m和n不过是整数的跑圈数, 也就是说X和K加在一起, 总归是结束cycle. X 和 K 互补 +- 结论: 当slow/fast 指针在K点相遇后, 再走X步, 就到了cycle的起点, 也就是题目要求的起点. + +#### Hash Table, O(n) space + + + + +--- + +**9. [Swap Nodes in Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Nodes%20in%20Pairs.java)** Level: Medium Tags: [Linked List] + + +#### enumurate +基本原理, 写出来, 然后连线: +pre -> A -> B -> C -> ... +需要一个虚拟 preNode做起始node, 不然无法把后面的node换到开头. + +#### 注意 +用dummy = pre作为head前一格. +用 `pre.next == null && pre.next.next` 来check是否为NULL. +pre.next.next 保证了至少有一次swap. + + + +--- + +**10. [Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Node%20in%20a%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +Given Singlely linked list, 删除一个任意node (不能是head node) + +#### Basic +- update node.val +- Link curr.next to curr.next.next + + + +--- + +**11. [Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)** Level: Medium Tags: [Linked List, Sort] + + +input一串数字, 需要出sorted output. 每次insert一个数字时, 都要放到正确的sorted的位置 + +每次insertion的时候, 都从input里面减掉这个数字 + +#### Linked List +- 把list里面每个元素都拿出来,scan and insert一遍 +- Time O(n^2), worst case, 每次放入n个数字里面的element, 刚好都是最大的 +- 所以每次要traverse n nodes, 然后走n次 + +##### 思考方法 +- 如果已经有个sorted list, insert一个element进去。怎么做? +- while 里面每个元素都小于 curr, keep going +- 一旦curr在某个点小了,加进去当下这个空隙。 + + + +--- + +**12. [Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Middle%20of%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +找Linked List的中间node + +- 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + + + +--- + +**13. [Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Linked%20List%20Elements.java)** Level: Easy Tags: [Linked List] + + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + + + +--- + +**14. [Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单, 但是要在Linkde List random access坐标, 是很难得: 所以需要把一半 ListNode 翻转 +- reverse linked list: 遍历接开头 +- 用快慢指正找到mid point +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + +#### Previous Note +- Palindrome都是要两边回溯相等 +- linkedlist不能reverse iterating, 那么就reverse the list, 从中间开花作比较。 + + + +--- + +**15. [Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +#### Reverse List +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + + + +--- + +**16. [Reverse Linked List II .java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List%20II%20.java)** Level: Medium Tags: [Linked List] + + +reverse 一个 linked list 中 [m ~ n] 的一部分. + +#### Reverse linked list +- 在基本的reverse linked list 上面 多了一层: 找到front node, 接下来的 [m ~ n] node 需要被reverse +- 只需要reverse中间的部分. +- Reverse的时候: 用一个dummyNode, 这道题里面, 其实就用 nodeFront, 那么 dummy.next 就是整个reversed list. + +##### 注意 +- 一定要Mark开头的那个mth node, 最后用它接上 剩下node tail. 不然后面的node会断掉 + +#### Previous notes +- 遍历到M前, +- 存一下那个点, +- 从M开始, for loop, reverse [m~n]。 然后把三段链接在一起。 + + + + +--- + +**17. [Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Linked%20Lists.java)** Level: Easy Tags: [Linked List] + + +给两个 linked list, 问从哪个node开始, 两个 linked list 开始有重复? + +#### Basics +- 长短list,找重合点 +- 长度不同的话,切掉长的list那个的extra length +- 那么起点一样后,重合点就会同时到达 +- Time O(n) * 2, constant space + + + +--- + +**18. [Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Sorted%20Lists.java)** Level: Easy Tags: [Linked List] + + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + + + +--- + +**19. [LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)** Level: Hard Tags: [Design, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度。 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) + +##### 巧妙点 +- 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +- 2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +- 直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法: +- moveToHead() +- insertHead() +- remove() + +#### O(n) 检查重复 +- timeout method, 天真的来了一个O(n) 的解法,结果果然timeout. +- 一个map存数值。一个queue来存排位。 +- 每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + + + + +--- + +**20. [Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List.java)** Level: Easy Tags: [Linked List] + + +从Linked list 里面摘掉重复元素, 只留下unique元素. + +#### Linked List +- sorted list, 重复元素都在一起 +- 知道如何构建Linked List. +- 一点遇到重复元素: node.val == node.next.val, 就去掉. +- 用一个dummy node 来跑路 +- 注意: +- 只有当没有重复的时候, 才node = node.next; +- 有重复的时候, 当后面第三个元素被提上来之后, 还是可能跟当下元素重复, 所以不能前移node. +- ex: A -> A -> A +- while loop 里面check node 和 node.next 比较好, 这样ending position会非常清晰 + + + +--- + +**21. [Remove Duplicates from Sorted List II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List%20II.java)** Level: Medium Tags: [Linked List] + + +从Linked list 里面摘掉重复元素: 只要重复过, 全部都删掉; 重复出现过得元素一个不留. + +#### Linked List +- sorted list, 重复元素都在一起 +- 运用 dummyHead: 如果要去掉所有重复元素, 就要有个dummyHead作为局外人在开头牵线 +- 只要发现一个 node.val == node.next.val, 就记下这个duplicated val, move forward, 过掉所有重复过的元素 +- 思想: +- 用第二个 inner while loop, 把所有的重复元素都处理干净, 然后再move forward +- 优点: outter while loop 不需要考虑太多case, 在inner loop 都把主要的business logic 解决了. + +##### 注意DummyHead 的使用 +- 当我们有了DummyHead 作为Linked List 的局外线头, 其实可以选择每次遇到duplicate, 就把更加后面的元素 强行assign 给 dummyHead.next +- 下面还尝试过一种做法: 但是需要考虑的edge case 太多了: 不断移动node, 知道不重复, assign prev.next = node. +- 这样的做法比较直白, 但是需要考虑很多edge case, 而且并没有很好利用到 dummy head, 注意规避. + +##### Previous Note +- 斩草除根。 +- 多个node,check node.next ?= node.next.next + + + + +--- + +**22. [Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +给一个single linked list, 右移k steps. k non-negative. + +#### Linked List basics +- 记得用dummy.next来存head. +- 特殊: 这里k可能大于list总长. 写一写linked node 移动的步数, 然后 k = k % n. +- 找到newTail, newHead, 然后利用dummy, 换位子 + + + +--- + +**23. [Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + +time: O(n) +space: O(1) + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List +- Basic Implementation of copy linked list: +- use node and dummy to hold new list, 遍历head.next .... null. +- Map 在这里用来: 1. avoid creating same node; 2. return the item if existing +- map 的 key全部是old object, 新的key全部是 newly created object +- 每一步都check map里面有没有head. 没有? 加上 +- 每一步都check map里面有没有head.random. 没有? 加上 + + + +--- + +**24. [Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Linked List +- linked list 不能像partitioin array一样从两边遍历 +- 把小于value的加在前半段, 把 >= value的加在后半段 +- 做法很普通: 建造两个list, midTail pointer, post pointer +- 把满足条件(=x)的数字分别放到两个list里面 +- 记得用dummyNode track head. +- 最终midTail.next = post链接起来。 + + + +--- + +**25. [Reorder List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reorder%20List.java)** Level: Medium Tags: [Linked List] + + +给一个Linked list, reorder: 从head/tail 两个方向 向中间进发, re-order like: one node at a time, + +#### Linked List 功能大集合 +- reverse list, find mid of list, merge two list +- 先find mid, 然后把 mid.next reverse了, 最后merge 两段. +- 注意, 用完mid.next之后, 一定要 mid.next = null, 不然merge会出问题 + + + +--- + +**26. [Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/Nth%20to%20Last%20Node%20in%20List.java)** Level: Easy Tags: [Linked List] + + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth + + + +--- + +**27. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**28. [Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)** Level: Medium Tags: [Linked List, Stack, Tree] + +time: O(n) +space: O(n) + +给一个BST, convert成 sorted doubly DoublyListNode. + +#### Inorder Traversal, Linked List +- 会iterative traverse Binary Search Tree(Stack && handle left-dig-down) +- create Doubly-ListNode, 注意用一个dNode作为tail node of the list + +##### Iterative inorder traversal +- 在check right node的事后, +- 不论right == null or != null, 每次都要强行move to right. +- 如果不node = node.right, +- 很可能发生窘境: +- node always = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 + + + +--- + +**29. [Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- 记得k lists 需要是已经sort好的 +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: +- 1. 不要忘记customized priority需要一个customized new Comparator() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**30. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + diff --git a/review/Lint.md b/review/Lint.md new file mode 100644 index 0000000..5a8e43c --- /dev/null +++ b/review/Lint.md @@ -0,0 +1,158 @@ + + + +## Lint (9) +**0. [Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**1. [Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 + + + +--- + +**2. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**3. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**4. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**5. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**6. [Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的sum. + +#### Segment Tree + Binary Search +- 其实是segment tree 每个node上面加个sum +- 记得Segment Tree methods: Build, Query +- Note: 存在SegmentTreeNode里面的是sum. 其他题目可能是min,max,count ... or something else. + + + +--- + +**7. [Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)** Level: Hard Tags: [Binary Search, Lint, Segment Tree] + + +SegmentTree大集合. Methods: `build, query, modify`. 不难。只是要都记得不犯错. + +#### Segment Tree +- build: recursively build children based on index-mid and link to curr node +- query: recursively try to find `node.start == targetStart && node.end == targetEnd`. Compare with node.mid +- modify: recursively try to find `node.start == targetStart && node.end == targetEnd`; modify and recursively assign upper interval with updated interval property. + + + +--- + +**8. [Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字, array size = n. 给一串query: 每个query是一个数, 目的找 count# items smaller than query element. + +#### Segment Tree +- 和平时的segment tree问题不同。 [0 ~ n] 代表实际数字: based on real value的segment tree. +- Modify时,把array里面的value带进去,找到特定的位子, 然后count + 1. +- 最终在SegmentTree leaf上面全是array里面实际的数字。 +- node.count: 在node range里面的有多少个数字 + +##### right use of modify() +- build() 只是 empty segment tree, 没有property +- modify() 需要: 1. 找到left, update count+=1; 2. aggregate all parent when after returning +- 所以每一个modify 都是在整个path上所有的node上 + count + +##### query trick +- 在query前,给进去的start和end是: 0 ~ value-1. +- `value-1`: 找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. + +##### About other basic segment tree setup +- [那么其他做过的SegmentTree是怎么样呢?] +- 那些构成好的SegmentTree(找min,max,sum)也有一个Array。但是构成Tree时候,随Array的index而构架。 +- 也就是说,假如有Array[x,y,....]:在leaf,会有[0,0] with value = x. [1,1] with value = y. +- [但是这题] +- 构成时,是用actual value.也就是比如Array[x,y,....]会产生leaf:[x,x]with value = ..; [y,y]with value =... +- 其实很容易看穿: +- 若给出一个固定的array构成 SegmentTree,那估计很简单:按照index从0~array.lengh,leaf上就是[0,0] with value = x. +- 若题目让构造一个空心SegmentTree, `based on value 0 ~ n-1 (n <= 10000)`, 然后把一个Array的value modify 进去。 +- 这样八成是另外一种咯。 + + + +--- + diff --git a/review/Math.md b/review/Math.md new file mode 100644 index 0000000..4838d45 --- /dev/null +++ b/review/Math.md @@ -0,0 +1,670 @@ + + + +## Math (38) +**0. [Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)** Level: Medium Tags: [Hash Table, Math] + + +TODO: no need of hashMap, just use set to store the existing + +不难想到处理除法:考虑正负,考虑小数点前后。主要是小数点以后的要着重考虑。 +很容易忽略的是integer的益处。 + + +--- + +**1. [Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)** Level: Medium Tags: [Backtracking, Math] + + +TODO: what about regular DFS/backtracking to compute the kth? dfs(rst, list, candiate list, k) + +k是permutation的一个数位。而permutation是有规律的。 + +也就是说,可以根据k的大小来判断每一个数位的字符(从最大数位开始,因为默认factorio从最大数位开始变化)。 + +于是先求出n!, 然后 k/n!就可以推算出当下这一个数位的字符。然后分别把factorio 和 k减小。 + +另外, 用一个boolean[] visited来确保每个数字只出现一次。 + +这个方法比计算出每个permutation要efficient许多。 + + + + +--- + +**2. [Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)** Level: Easy Tags: [Math] + + + + +--- + +**3. [Power of Three.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Three.java)** Level: Easy Tags: [Math] + + +方法1: +Power of 3: 3 ^ x == n ? +意思是 n / 3 一直除, 最后是可以等于1的, 那么就有了 n/=3, check n%3, 最后看结果是不是整除到1的做法. 用while loop. + +方法2: +如果n是power of 3, 那么 3 ^ x的这个 x一定是个比n小的数字. 那么可以在 0 ~ n 之间做binary serach, 但是就比较慢. + +方法3: +巧妙的想法.最大的3^x integer是 3^19. 那么找到这个数, 一定可以被n整除. 一步到位. + + + +--- + +**4. [Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)** Level: Easy Tags: [Array, Math] + + +简单的实现, 加1, 进位. 唯一取巧的地方, 最后如果要多一位, 一定是10000...这个模式, 可以走捷径, 直接来个+1size的array, 然后第一位=1. +注意,转换成long也不合理,用太多memory. + + +--- + +**5. [Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)** Level: Easy Tags: [Bit Manipulation, Math] + + +跟powerOfThree一样: 可以loop, check mod; 也可以用binary search找合适的数字. + + + +--- + +**6. [Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)** Level: Medium Tags: [Hash Table, Math] + + +其实想到了切入点, 是个可难可简单的题目. 这里的encode就是想办法把url存起来, 然后给个 key. +那么具体怎么做这个key, 简单就可以用一个map, 然后counting. 复杂一点就可以做random letter/number组成key. + + + +--- + +**7. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**8. [Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Perfect%20Square.java)** Level: Review Tags: [Binary Search, Math] + + +Binary找sqrt. 基本 mid+1, mid-1 template. +注意: define index as long. + + + +--- + +**9. [Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)** Level: Medium Tags: [Binary Search, Math] + + +傻做就O(n), 要更好就考虑O(logN). +减少重复计算, 一切两半. + +注意: +- n/2的奇数偶数 +- n的正负 +- n == 0的情况, x == 0, x == 1 的情况. + + +--- + +**10. [Add Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Digits.java)** Level: Easy Tags: [Math] + + +方法1: 普通做法就是按照题意, double-while loop把数字加起来. 第一层循环是O(n), 然后第二层循环就少很多数位, overall O(n) + +方法2: 找数学规律, 每过9个数字, 取mod就会开始重复, 所以给所有数字取mod 就可以间接找到答案. O(1) + + + +--- + +**11. [Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. +遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + +跟Add Binary的理解方式一模一样. + +注意: +Linked List 没有天然size. +用DummyNode(-1).next来hold住结果. + + + + +--- + +**12. [Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Linked List, Math, Two Pointers] + + +LinkedList 里面有 cycle, 找到cycle的起始点(第一个重复出现的element). + +#### Slow, fast Pointer +- 快慢指针, O(1)space. +- 1. 确认有cycle后 2. 数学问题:找到开头. +- 当head == slow.next时候, head就是cycle starting point. +- 也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +#### 证明 +- 1. 假设慢指针走t步, 快指针走快一倍, 也就是2t. +- 2. 我们假设cycle的长度是Y, 而进入cycle之前的长度为X. +- 3. 假设慢指针走了m圈cycle, 而快指针走了n圈cycle之后, 两个pointer相遇. +- 4. 最终在Y cycle里面的K点相遇, 也就是两个指针都在这最后一圈里面走了K 步. +- 那么: +- t = X + mY + K +- 2t = X + nY + K +- 整合公式: X + K = (n - 2m)Y +- 这里的m和n不过是整数的跑圈数, 也就是说X和K加在一起, 总归是结束cycle. X 和 K 互补 +- 结论: 当slow/fast 指针在K点相遇后, 再走X步, 就到了cycle的起点, 也就是题目要求的起点. + +#### Hash Table, O(n) space + + + + +--- + +**13. [Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Integer.java)** Level: Easy Tags: [Math] + + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + + + +--- + +**14. [Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + + +#### s- qrt(int x) +- 理解题意, 从[0, x]找一个可以m*m=x的值. +- 注意, 如果找不到, 最后问考官该return一个什么值:按道理,因为return int, 会取整,那么return一个平方最close to x就可以. +- 注意 mid 用 long, 因为很可能超过最大int. + +#### sqrt(double x) +- 二分float number, 应该用精度来定义结尾. +- 还是二分, 但是判断条件变成: while ( end - start > eps) +- eps = 1e-12,也就是精度到1e-12 + + + +--- + +**15. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**16. [Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Primes.java)** Level: Easy Tags: [Hash Table, Math] + + +计数: 所有小于n的prime number. + +#### prime number定义 +- >=2的没有除自己和1以外公约数的数。 +- 还有另外一个定义方法: 这个n,有没有小于n的一个i, 而达到: i * i + # of i = n. 如果有,那就不是 prime + +#### Steps +- 一个boolean长条,存isPrime[]。 然后从i=2, 全部变true. +- hash key: the number itself +- 然后利用这个因子的性质,非prime满足条件: self*self, self * self + self ... etc. +- 所以就check每一个j, j+i, j+i+i, 然后把所有non-prime全部mark成false. +- 最后,数一遍还剩下的true个数就好了 + + + +--- + +**17. [Excel Sheet Column Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Number.java)** Level: Easy Tags: [Math] + + +#### Math +- 26位的运算, 根据10位运算去思考 +- 'A' - 'A' = 0. 所以 char - 'A' + 1 = 题目里的对应数位 +- 或者: 26位运算和10位一样:num += 每位的digit * Math.pow(26, 数位号) + + + + +--- + +**18. [Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Title.java)** Level: Easy Tags: [Math] + + +#### 基本换算 +- 26位 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + + + +--- + +**19. [Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)** Level: Easy Tags: [DP, Math, Memoization] + + +#### Memoization +- fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + +#### DP array. +- 滚动数组, 简化DP + +#### recursively calculate +- recursively calculate fib(n - 1) + fib(n - 2). 公式没问题, 但是时间太长, timeout. + + + + +--- + +**20. [Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)** Level: Easy Tags: [Array, Bit Manipulation, Math] + + +给一串unique数字, 数字取自 [0 ~ n], 无序, 找第一个skipped的数字. + +#### Swap +- 跟First Missing Positive 非常像, 只有一行代码的区别. +- swap 所有的数字, 到自己的correct position +- 最后一个for loop找到错位的index, 也就是缺的数字. + +#### Bit Manipulation +- XOR will only retain bits that are different 1 ^ 0 = 1, but 0^0, 1^1 == 0 +- Use that feature, 把所有value都和index XOR了 +- 剩下的多余的数字, 其实是那个index无法被XOR消掉, 也就是那个缺的number value. +- 注意: 题目告诉数字是 [0 ~ n], 然而缺一个数字, 那么在[0 ~ n - 1] 里面, 最大的数字(不管缺没缺), 一定是 n = nums.length. + +#### HastSet +- 全存, 找missing +- O(n) space, 不合题意 + +#### sorting +- sort, 找1st missing +- O(n log n) 太慢, 不合题意 + + + +--- + +**21. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + +**22. [Ugly Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number.java)** Level: Medium Tags: [Math] + + +LeetCode: 判断数字是否是ugly number. (definition: factor only have 2, 3, 5) + +#### Math +- 看是否可以整除. +- 看整除最终结果是否== 1 + +LintCode: 找kth ugly number, 应该与 Ugly Number II是一样的 + +- 方法1: PriorityQueue排序。用ArrayList check 新的ugly Number是否出现过。 +- 方法1-1:(解释不通,不可取)用PriorityQueue排序。神奇的3,5,7走位:按照题目答案的出发,定了3,5,7以什么规律出现。但是题目并没有特殊表明。 +- 方法2: DP . Not Done yet. + + + + +--- + +**23. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + +**24. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + +**25. [Number of Digit One.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Digit%20One.java)** Level: Hard Tags: [Math] + + +Pure math problem, not quite representative + +Explanation +https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython + + + +--- + +**26. [Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)** Level: Medium Tags: [DP, Math] + + +具体看题目: count # of valid rectangles (four corner are 1) in a grid[][]. + +#### basic thinking + Math +- Fix two rows and count matching columns +- Calculate number rectangles with `combination` concept: +- total number of combinations of pick 2 points randomly: count * (count - 1) / 2 + +#### DP +- TODO. HOW? + +#### Brutle +- O(m^2 * n^2), times out + + + +--- + +**27. [Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Multiply%20Strings.java)** Level: Medium Tags: [Math, String] + + +给两个integer String, 求乘积 + +#### String calculation, basic implementation +- let num1 = multipier, num2 = base. +- mutiply and save into int[m + n], without carry. Loop over num1, each row num1[x] * num2 +- move carry to the correct index and direclty save result +- calculate carry on rst[]: sb.insert(0, c) such that no need to reverse() later +- remove leading '0', but do not delete string "0" +- time,space O(mn) + +#### Previous notes. +- Bad solution: reversing makes it complicated, no need to reverse. +- 1. 数字‘123’, 在数组里面, index == 0 是 ‘1’。 但是我们平时习惯从最小位数开始乘积,就是末尾的'3'开始。 +- 所以!翻转两个数字先!我去。这个是个大坑。 +- 2. 乘积product,和移动Carrier都很普通。 +- 3. !!最后不能忘了再翻转。 +- 4. 最后一个看坑。要是乘积是0,就返回‘0’。 但是这个其实可以在开头catch到没必要做到结尾catch。 +- 用到几个StringBuffer的好东西: reverse(), sb.deleteCharAt(i) +- 找数字,或者26个字母,都可以: s.charAt(i) - '0'; s.charAt(i) - 'a'; + + + +--- + +**28. [Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)** Level: Medium Tags: [Array, Math] + + +#### Array, Math +- 这个问题更在于问题本身的分析 (而且还有多余条件); 最终的for loop 也比较不standard. +- People younger than 15 cannot make requests due to the first rule. +- From the age of 15, people can make requests to the same age: a[i] * (a[i] - 1) requests. +- People can make requests to younger people older than 0.5 * i + 7: a[j] * a[i] requests. +- The third rule is redundant as the condition is already covered by the second rule. +- TODO: the approach. + + + +--- + +**29. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + +time: O(n) +space: O(n) + +给一串点, 找是否有一个所有点中间的, 跟y-axis平行的中线. + +#### Hash Table +- 1. store in `Map>`, 2. iterate over map, check head,tail against the mid point +- 很好的细节题目: +- 1. 除以2, 需要存double +- 2. (问面试官)可以有重复的点! 所以track `set` +- 3. 处理 left==right时候, 就当做两个点来处理. +- 4. 存进set里面没有sort, 但是最后做check的时候, 需要sort list +- 时间: visit all nodes 两遍, O(n) + + + +--- + +**30. [Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + +time: O(n) + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**31. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**32. [Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)** Level: Hard Tags: [Enumeration, Math, String] + + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 + +#### 注意 +- StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 +- 注意加 " " 的时候, 如果多余, 要`trim()` +- 注意, 小于20的数字, 有自己的特殊写法, 需要额外handle +- 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 + + + + +--- + +**33. [String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- check sign, leading-0, overall size > 11, check max/min in Long format +- if passed all tests, parseInt() + +#### regular expression +- if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**34. [Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Roman%20to%20Integer.java)** Level: Easy Tags: [Math, String] + + +#### String +- 熟悉罗马字母规则 +- 1. 'I V X L C D M' 分别代表的数字 +- 2. 列举combo的情况,需要从原sum里面减掉多加的部分: 'IV, IX'减2, 'XL, XC'减20, 'CD, CM'减200. +- Leading `I(1*2)`, `X(10*2)`, `C(100*2)` causes double counting + +https://en.wikipedia.org/wiki/Roman_numerals + + + +--- + +**35. [Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math] + + +根据题意枚举, 再根据规则basic implementation + +#### Alter input + +#### HashTable + Two Pointer + + + +--- + +**36. [Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)** Level: Easy Tags: [Math, String, Two Pointers] + + +#### Two pointers +- Use two pointers i, j to track the 2 strings +- Add when i and j are applicable. While (i >= 0 || j >= 0) +- StringBuffer.insert(0, x); +- handle carry + +#### wrong: convert to int +- 土办法没技术,把binary换成数字,加起来,再换成binary +- 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**37. [Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)** Level: Hard Tags: [DFS, Greedy, Math] + + +#### Greedy, Iterative +- For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +- We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +- Track the newly formed string; if new, add the new char to overall result +- Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] +- Same concept as dfs + +#### DFS +- Same concept: use window to cut out tail, and append with new candidate +- do this for k^n = Math.pow(k, n) times + + + +--- + diff --git a/review/Matrix DFS.md b/review/Matrix DFS.md new file mode 100644 index 0000000..bdd1096 --- /dev/null +++ b/review/Matrix DFS.md @@ -0,0 +1,59 @@ + + + +## Matrix DFS (2) +**0. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**1. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + diff --git a/review/MaxHeap.md b/review/MaxHeap.md new file mode 100644 index 0000000..ce56917 --- /dev/null +++ b/review/MaxHeap.md @@ -0,0 +1,109 @@ + + + +## MaxHeap (4) +**0. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**1. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**2. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**3. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + diff --git a/review/Memoization.md b/review/Memoization.md new file mode 100644 index 0000000..b3dc8d9 --- /dev/null +++ b/review/Memoization.md @@ -0,0 +1,388 @@ + + + +## Memoization (12) +**0. [Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + +#### DP +- 找对方程dp[x], 积累到amount x最少用多少个coin: #coin是value, index是 [0~x]. +- 子问题的关系是: 如果用了一个coin, 那么就应该是f[x - coinValue]那个位置的#coins + 1 + +##### initialization +- 处理边界, 一开始0index的时候, 用value0. +- 中间利用Integer.MAX_VALUE来作比较, initialize dp[x] +- 注意, 一旦 Integer.MAX_VALUE + 1 就会变成负数. 这种情况会在coin=0的时候发生. + +##### Optimization +- 方法1: 直接用Integer.MAX_VALUE +- 方法2: 用-1, 稍微简洁一点, 每次比较dp[i]和 dp[i - coin] + 1, 然后save. 不必要做多次min比较. + +#### Memoization +- dp[i] 依然表示: min # of coints to make amount i +- initialize dp[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录dp[amount] 如果已经给过value, 不要重复计算, 直接return. +- 但是这道题没必要强行做memoization, 普通DP的状态和方程相对来说很好找到 + + + +--- + +**1. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**2. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**3. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**4. [Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)** Level: Easy Tags: [DP, Memoization, Sequence DP] + + +每一步可以走1步或者2步, 求总共多少种方法爬完梯子. + +#### Recursive + Memoization +- 递归很好写, 但是重复计算, timeout. time: O(2^n) +- O(2^n): each n can spawn 2 dfs child, at next level, it will keep spawn. Total 2^n nodes will spawn. +- 用全局变量int[] memo 帮助减少重复计算 +- O(n) time, space + +#### DP +- 加法原理, 最后一步被前两种走法决定: dp[i] = dp[i - 1] + dp[i - 2] +- 基础sequence DP, int[] dp = int[n + 1]; +- DP[]存的是以 1-based index的状态 +- dp[i]: count # of ways to finish 前i个 台阶 +- 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1] +- dp[0]往往是有特殊状态的 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**5. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**6. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**7. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**8. [Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)** Level: Easy Tags: [DP, Math, Memoization] + + +#### Memoization +- fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + +#### DP array. +- 滚动数组, 简化DP + +#### recursively calculate +- recursively calculate fib(n - 1) + fib(n - 2). 公式没问题, 但是时间太长, timeout. + + + + +--- + +**9. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + +**10. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**11. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + diff --git a/review/Merge Sort.md b/review/Merge Sort.md new file mode 100644 index 0000000..8554b1b --- /dev/null +++ b/review/Merge Sort.md @@ -0,0 +1,117 @@ + + + +## Merge Sort (4) +**0. [MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/MergeSort.java)** Level: Medium Tags: [Merge Sort, Sort] + + +#### Merge Sort +- Divide and conquer, recursively +- 先从中间分段, merge sort 左边 (dfs), merge sort 右边 +- 最后merge起来 +- merge的时候因为是做int[], 所以没办法必须要O(n) space +- Time O(nlogn), Space O(n) + + + +--- + +**1. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**2. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**3. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + diff --git a/review/MinHeap.md b/review/MinHeap.md new file mode 100644 index 0000000..6a06be7 --- /dev/null +++ b/review/MinHeap.md @@ -0,0 +1,252 @@ + + + +## MinHeap (9) +**0. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**1. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**2. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high(Comparator.comparing(a -> a.val));` is slower + + + +--- + +**4. [Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/Heapify.java)** Level: Medium Tags: [Heap, MinHeap] + + +Turn unsorted array into a min-heap array, where for each A[i], + +A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i]. + +#### Heap +- Heap用的不多. 得用一下, 才好理解. 通常default 的PriorityQueue就是给了一个现成的min-heap: +- 所有后面的对应element都比curr element 小。 +- Heapify里面的**siftdown**的部分: +- 只能从for(i = n/2-1 ~ 0), 而不能从for(i = 0 ~ n/2 -1): 必须中间开花,向上跑的时候才能确保脚下是符合heap规则的 + +#### Heapify/SiftDown做了什么? +- 确保在heap datastructure里面curr node下面的两个孩子,以及下面所有的node都遵循一个规律 +- 比如在这里,若是min-heap,就是后面的两孩子都要比自己大。若不是,就要swap。 + +#### min-heap的判断规律: +- for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. +- siftdown时:在curr node和两个child里面小的比较。如果的确curr < child, 搞定,break while. +- 但若curr 并不比child小,那么就要换位子,而且继续从child的位子往下面盘查。 + + + +--- + +**5. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**6. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**7. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**8. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + diff --git a/review/MiniMax.md b/review/MiniMax.md new file mode 100644 index 0000000..84f9305 --- /dev/null +++ b/review/MiniMax.md @@ -0,0 +1,58 @@ + + + +## MiniMax (2) +**0. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**1. [Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)** Level: Medium Tags: [DP, MiniMax] + + +Detailed in `Coins in a Line III` + + + +--- + diff --git a/review/Minimum Binary Tree.md b/review/Minimum Binary Tree.md new file mode 100644 index 0000000..2668fb1 --- /dev/null +++ b/review/Minimum Binary Tree.md @@ -0,0 +1,72 @@ + + + +## Minimum Binary Tree (3) +**0. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**1. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**2. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + diff --git a/review/Monotonous Stack.md b/review/Monotonous Stack.md new file mode 100644 index 0000000..94782ae --- /dev/null +++ b/review/Monotonous Stack.md @@ -0,0 +1,31 @@ + + + +## Monotonous Stack (1) +**0. [Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)** Level: Hard Tags: [Array, Monotonous Stack, Stack] + + +给n个bar,组成柱状图histogram. 求在这一排柱状图里面可以找到的面积最大的长方形. + +思考: 找长方形面积, 无非是找两个index, 然后底边长度 * height. + +#### Monotonous Stack +- 重点是根据找Histogram里面rectangle的性质, 维持一个单调递增的Stack +- 在loop over indexes的时候: +- 如果高度>= previous peek(), 那么对于那个peek, 就意味着, 往下走, 一直走高嘛, 之前的peek总可以继续抄底 +- 什么时候不能抄底了呢? 就是有一个下降趋势的时候 +- 这时候并不是calculate所有前面的peek, 而是考虑 大于 current height的之前所有的peek. +- 把这些peek到 current height 前一格的rectangle全部找出来: stack.pop() +- 这个stack.pop()的过程里面, 其实没有算上 current height, 因为需要留到下一轮, 把current index加进stack 再说 +- 为什么用stack? 因为需要知道连续递增的peek, stack.peek() O(1), 好用 + 而其实不用stack, 也可以用其他方式记录所有height, 只不过要 O(n)去找peek不方便 + +#### 知识点 +- 理解monotonous stack 是如何被维护的 +- 维护monotonous stack 是题目需要, 而不是stack本身性质, 是一种借助 stack.peek() O(1)的巧妙用法. + + + + +--- + diff --git a/review/Partition DP.md b/review/Partition DP.md new file mode 100644 index 0000000..6934503 --- /dev/null +++ b/review/Partition DP.md @@ -0,0 +1,176 @@ + + + +## Partition DP (5) +**0. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**1. [Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)** Level: Hard Tags: [Binary Search, DP, Partition DP] + + +给一串书pages[i], k个人, pages[i] 代表每本书的页数. k个人从不同的点同时开始抄书. + +问, 最快什么时候可以抄完? + +#### Partition DP +- 第一步, 理解题目要求的问题: 前k个人copy完n本书, 找到最少的用时; 也可以翻译成: `n本书, 让k个人来copy, 也就是分割成k段`. +- 最后需要求出 dp[n][k]. 开: int[n+1][k+1]. +- 原理: +- 1. 考虑最后一步: 在[0 ~ n - 1]本书里, 最后一个人可以选择copy 1 本, 2 本....n本, 每一种切割的方法的结果都不一样 +- 2. 讨论第k个人的情况, 在 j = [0 ~ i] 循环. 而循环j时候最慢的情况决定 第k个人的结果(木桶原理): `Math.max(dp[j][k - 1], sum)`. +- 3. 其中: `dp[j][k-1]` 是 [k-1]个人读完j本书的结果, 也就是著名的`上一步`. 这里循环考虑的是第k个人不同的j种上一步 : ) +- 4. 循环的结果, 是要存在 dp[i][k] = Math.min(Math.max(dp[j][k - 1], sum[j, i]), loop over i, k, j = [i ~ 0]) +- Time: O(kn^2), space O(nk) + +##### Init +- Init: dp[0][0] = 0, 0个人0本书 +- Integer.MAX_VALUE的运用: +- 当 i = 1, k = 1, 表达式: dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); +- 唯一可行的情况就只有一种: i=0, k=0, 刚好 0 个人 copy 0 本书, dp[0][0] = 0. +- 其他情况, i = 1, k = 0, 0 个人读 1本书, 不可能发生: 所以用Integer.MAX_VALUE来冲破 Math.max, 维持荒谬值. +- 当 i=0, k=0 的情况被讨论时候, 上面的方程式才会按照实际情况计算出 dp[i][k] +- 这道题的init是非常重要而tricky的 + +##### 计算顺序 +- k个人, 需要一个for loop; +- k个人, 从copy1本书开始, 2, 3, ... n-1,所以 i=[1, n], 需要第二个for loop +- 在每一个i上, 切割的方式可以有[0 ~ i] 中, 我们要计算每一种的worst time + +##### 滚动数组 +- [k] 只有和 [k - 1] 相关 +- Space: O(n) + +#### Binary Search +- 根据: 每个人花的多少时间(time)来做binary search: 每个人花多久时间, 可以在K个人之内, 用最少的时间完成? +- time variable的范围不是index, 也不是page大小. 而是[minPage, pageSum] +- validation 的时候注意3种情况: 人够用 k>=0, 人不够所以结尾减成k<0, 还有一种是time(每个人最多花的时间)小于当下的页面, return -1 +- O(nLogM). n = pages.length; m = sum of pages. + + + + +--- + +**2. [Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Partition DP +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- 确定末尾的2种状态: single letter or combos. 然后计算出单个letter的情况, 和双数的情况 +- 定义`dp[i] = 前i个digits最多有多少种decode的方法`. new dp[n + 1]. +- 加法原理: 把不同的情况, single-digit, double-digit 的情况加起来 +- dp[i] += dp[i - x], where x = 1, 2 +- note: calculate number from characters, need to - '0' to get the correct integer mapping. +- 注意: check value != '0', 因为'0' 不在条件之中(A-Z) +- Space, Time O(n) + +#### 引申 +- 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + + + +--- + +**3. [Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)** Level: Hard Tags: [DP, Enumeration, Partition DP] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +其中字符可能是 "*", 可以代表 [1 - 9] + +#### DP +- 乘法原理 +- 跟decode way I 一样, 加法原理, 切割点时: 当下是取了 1 digit 还是 2 digits 来decode +- 定义dp[i] = 前i个digits最多有多少种decode的方法. new dp[n + 1]. +- 不同的情况是: 每一个partition里面, 如果有"*", 就会在自身延伸出很多不同的可能 +- 那么: dp[i] = dp[i - 1] * (#variations of ss[i]) + dp[i - 2] * (#variations of ss[i,i+1]) + +##### 特点 +- 枚举的能力: 具体分析 '*' 出现的位置, 枚举出数字, 基本功. +- 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) +- 理解取MOD的原因: 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. +- 枚举好以后, 其实这个题目的写法和思考过程都不难 + + + + +--- + +**4. [Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)** Level: Hard Tags: [DP, Partition DP] + + +给一个String s, 找出最少用多少cut, 使致 切割出的每一个substring, 都是palindrome + +#### Partition DP +- Find minimum cut: 分割型DP +- dp[i]: 最少cut多少刀, 使得前 i 长度的string, 割出来都是palindrome +- 最终要得到 dp[n], 所以 int[n + 1] +- 移动切刀, 看在哪里切, index j in [0 ~ i] +- 考虑[j, i - 1] 是否是回文串, 如果是, 那么: dp[i]= min(dp[i], d[j] + 1). +- note: 估计遍历 j 的时候, 反过来遍历也可以. + +#### 计算Palindrome的优化 +- 利用palindrome的性质, 可以算出 boolean palindrome[i, j]的情况. +- 找一个任意mid point: +- 1. 假设palindrome是奇数长度, 那么 mid 是单独的字符, 而两边的字符 [mid-1], [mid+1] 应该完全相等. +- 2. 假设palindrome是偶数长度, 那么 [mid] 和 [mid + 1] 这样位置的字符应该相等. +- 这样做出来 palindrome[i, j]: 从字符 i 到 字符 j 的 substring 是否是 palindrome +- 这样就给我们的问题合理降维, 目前是time: O(n^2). +- 不然求一次palindrome, 就是n, 会变成O(n^3) + +#### Previous Notes +- Double for loop 检查每种substring string (i~j). 若i,j相邻或者同点,那么肯定isPal;否则,i,j之间的(i+1, j-1)一定得isPal。 +- 看上去,在检查i,j的时候,中间按的(i+1, j-1)怎么可能先知道? 其实不然..在j慢慢长大的时候,所有的0~j的substring都检查过。所以isPal[i+1][j-1]一定是已经知道结果的。 +- okay.那么假如以上任意一种情况成立,也就是说isPal[i][j] == true。那就要判断,切到第一层循环参数j的末尾点时,有多少种切法? +- 想法很顺:我们naturally会想到,把i之前的cut加上i~j之间发生的不就好了。 +- 反正现在j不变,现在就看吧i定在哪里,cut[i - 1]是否更小/最小; 再在cut[i-1]基础上+1就完了。 +- 当然,如果i==0, 而 i~j又是isPal,那没啥好谈的,不必切,0刀。 +- 最终,刷到cut[s.length() - 1] 也就是最后一点。 return的理所应当。 + + + + +--- + diff --git a/review/Partition.md b/review/Partition.md new file mode 100644 index 0000000..f285ea9 --- /dev/null +++ b/review/Partition.md @@ -0,0 +1,75 @@ + + + +## Partition (3) +**0. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**1. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**2. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + diff --git a/review/Permutation.md b/review/Permutation.md new file mode 100644 index 0000000..dab57b5 --- /dev/null +++ b/review/Permutation.md @@ -0,0 +1,68 @@ + + + +## Permutation (3) +**0. [Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)** Level: Medium Tags: [Backtracking, Permutation] + + +TODO: need to review permutation + +permutation的综合题: +1. validate Input 是不是可以做palindromic permutation. 这个就是(Palindrome Permutation I) +2. 顺便存一下permutation string的前半部分和中间的single character(if any) +3. DFS 做unique permutation: given input有duplicate characters。 + + + +--- + +**1. [Shuffle an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Shuffle%20an%20Array.java)** Level: Medium Tags: [Permutation] + + +像shuffle music 一样, 做一套shuffle array的functions: + +shuffle() 给出random的permutation + +reset() 给出最初的nums + +#### Permutation +- Permutation 实际上就是在list/array/... 上面给元素换位置 +- 硬换位置, 每次换的位置不同, 用random来找到要换的index +- 维持同一个random seed +- O(n) + +##### Note +- compute all permutations 太慢, 不可行. + + + +--- + +**2. [Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)** Level: Medium Tags: [Backtracking, DFS, Permutation] + + +#### Recursive: Backtracking +- Given a remaining list: 取, 或者不取 +- always iterate over full `nums[]`, use list.contains() to check if item has been added. +- Improvement: maintain list (add/remove elements) instead of 'list.contains' +- time O(n!): visit all possible outcome +- T(n) = n * T(n-1) + O(1) + +#### Iterative: Insertion +- 插入法: +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- 但是比dfs要快, 因该是因为 # of checks 少: 不需要check list.size(), 不需要maintain remaining list. + +#### Previous Notes +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- A bit slower, possibly because of the polling and saving the entire list every time + + + + +--- + diff --git a/review/PreProduct.md b/review/PreProduct.md new file mode 100644 index 0000000..cc6c1ed --- /dev/null +++ b/review/PreProduct.md @@ -0,0 +1,21 @@ + + + +## PreProduct (1) +**0. [Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + +time: O(n) +space: O(1) + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 看第二个解答, 进一步简化了代码 +- PreProduct, 跟preSum的感觉有点像, 就是差一位. + + + +--- + diff --git a/review/PreSum.md b/review/PreSum.md new file mode 100644 index 0000000..615804e --- /dev/null +++ b/review/PreSum.md @@ -0,0 +1,233 @@ + + + +## PreSum (10) +**0. [Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)** Level: Review Tags: [Array, Binary Search, PreSum] + + +给int[] nums 和 window min size k. window size可以大于K. 找最大的连续数列average value. + +- Binary Search的思想, 用在所要找的这个 average sum 上面. 大小是在[min, max]之中 +- 找k的时候, 是>=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + + + +--- + +**1. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**2. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**3. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**4. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**5. [Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)** Level: Medium Tags: [Array, Hash Table, PreSum] + + +给一个int[][] matrix, 找一个sub matrix, where the sum == 0. + +#### PreSum的思想 +- 算出一个右下角点(i,j)到(0,0)的大小: 上一块 + 左一块 + curr node - overlap area +- preSum[i][j]: sum from (0,0) to (i-1,j-1) +- same approach as `subarray sum`: use hashmap to store diff->index; if diff re-appears, that means sum of 0 has occurred +- sequence of calculation: 1. iterate over start row. 2. iterate over end row. 3. iterate over col number (this is where hashmap is stored based on) +- the iteration over col is like a screening: find previous sum and determine result +- Note: 其实并没有真的去找 `== 0` 的解答,而是根据特性来判断 `剩下的/后来加上的一定是0` + + + +--- + +**6. [Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- 就是pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**7. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**8. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**9. [Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)** Level: Medium Tags: [Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +#### Map +- use `Map` to store inline preSum and its index. +- 1. Build presum incline +- 2. Use map to cache current preSum value and its index: `Map` +- 3. Each iteration: calculate possible preSum candidate that prior target sequence. ex: `(preSum - k)` +- 4. Use the calculated preSum candidate to find index +- 5. Use found index to calculate for result. ex: calculate range. + + + +--- + diff --git a/review/Priority Queue.md b/review/Priority Queue.md new file mode 100644 index 0000000..4efd7e1 --- /dev/null +++ b/review/Priority Queue.md @@ -0,0 +1,19 @@ + + + +## Priority Queue (1) +**0. [Delete Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Digits.java)** Level: Medium Tags: [Greedy, Priority Queue] + + +#### Priority Queue +- TODO: parse into node(index, digitValue) +- find the top k, and remove from char array +- O(nlogn) time + +#### Greedy +- 数位靠前的,权值更大. 所以硬来把靠前的相对更大的(跟following digit相比)去掉。 + + + +--- + diff --git a/review/PriorityQueue.md b/review/PriorityQueue.md new file mode 100644 index 0000000..1dc1398 --- /dev/null +++ b/review/PriorityQueue.md @@ -0,0 +1,492 @@ + + + +## PriorityQueue (19) +**0. [Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### 方法1: +找是否有overlap. priorityQueue 按照start time排序好以后, 比较current和peek: current.end > peek.start? + +#### 方法2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 + + + +--- + +**1. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**2. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**3. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**4. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**5. [Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort] + + +#### Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn) + + +#### Basic Implementation +- 这里已经给了 **sorted** intervals by start point. +- 直接找到可以insert newInterval的位子. Insert +- 然后loop to merge entire interval array +- 因为给的是个list, 所以方便`intervals.remove(i)` +- remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture +- O(n) + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**6. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + +**7. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**8. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**9. [Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort] + + +#### PriorityQueue +- Use priority queue to sort by customized class interval{int dist; int x, y;}. +- Sort by larger distance and then sort by start index +- seat(): pq.poll() to find interval of largest distance. Split and add new intervals back to queue. +- leave(x): one seat will be in 2 intervals: remove both from pq, and merge to a new interval. +- 主方程写出来其实很好写, 就是 split + add interval, 然后 find + delete interval 而已. 最难的是构建data structure +- seat(): O(logn), leave(): O(n) + +##### Trick: 构建虚拟 boundary +- 如果是开头的seat, 或者是结尾的seat, 比较难handle: 一开始坐在seat=0的时候, 没有interval啊! +- Trick就是, 我们自己定义个虚拟的座位 `seat=-1`, `seat=N` +- 一开始有一个 interval[-1, N] 然后就建立了boundary. +- 从此以后, 每次split成小interval的时候: +- 遇到 `interval[-1, y]`, distance就是 `(y - 0)` +- 遇到 `interval[x, N]`, distance就是 `(N - 1 - x)` +- 当然正常的interval dist 就是 `(y - x) / 2` + +##### distance 中间点 +- Interval.dist 我们其实做的是 distance的中间点 `(y - x) / 2` +- 这里的dist是 `距离两边的距离` 而不是 x, y 之间的距离. 这里要特别注意. + +#### TreeSet +- https://leetcode.com/problems/exam-room/discuss/139885/Java-Solution-based-on-treeset/153588 + +#### Map +- how? +- TODO, not sure. + + + +--- + +**10. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**11. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**13. [Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)** Level: Medium Tags: [Heap, MinHeap, PriorityQueue] + + +Same as merge k sorted list, use priorityQueue + +#### Priority Queue +- 由Merge k sorted list启发。用PriorityQueue,存那k个首发element +- PriorityQueue需要存储单位: 自己建一个Class Node 存val, x, y index. +- 因为array里没有 'next' pointer,只能存x,y来推next element +- Not sure why `new PriorityQueue<>(Comparator.comparing(a -> a.val));` is slower + + + +--- + +**14. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**15. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**16. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + +**17. [The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)** Level: Medium Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- if already found a good/shorter route, skip +- `if (distMap[node.x][node.y] <= node.dist) continue;` +- This always terminates the possibility to go return to original route, because the dist will be double/higher + + + +--- + +**18. [The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)** Level: Hard Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- 跟 Maze I, II 类似, 用一个 Node[][] 来存每一个(x,y)的state. +- Different from traditional BFS(shortest path): `it terminates BFS when good solution exists (distance), but will finish all possible routes` +- 1. `Termination condition`: if we already have a good/better solution on nodeMap[x][y], no need to add a new one +- 2. Always cache the node if passed the test in step1 +- 3. Always offer the moved position as a new node to queue (as long as it fits condition) +- 4. Finally the item at nodeMap[target.x][target.y] will have the best solution. + + + +--- + diff --git a/review/Queue.md b/review/Queue.md new file mode 100644 index 0000000..cd08bb3 --- /dev/null +++ b/review/Queue.md @@ -0,0 +1,71 @@ + + + +## Queue (3) +**0. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**1. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**2. [Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison + + + +--- + diff --git a/review/Quick Select.md b/review/Quick Select.md new file mode 100644 index 0000000..869cb00 --- /dev/null +++ b/review/Quick Select.md @@ -0,0 +1,22 @@ + + + +## Quick Select (1) +**0. [Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)** Level: Easy Tags: [Array, Quick Select, Quick Sort] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- quickSelect 可以找到 kth 最小的元素 +- 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- quick select 的template要熟悉一下, 一下子可能想得到, 但写不出来 +- 主要步骤: partition, dfs, only recur on one part of the array + + + + +--- + diff --git a/review/Quick Sort.md b/review/Quick Sort.md new file mode 100644 index 0000000..9fff1bf --- /dev/null +++ b/review/Quick Sort.md @@ -0,0 +1,131 @@ + + + +## Quick Sort (6) +**0. [Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)** Level: Easy Tags: [Array, Quick Select, Quick Sort] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- quickSelect 可以找到 kth 最小的元素 +- 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- quick select 的template要熟悉一下, 一下子可能想得到, 但写不出来 +- 主要步骤: partition, dfs, only recur on one part of the array + + + + +--- + +**1. [QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)** Level: Medium Tags: [Quick Sort, Sort] + + +implement quick sort. + +#### Quick Sort +- 首先partition. 返还一个partition的那个中间点的位置: 这个时候, 所有小于nums[partitionIndex] 都应该在 partitionIndex左边 +- 然后劈开两半 +- 前后各自 quick sort, recursively +- 注意:在partition里面, 比较的时候nums[start] < pivot, nums[end]>pivot, 如果写成了 <= 会 stack overflow. +- Time O(nlogn), Space: O(1) + + + +--- + +**2. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**3. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**4. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**5. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**7. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**8. [Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的sum. + +#### Segment Tree + Binary Search +- 其实是segment tree 每个node上面加个sum +- 记得Segment Tree methods: Build, Query +- Note: 存在SegmentTreeNode里面的是sum. 其他题目可能是min,max,count ... or something else. + + + +--- + +**9. [Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)** Level: Hard Tags: [Binary Search, Lint, Segment Tree] + + +SegmentTree大集合. Methods: `build, query, modify`. 不难。只是要都记得不犯错. + +#### Segment Tree +- build: recursively build children based on index-mid and link to curr node +- query: recursively try to find `node.start == targetStart && node.end == targetEnd`. Compare with node.mid +- modify: recursively try to find `node.start == targetStart && node.end == targetEnd`; modify and recursively assign upper interval with updated interval property. + + + +--- + +**10. [Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字, array size = n. 给一串query: 每个query是一个数, 目的找 count# items smaller than query element. + +#### Segment Tree +- 和平时的segment tree问题不同。 [0 ~ n] 代表实际数字: based on real value的segment tree. +- Modify时,把array里面的value带进去,找到特定的位子, 然后count + 1. +- 最终在SegmentTree leaf上面全是array里面实际的数字。 +- node.count: 在node range里面的有多少个数字 + +##### right use of modify() +- build() 只是 empty segment tree, 没有property +- modify() 需要: 1. 找到left, update count+=1; 2. aggregate all parent when after returning +- 所以每一个modify 都是在整个path上所有的node上 + count + +##### query trick +- 在query前,给进去的start和end是: 0 ~ value-1. +- `value-1`: 找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. + +##### About other basic segment tree setup +- [那么其他做过的SegmentTree是怎么样呢?] +- 那些构成好的SegmentTree(找min,max,sum)也有一个Array。但是构成Tree时候,随Array的index而构架。 +- 也就是说,假如有Array[x,y,....]:在leaf,会有[0,0] with value = x. [1,1] with value = y. +- [但是这题] +- 构成时,是用actual value.也就是比如Array[x,y,....]会产生leaf:[x,x]with value = ..; [y,y]with value =... +- 其实很容易看穿: +- 若给出一个固定的array构成 SegmentTree,那估计很简单:按照index从0~array.lengh,leaf上就是[0,0] with value = x. +- 若题目让构造一个空心SegmentTree, `based on value 0 ~ n-1 (n <= 10000)`, 然后把一个Array的value modify 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**11. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + diff --git a/review/Sequence DFS.md b/review/Sequence DFS.md new file mode 100644 index 0000000..9e06401 --- /dev/null +++ b/review/Sequence DFS.md @@ -0,0 +1,47 @@ + + + +## Sequence DFS (2) +**0. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**1. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + diff --git a/review/Sequence DP.md b/review/Sequence DP.md new file mode 100644 index 0000000..05d93be --- /dev/null +++ b/review/Sequence DP.md @@ -0,0 +1,570 @@ + + + +## Sequence DP (21) +**0. [Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP] + + +给两个string, A, B. 找这两个string里面的LCS: 最长公共字符长度 (不需要是continuous substring) + +#### Double Sequence DP +- 设定dp长度为(n+1), 因为dp[i]要用来表示前i个(ith)时候的状态, 所以长度需要时i+1才可以在i位置, hold住i. +- 双序列: 两个sequence之间的关系, 都是从末尾字符看起, 分析2种情况: +- 1. A最后字符不在common sequence 或者 B最后字符不在common sequence. +- 2. A/B最后字符都在common sequence. 总体count + 1. + + + +--- + +**1. [Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + +time: O(MN) +Space: O(N) + +两个字符串, A要变成B, 可以 insert/delete/replace, 找最小变化operation count + +#### Double Sequence +- 考虑两个字符串的末尾index s[i], t[j]: 如果需要让这两个字符一样, 可能使用题目给出的三种operation: insert/delete/replace? +- 先calculate最坏的情况, 3种operation count + 1; 然后在比较match的情况. +- 注意, 在i或者j为0的时候, 变成另外一个数字的steps只能是全变. +- 第一步, 空间时间都是O(MN), O(MN) +- 滚动数组优化, 空间O(N) + +##### Detail analysis +- insert: assume insert on s, `#ofOperation = (s[0 ~ i] to t[0 ~ j-1]) + 1;` +- delete: assume delete on t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j]) + 1;` +- replace: replace both s and t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j - 1]) + 1;` +- dp[i][j]代表了两个 sequence 互相之间的性质: s[0 ~ i] 转换成 s[0~j] 所需要的最少 operation count +- init: 当i==0, dp[0][j] = j; 每次都要 + j 个character; 同理, 当j==0, dp[i][0] = i; +- 而dp[i][j]有两种情况处理: `s[i] == t[j]` or `s[i] != t[j]` + +##### 何时initialize +- 这种判断取决于经验: 如果知道initialization可以再 double for loop 里面一起做, 那么可以留着那么做 +- 这样属于 `需要什么, initialize什么` +- 事后在做space optimization的时候, 可以轻易在 1st dimension 上做rolling array + +#### Search +- 可以做, 但是不建议:这道题需要找 min count, 而不是search/find all solutions, 所以search会写的比较复杂, 牛刀杀鸡. + + + +--- + +**2. [Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)** Level: Easy Tags: [DP, Memoization, Sequence DP] + + +每一步可以走1步或者2步, 求总共多少种方法爬完梯子. + +#### Recursive + Memoization +- 递归很好写, 但是重复计算, timeout. time: O(2^n) +- O(2^n): each n can spawn 2 dfs child, at next level, it will keep spawn. Total 2^n nodes will spawn. +- 用全局变量int[] memo 帮助减少重复计算 +- O(n) time, space + +#### DP +- 加法原理, 最后一步被前两种走法决定: dp[i] = dp[i - 1] + dp[i - 2] +- 基础sequence DP, int[] dp = int[n + 1]; +- DP[]存的是以 1-based index的状态 +- dp[i]: count # of ways to finish 前i个 台阶 +- 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1] +- dp[0]往往是有特殊状态的 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**3. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**4. [Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + +time: O(nm), m = # of colors +space: O(nm) + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, 但是不知道最后一个房子选什么颜色, 那么就遍历最后一个房子(i - 1)的颜色 +- 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- 考虑DP最后一个位置的情况(颜色选择):需要附带颜色status在DP[i]上: 定义二维数组, 其中一位是status +- dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. +- dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- 然后选ith 房子的 color, 再选(i-1)th 房子的color. Double for loop, skip same color + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 + + + +--- + +**5. [House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) or rolling array O(1) + +搜刮房子, 相邻的不能碰. 每个房子里有value, 求max. + +#### Sequence DP +- dp[i]: 前i个房子拿到的max gain +- 看最后结尾状态的前一个或前两个的情况,再综合考虑当下的 +- 搞清楚当下[i]的和之前[i-x]的情况的关系: 不可以连着house, 那么就直接考虑 dp[i-2]的情况 +- Sequence DP, new dp[n + 1]; + +#### Rolling Array +- [i]'只和前两个位子 [i-1], [i - 2]'相关 +- 用%2来标记 [i], [i - 1], [i - 2]三个位置. +- 其他滚动时惯用curr/prev来表示坐标, 这里%2虽然抽象, 但是更加实用. + + + + +--- + +**6. [House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)** Level: Medium Tags: [DP, Sequence DP, Status DP] + + +和House Robber I 类似, 搜刮房子, 相邻不能动. 特点是: 现在nums排成了圈, 首尾相连. + +#### Sequence DP +- dp[i][status]: 在 status=[0,1] 情况下, 前i个 房子拿到的 max rob gain. status=0, 1st house robbed; status=1, 1st house skipped +- 根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]); +- 特别的是,末尾的last house 和 first house相连. 这里就需要分别讨论两种情况: 第一个房子被搜刮, 或者第一个房子没被搜刮 +- be careful with edge case nums = [0], only with 1 element. +- Time,space: O(n) + +#### 两个状态 +- 是否搜刮了第一个房子, 分出两个branch, 可以看做两种状态. +- 可以考虑用两个DP array; 也可以加一dp维度, 补充这个状态. +- 连个维度表示的是2种状态(1st house being robbed or not); 这两种状态是平行世界的两种状态, 互不相关. + +#### Rolling array +- 与House Robber I一样, 可以用%2 来操作rolling array, space reduced to O(1) + + + +--- + +**7. [Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + +time: O(NK^2): +space: (NK) + +一排n个房子, 每个房子可涂成k种颜色, 涂每个房子的价钱不一样, 用costs[][]表示. + +costs[0][1]表示涂了index是0的房子, 用了color 1. + +规则: 相邻的两个房子不能使同一种颜色 + +求: 最少的cost + +#### DP +- 跟Paint House I 几乎一模一样, 只不过paint color更多了: k colors. +- 先考虑单纯地用dp[i]表示涂前 i 个房子的最小cost +- 但是 dp[i] 和 dp[i-1] 两个index选什么颜色会互相影响, 难讨论, 于是加状态: 序列DP被加了状态变成2D. +- 考虑最后位, 而前一位i-1又被i位的颜色限制, 于是在考虑 min dp[i] 时候, 又多了一层iteration. +- 做dp[i][j]: # cost for 前 i 个房子, 所以要先pick (i-1) 房子的cost, 然后在找出 (i-2)房子的cost +- K种颜色 => O(NK^2) +- 如果不优化, 跟Paint House I 几乎是一模一样的代码 +- Time O(NK^2), space(NK) +- Rolling array: reduce space to O(K) + +#### 注意 +- 序列型dp[i]表示'前i-1个'的结果. 所以dp最好设定为 int[n + 1] size. +- 然而, 颜色在这里是状态, 所以保留在 j: [ 0~k) +- [[8]] 这样的edge case. 跑不进for loop, 所以特殊handle. + +#### Optimization Solution +- Time: O(NK) +- 如果已知每次都要从cost里面选两个不同的最小cost,那么先把最小两个挑出来, 就不必有第三个for loop 找 min +- 每次在数列里面找: 除去自己之外的最小值, 利用最小值/次小值的思想 +- 维持2个最值: 最小值/次小值. +- 计算的时候, 如果除掉的不是最小值的index, 就给出最小值; 如果除掉的是最小值的index, 就给出次小值. +- Every loop: 1. calculate the two min vlaues for each i; 2. calcualte dp[i][j] +- 如何想到优化: 把表达式写出来, 然后看哪里可以优化 +- 另外, 还是可以rolling array, reduce space complexity to O(K) + + + +--- + +**8. [Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)** Level: Hard Tags: [Array, DP, Sequence DP] + + +比stock II 多了一个限制:只有2次卖出机会. + +#### DP加状态 +- 只卖2次, 把买卖分割成5个状态模块. +- 在状态index 0, 2, 4: 没有持有股票. 1. 一直在此状态, max profit不变; 2. 刚卖掉, dp[i][前状态] + profit +- 在状态index 1, 3: 持有股票. 1. 一直在此状态, daily profit. 2. 刚刚买进, 状态改变, 但是没有profit yet: dp[i][前状态] + +##### Partial profit +- 把每天的partial profit (diff)加在一起, 最终的overall profit是一样的. 唯一更好的是, 不需要记录中间买入的时间点. +- 什么时候会积累profit呢? +- 1. 原本就持有股票的, 如果毫无动作, 那么状态不变, 积累profit diff. +- 2. 卖出了股票, 状态改变, 积累profit diff. +- 注意: 只有在状态index: 0, 2, 4, 也就是卖掉股票的时候, 才可以积累profit + +##### Rolling Array +- [i] 只有和 [i-1] 打交道, reduce space +- O(1) space, O(n) time + +#### 找峰头 +- 找峰头;然后往下再找一个峰头。 +- 怎么样在才能Optimize两次巅峰呢?从两边同时开始找Max!(棒棒的想法) +- leftProfit是从左往右,每个i点上的最大Profit。 +- rightProfit是从i点开始到结尾,每个点上的最大profit. +- 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 +- 三个O(n),还是O(n) + + + +--- + +**9. [Best Time to Buy and Sell Stock IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20IV.java)** Level: Hard Tags: [DP, Sequence DP] + + +有int[] price of stock, 最多做 k transactions. 求最大profit. + +#### DP +- 根据StockIII, 不难发现StockIV就是把状态划分为2k+1份. 那么同样的代码, 移植. + +##### 注意1: +- 如果k很大, k>n/2, 那么长度为n的数组里面, 最多也只能n/2个transaction +- 那么题目简化为stockII, 给n数组, 无限次transaction. +- 注意, status的数量是 2k+1 +- Time O(NK), Space O(2k+1) to store the status + +##### 注意2: +- 最后状态是'没有stock'的都该考虑, 做一个 for 循环比较max. +- 当然, 来一个profit variable, 不断比较, 也是可以的. + +#### 方法2 +- (previous notes, 熟练第一种方法的思考就可以) +- 记得要理解:为什么 i-1天的卖了又买,可以和第 i 天的卖合成一次交易? +- 因为每天交易的price是定的。所以卖了又买,等于没卖!这就是可以合并的原因。要对价格敏感啊少年。 +- Inspired from here: http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html + +##### 局部最优解 vs. 全局最优解: +- local[i][j] = max(global[i – 1][j – 1] + diff, local[i – 1][j] + diff) +- global[i][j] = max(global[i – 1][j], local[i][j]) +- local[i][j]: 第i天,当天一定进行第j次交易的profit +- global[i][j]: 第i天,总共进行了j次交易的profit. + +- local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生。 +- 当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff; +- 当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。 +- (Note:在我下面这个solution里面没有省去 +diff) + +- global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况: +- 如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j]; +- 如果第i天有交易(卖出),那么global[i][j]=local[i][j]。 + + + + + +--- + +**10. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**11. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**12. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**13. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) + +#### DP +- 最多2个fence 颜色相同 +- 假设i是和 i-1不同,那么结果就是 (k-1)*dp[i - 1] +- 假设i是何 i-1相同,那么根据条件,i-1和i-2肯定不同。那么所有的结果就是(k-1)*dp[i-2] +- dp[i]: count # of ways to paint 前i个 fence +- 加法原理 +- time, space: O(n) +- rolling array: space O(1) + +#### Previous Notes +- 这题目很有意思. 一开始分析的太复杂, 最后按照这个哥们的想法(http://yuanhsh.iteye.com/blog/2219891) 的来做,反而简单了许多。 +- 设定T(n)的做法,最后题目化简以后就跟Fibonacci number一样一样的。详细分析如下。 +- 做完,还是觉得如有神。本来是个Easy题,想不到,就是搞不出。 + + + + +--- + +**14. [Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- true/false problem, think about dp +- 子问题: 前i个字母, 是否可以有valid break +- 检查dp[j] && `if substring(j, i) valid`, for all j = [0 ~ i] +- dp = new boolean[n + 1]; dp[0] = true; +- goal: if there is a j, `dp[j] == true && word[j, n] in dict`. Need iterate over i = [0 ~ n], also j = [0, i] +- 注意, 用set代替list, 因为要用 contains(). + +#### Previous notes +##### 方法2(attempt4 code) +- 与Word BreakII用同样的DP。 +- valid[i]: 记录从i到valid array末尾是否valid. + +##### 方法1:(attempt3 code) +- state,rst[i]: 从[0~i] inclusive的string是否可以在dict中break开来找到? +- function: rst[i] = true if (rst[i - j] && set.contains(s.substring(i - j, i))); j in[0~i] +- 1. rst[i - j] 记录的是[0, i-j]这一段是否可以break后在dict找到。 +- 2. 若true,再加上剩下所有[i-j, i]都能在dict找到,那么rst[i] = rst[0, i - j] && rst[i-j, i] == true +- 优化:找dict里面最长string, 限制j的增大。 + + + + +--- + +**15. [Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### 理解意思是关键 +- 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出 +- 记录每天最小值Min是多少. O(n) +- 每天都算和当下的Min买卖,profit最大多少. + +#### DP +- Find min value for first i items, new dp[n + 1]. +- dp[i]: 前i天, prices最小的price是多少: min cost of first i days +- 然后用当天的price做减法dp[i]算max profit. +- Time, Space: O(n) +- 更进一步, 用一个min来表示min[i], 因为计算中只需要当下的min. + +#### Rolling array +- index i only depend on [i - 2] +- Space O(n) + +#### Brutle Failed +- 每天都试着买进,然后之后的每一天尝试卖出. double for loop, O(n^2). timeout. +- 其中很多都是没必要的计算:[7, 1, 5, 3, 6, 4]。 if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. + + + +--- + +**16. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**17. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**18. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**19. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**20. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + diff --git a/review/Sliding Window.md b/review/Sliding Window.md new file mode 100644 index 0000000..6e8dc3f --- /dev/null +++ b/review/Sliding Window.md @@ -0,0 +1,97 @@ + + + +## Sliding Window (6) +**0. [Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String] + + +大清洗 O(nk) +map.size一旦>k,要把longest string最开头(marked by pointer:start)的那个char抹掉 +一旦某一个char要被清除,所以在这个char 的1st and last appearance之间的char都要被清洗from map + + + + +--- + +**1. [Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Anagrams%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, Sliding Window] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### HashTable +- count character apperance 就想到hashtable +- 注意countS, countP 的技巧: 作比较只需要O(26) +- Overall timeO(n) +- 小心不要用一个int[] count 来技术 查0, 复杂度是O(n) + + + +--- + +**2. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**3. [Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison + + + +--- + +**4. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**5. [Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Deque, Monotonous queue +- 维持monotonuous queue: one end is always at max and the other end is min. Always need to return the max end of queue. +- when adding new elements x: start from small-end of the queue, drop all smaller elements and append to first element larger than x. +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`. +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! + + + +--- + diff --git a/review/Sort.md b/review/Sort.md new file mode 100644 index 0000000..ed66c57 --- /dev/null +++ b/review/Sort.md @@ -0,0 +1,444 @@ + + + +## Sort (22) +**0. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + + +--- + +**1. [Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)** Level: Medium Tags: [Array, Sort] + + +方法1: +排序, nLog(n). 然后把直线上坡变成层叠山峰, 需要每隔几个(题目中是每隔2位)就做个swap 造成高低不平. +Note: 每隔山峰之间是相互没有关系的, 所以每次只要操心 [i], [i-1]两个位置就好了. + +方法2: +O(n) +看好奇数偶数位的规律, 然后根据题目给出的规律, 跑一遍, 每次只关注两个位置: 把不合适的[i], [i-1]调换位置就好了. + +方法3: +跟法2一样, 只是更巧妙一点罢了: +第一遍想太多. 其实做一个fall-through就能把问题解决,原因是因为: +这样的fall-through每次在乎两个element,可以一口气搞定,无关乎再之前的elements。 +特别的一点:flag来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**2. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**3. [Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +HashMap + + + +--- + +**4. [Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### 方法1: +找是否有overlap. priorityQueue 按照start time排序好以后, 比较current和peek: current.end > peek.start? + +#### 方法2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 + + + +--- + +**5. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**6. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**7. [Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)** Level: Medium Tags: [Linked List, Sort] + + +input一串数字, 需要出sorted output. 每次insert一个数字时, 都要放到正确的sorted的位置 + +每次insertion的时候, 都从input里面减掉这个数字 + +#### Linked List +- 把list里面每个元素都拿出来,scan and insert一遍 +- Time O(n^2), worst case, 每次放入n个数字里面的element, 刚好都是最大的 +- 所以每次要traverse n nodes, 然后走n次 + +##### 思考方法 +- 如果已经有个sorted list, insert一个element进去。怎么做? +- while 里面每个元素都小于 curr, keep going +- 一旦curr在某个点小了,加进去当下这个空隙。 + + + +--- + +**8. [Largest Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number.java)** Level: Medium Tags: [Sort] + + +给一串数字, 非负数, 把所有数字串联起来, 组成最大数字. + +因为结果很大, 所以用string表示 + +#### Sort, Comparator +- 考虑 more significant spot 应该拿到更大的值 +- 如果sort number, comparator 会比较难写: 每个digit的weight不同, 要分别讨论个位数和多位数. +- goal: 让较大的组合数排在前面, 让较小的组合数排在后面 +- 不如: 组合两种情况, 用String比较一下大小 (也可以用 integer来比较组合数, 但是为保险不超Integer.MAX_VALUE, 这里比较String) +- String.compareTo() 是按照 lexicographically, 字典顺序排列的 +- 利用compareTo, 来倒序排列 string, 刚好就得到我们要的结果. +- O(nlogn), 排序 + + + +--- + +**9. [QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)** Level: Medium Tags: [Quick Sort, Sort] + + +implement quick sort. + +#### Quick Sort +- 首先partition. 返还一个partition的那个中间点的位置: 这个时候, 所有小于nums[partitionIndex] 都应该在 partitionIndex左边 +- 然后劈开两半 +- 前后各自 quick sort, recursively +- 注意:在partition里面, 比较的时候nums[start] < pivot, nums[end]>pivot, 如果写成了 <= 会 stack overflow. +- Time O(nlogn), Space: O(1) + + + +--- + +**10. [MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/MergeSort.java)** Level: Medium Tags: [Merge Sort, Sort] + + +#### Merge Sort +- Divide and conquer, recursively +- 先从中间分段, merge sort 左边 (dfs), merge sort 右边 +- 最后merge起来 +- merge的时候因为是做int[], 所以没办法必须要O(n) space +- Time O(nlogn), Space O(n) + + + +--- + +**11. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**12. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**13. [Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort] + + +#### Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn) + + +#### Basic Implementation +- 这里已经给了 **sorted** intervals by start point. +- 直接找到可以insert newInterval的位子. Insert +- 然后loop to merge entire interval array +- 因为给的是个list, 所以方便`intervals.remove(i)` +- remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture +- O(n) + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**14. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + +**15. [H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)** Level: Medium Tags: [Bucket Sort, Hash Table, Sort] + + +找到h-index, 给的citation int[] 并不是sorted. h-index 的definition 具体看题目. + +#### Sort, find h from end +- 例子写出来,发现可以sort以后按照定义搜索一遍。 nlogn. +- 搜索一遍时候可以优化,用binary search. 但是没意义,因为array.sort已经用了nlogn +- 题目给的规则, 从小到大排序后: 剩下的 paper `n-h`, 全部要 <= h 个 citation. +- time O(nlogn), search O(n) + +##### 正向思考 +- 从i = 0 开始找第一个 `citations[i] >= h`, 就是第一个符合 h-index 规则的paper, return h + +##### 反向思考 +- 如果从 h = n, 每次h--; 那么 `x = n - h` 就是从 `[0 ~ n)` 开始找第一个 `dictations[x] >= h`, 就是结果 +- 同时, `dictations[x-1]` 就是最后一个(dictation最多的)其余的paper. + +#### Bucket count / Bucket Sort +- O(n) +- Bucket sort的思想(更像是counting sort?): 过一遍 input, 把dictation value 作为 index, 分布在bucket[index]上++ +- bucket[x] 是 count when # of citation == x. +- 如果 x 大于 n的时候, 就超出了index范围, 但是刚好这个问题可以包容, 把这样的情况记位在bucket[n]就可以 +- 巧妙: `sum += bucket[h]` where `h = [n ~ 0]` 利用了h-index的definition: +- #of papers (sum of bucket[n]...bucket[0]) has more than h cidations +- 这里运用到了bucket sort的思想, 但是并不是sorting, 而h-index的定义运用的很巧妙. +- Read more about actual bucket sort: https://en.wikipedia.org/wiki/Bucket_sort + + + +--- + +**16. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**17. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**18. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + +**19. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**20. [Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort] + + +#### PriorityQueue +- Use priority queue to sort by customized class interval{int dist; int x, y;}. +- Sort by larger distance and then sort by start index +- seat(): pq.poll() to find interval of largest distance. Split and add new intervals back to queue. +- leave(x): one seat will be in 2 intervals: remove both from pq, and merge to a new interval. +- 主方程写出来其实很好写, 就是 split + add interval, 然后 find + delete interval 而已. 最难的是构建data structure +- seat(): O(logn), leave(): O(n) + +##### Trick: 构建虚拟 boundary +- 如果是开头的seat, 或者是结尾的seat, 比较难handle: 一开始坐在seat=0的时候, 没有interval啊! +- Trick就是, 我们自己定义个虚拟的座位 `seat=-1`, `seat=N` +- 一开始有一个 interval[-1, N] 然后就建立了boundary. +- 从此以后, 每次split成小interval的时候: +- 遇到 `interval[-1, y]`, distance就是 `(y - 0)` +- 遇到 `interval[x, N]`, distance就是 `(N - 1 - x)` +- 当然正常的interval dist 就是 `(y - x) / 2` + +##### distance 中间点 +- Interval.dist 我们其实做的是 distance的中间点 `(y - x) / 2` +- 这里的dist是 `距离两边的距离` 而不是 x, y 之间的距离. 这里要特别注意. + +#### TreeSet +- https://leetcode.com/problems/exam-room/discuss/139885/Java-Solution-based-on-treeset/153588 + +#### Map +- how? +- TODO, not sure. + + + +--- + +**21. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + diff --git a/review/Stack.md b/review/Stack.md new file mode 100644 index 0000000..661b861 --- /dev/null +++ b/review/Stack.md @@ -0,0 +1,609 @@ + + + +## Stack (30) +**0. [Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)** Level: Medium Tags: [BST, Design, Stack, Tree] + + +画一下, BST in order traversal. 用stack记录最小值, 放在top. O(h) space. +每次消耗TreeNode, 都看看rightNode(其实就是下一个最小的candidate), 并且一条龙stack叠上rightNode所有的left子孙. + +Previous Notes: +用O(h)空间的做法: + +理解binary search tree inorder traversal的规律: + 先找left.left.left ....left 到底,这里是加进stack. + 然后考虑parent,然后再right. + +例如这题: + stack里面top,也就是tree最左下角的node先考虑,取名rst. + 其实这个rst拿出来以后, 它也同时是最底层left null的parent,算考虑过了最底层的parent。 + 最后就考虑最底层的parent.right, 也就是rst.right. + +注意: + next()其实有个while loop, 很可能是O(h).题目要求average O(1),所以也是okay的. + + +用O(1)空间的做法:不存stack, 时刻update current为最小值。 + +找下一个最小值,如果current有right child: + 和用stack时的iteration类似,那么再找一遍current.right的left-most child,就是最小值了。 + +如果current没有right child: + 那么就要找current node的右上parent, search in BinarySearchTree from root. + +注意: + 一定要确保找到的parent满足parent.left == current. + 反而言之,如果current是parent的 right child, 那么下一轮就会重新process parent。 + 但是有错:binary search tree里面parent是小于right child的,也就是在之前一步肯定visit过,如此便会死循环。 + + + + +--- + +**1. [Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, Stack] + + +方法1: 用queue, 把需要的item全部打出来 +方法2: 用stack, 把需要的item先存一行, 每次打开子序列时候, 全部加回stack. + + + +--- + +**2. [Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. +#### 方法1 +Array, 维持一个左手最高墙array, 右手最高强array. +对于每个index而言, vertically 能存放的最大水柱, 就是靠左右最高墙决定的: +min(leftHighestWall, rightHighestWall) - currHeight. + +#### 方法2 +方法1上面的优化, two pointer, 还是找左边最高和右边最高. O(1) space. +利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +左边按照maxLeft来计算, 右边按照maxRight来计算. + +#### 方法3 +2 Pointers, 双面夹击: +1. 找中间最高bar的index +2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条。 +3. 每次还要减去block自身的height + +#### 方法4 +主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. + + + + +--- + +**3. [Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)** Level: Easy Tags: [Design, Stack] + + +双Stack:一个正常stack,另一个minStack存当下level最小值. 注意维护minStack的变化 + +另外. 如果要maxStack,也是类似做法 + + + +--- + +**4. [Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)** Level: Easy Tags: [Design, Stack] + + +#### 双Stack +画图, 知道最后maintain的stack是那个 reverseStack: pop(), peek(), empty() 都在这个stack上, 无需变换. +push()里面做stack和reverseStack的来回倾倒. +相比老的code, 在PUSH里面做倾倒, 更容易读. + +#### Previous notes +双Stack. 一个是等于是queue,一个是backfillStack. +Tricky: 是在pop()和peek()的时候backfill, 并且要等到stack用完再backfill. +写一下例子就知道,如果提早backfill,stack.peek()就不是queue的head了. + + + + +--- + +**5. [Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)** Level: Medium Tags: [DFS, Divide and Conquer, Stack] + + +给一个expression string. 里面包括数字, 字母, 括号. 其中数字代表括号里面的内容重复几次. + +括号里面可以是String, 也可能是expression. + +目的: 把expression展开成一个正常的String. + + +#### Stack, Iteratively +- Process inner item first: last come, first serve, use stack. +- Record number globally and only use it when '[' is met. +- Stack存 [ ] 里面的内容, detect 括号开头结尾: 结尾时process inner string +- 有很多需要注意的细节才能做对: +- Stack 也可以用, 每个地方要注意 cast. 存进去的需要是Object: String, Integer +- 几个 type check: instanceof String, Character.isDigit(x), Integer.valueOf(int num) +- 出结果时候: `sb.insert(0, stack.pop())` + + +#### DFS +- Bottom->up: find deepest inner string first and expand from inside of `[ ]` +- 与Stack时需要考虑的一些function类似. 特别之处: **检查`[ ]`的结尾** +- 因为DFS时候, 括号里的substring会被保留着进入下一个level, 所以我们在base level要keep track of substring. +- 用int paren 来track 括号的开合, 当paren再次==0的时候 找到closure ']' +- 其他时候, 都要继续 append to substring + + + + +--- + +**6. [Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)** Level: Hard Tags: [Array, Monotonous Stack, Stack] + + +给n个bar,组成柱状图histogram. 求在这一排柱状图里面可以找到的面积最大的长方形. + +思考: 找长方形面积, 无非是找两个index, 然后底边长度 * height. + +#### Monotonous Stack +- 重点是根据找Histogram里面rectangle的性质, 维持一个单调递增的Stack +- 在loop over indexes的时候: +- 如果高度>= previous peek(), 那么对于那个peek, 就意味着, 往下走, 一直走高嘛, 之前的peek总可以继续抄底 +- 什么时候不能抄底了呢? 就是有一个下降趋势的时候 +- 这时候并不是calculate所有前面的peek, 而是考虑 大于 current height的之前所有的peek. +- 把这些peek到 current height 前一格的rectangle全部找出来: stack.pop() +- 这个stack.pop()的过程里面, 其实没有算上 current height, 因为需要留到下一轮, 把current index加进stack 再说 +- 为什么用stack? 因为需要知道连续递增的peek, stack.peek() O(1), 好用 + 而其实不用stack, 也可以用其他方式记录所有height, 只不过要 O(n)去找peek不方便 + +#### 知识点 +- 理解monotonous stack 是如何被维护的 +- 维护monotonous stack 是题目需要, 而不是stack本身性质, 是一种借助 stack.peek() O(1)的巧妙用法. + + + + +--- + +**7. [Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)** Level: Medium Tags: [Stack, Tree] + + +给一串数字, 做一个 maximum binary tree: 最顶上的root最大; 左child也是一个max tree, 右child也必须是max tree. + +#### Monotonous Stack +- 用到bottom->top递减的stack: 最底下的root维持成最大的element. +- 过程当中, 一旦遇到currNode.val > stack.peek(), 就意味着需要把这个currNode放在 stack的底层位置. +- 也就是说, 遇到这个条件, process, pop()所有 currNode.val > stack.peek(), 最后把currNode加进去. + +- maxTree题目本身的要求是: 大的在最中间, 左右两边的subTree也要是maxTree: +- Monotonous Stack在这里帮助 keep/track of max value, 但是left/right tree的logic是MaxTree独有的. +- left/right node的assignment是根据题目要求: 中间最大值分开后, 左边的是左边subTree, 右边的作为右边subTree. + +#### Previous notes +- Should memorize MaxTree. 依次类推,会做Min-Tree, Expression Tree +- Stack里,最大的值在下面。利用此性质,有这样几个step: + +##### Step1 +- 把所有小于curr node的,全Pop出来, while loop, keep it going. +- 最后pop出的这个小于Curr的node:它同时也是stack里面pop出来小于curr的最大的一个,最接近curr大小。(因为这个stack最大值靠下面) +- 把这个最大的小于curr的node放在curr.left. + +##### Step2 +- 那么,接下去stack里面的一定是大于curr: +- 那就变成curr的left parent. set stack.peek().right = curr. + +##### Step3 +- 结尾:stack底部一定是最大的那个,也就是max tree的头。 + + + + + +--- + +**8. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**9. [Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Recursive +- 在自己的基础上recursive, 不用helper function +- Divide and Conquer, with helper(dfs) method +- O(n) time, no extra space + +#### Iterative: Stack +- Add left nodes all the way +- Print curr +- Move to right, add right if possible +- O(n) time, O(h) space + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. + +若不右移, 很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + +#### HashMap +? How? + + + +--- + +**10. [Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Recursive +trivial, 先加left recursively, 再加right recursively, 然后组成头部. + +#### Stack +- 双stack的思想, 需要在图纸上画一画 +- 原本需要的顺序是: 先leftChild, rightChild, currNode. +- 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild +- 这样出来的结果是reverse的, 那么翻转一下就可以了. +- v1做的时候用了stack1, stack2, 因为根据这个双stack的思想而来 +- v2简化, 可以放在一个stack里面, 每次record result 的时候: rst.add(0, item); + +##### 利用stack的特点 +- 每次加element进stack的时候, 想要在 bottom/后process的, 先加 +- 想要下一轮立刻process的, 最后push进stack. + +##### 注意 +这些binary tree traversal的题目.常常有多个做法:recursive or iterative + + + +--- + +**11. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**12. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**13. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**14. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**15. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**16. [Evaluate Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Reverse%20Polish%20Notation.java)** Level: Medium Tags: [Stack] + + +给一个 RPN string list, 根据这个list, 计算结果. + +#### Stack +- stack 里面 存数字 +- 每次遇到operator, 都拿前2个数字计算 +- 计算结果存回到stack里面, 方便下一轮使用. +- Time,Space O(n) + + + + +--- + +**17. [Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)** Level: Easy Tags: [Design, Stack] + + +如题. + +#### Queue, 倒水 +- 两个Queue,交互倒水 +- 用一个Temp做swap + +##### 做法1 +- 逻辑在push里面: +- 1. x 放q2。 +- 2. q1全部offer/append到q2. +- 3. 用一个Temp做swap q1, q2. +- q1的头,就一直是最后加进去的值. + + +##### 做法2 +- 逻辑在top()/pop()里, 每次换水,查看末尾项. + + + + +--- + +**18. [Implement Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack.java)** Level: Easy Tags: [Stack] + + +随便用一个data structure, implement stack. + +#### Stack, 先入, 后出 +- ArrayList: return/remove ArrayList的末尾项。 +- 2 Queues + + + +--- + +**19. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + +**20. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**21. [Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)** Level: Hard Tags: [Greedy, Hash Table, Stack] + + +#### Hash Table, Greedy +- count[] = int[256], 不需要 `c-'a'` +- boolean visited[]: 一旦一个字母固定了位置后, 再次遇到时候, 直接跳过用过的character +- 如果tail字母可以变小, 那就delete掉tail, 重新接上新字母 (前提条件: 去掉的字母后面还会再出现, set visited[tail] = false) +- Space: O(1) count[], visited[]. +- Time: Go through all letters O(n) + +#### Stack +- Use stack instead of stringBuffer: keep append/remove last added item +- However, stringBuffer appears to be faster than stack. + + + +--- + +**22. [Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)** Level: Hard Tags: [Coordinate DP, Stack, String] + + +给一串string, 里面只有`(`, `)`. 找最长valid parentheses 的长度. + +#### 1D Coordinate DP +- use dp[i] track local max, maintain global max +- int[] dp. dp[i]: longest valid string that ends on i. +- 结尾是 ')', 2种情况: 1. 刚好s[i-1]是'('; 2. s[i]的')'更前面的一个起始'(' 对应 +- 注意, 结尾如果是'('属于不合理情况, 忽略. +- init: dp[0] = 0, 单个char不可能成型. +- 计算顺序: 从左到右, 找local max, maintain global max +- O(n) space, O(n) runtime + +#### Stack +- Stack 里面存所有的open/close parentheses. +- 如果遇到stack.top()刚好开合结掉, 就stack.pop(). +- 剩下的都是不合理的elements. +- 有点像negatively找 solution: `endIndex - 最后一个failedIndex(stack.pop()) - 1`, 应该就是最后一个succeeded string的长度 +- 每次更新 endIndex 为stack.top(), 然后从stack继续找下一个failedIndex +- 所有的length作比较, 就可以找出最长length +- O(n) stack space, O(n) runtime. 应该比dp慢一点, 因为做了2遍O(n) + + + + +--- + +**23. [Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + +time: O(n) +space: O(n) + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path 的情况, 不懂得要问: +- 1. `.` 代表current directory, 可以忽略. +- 2. `../` 表示previous level. +- 3. double slash 可以忽略. +- 4. empty string 要output `/` +- 最终就是用stack (`上一个加进去的item, 用来备选pop() out`), 遇到 `../` pop()掉上一个加上去的item, 其余加进stack +- 最终用 '/' 把所有item连接起来. + + + +--- + +**24. [Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)** Level: Medium Tags: [Linked List, Stack, Tree] + +time: O(n) +space: O(n) + +给一个BST, convert成 sorted doubly DoublyListNode. + +#### Inorder Traversal, Linked List +- 会iterative traverse Binary Search Tree(Stack && handle left-dig-down) +- create Doubly-ListNode, 注意用一个dNode作为tail node of the list + +##### Iterative inorder traversal +- 在check right node的事后, +- 不论right == null or != null, 每次都要强行move to right. +- 如果不node = node.right, +- 很可能发生窘境: +- node always = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 + + + +--- + +**25. [Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + +time: O(n) +space: O(n) + +#### Queue +- 简单的level traversal.根据level奇数偶数而add到不同位子. +- Option1: based on level % 2, insert to front/end of list +- Option2: based on level, insert right/left of node into queue + + + +--- + +**26. [Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/Exclusive%20Time%20of%20Functions.java)** Level: Medium Tags: [Stack] + + +#### Stack +- 1. later function always appears after prior fn: 1 is called by 0 +- 2. `Not mentione in the question`: a function can be started multiple times +- 3. `Not mentione in the question`: a fn cannot start if children fn starts +- 4. Use stack to keep id +- TODO: what leads to the choice of stack? stacking fn id + + + +--- + +**27. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + + +--- + +**28. [Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +剥皮过程。解铃还须系铃人 +左边的外皮'{['在stack底部 +右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**29. [Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack? + + + +--- + diff --git a/review/Status DP.md b/review/Status DP.md new file mode 100644 index 0000000..f3cc68b --- /dev/null +++ b/review/Status DP.md @@ -0,0 +1,207 @@ + + + +## Status DP (7) +**0. [Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + +time: O(nm), m = # of colors +space: O(nm) + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, 但是不知道最后一个房子选什么颜色, 那么就遍历最后一个房子(i - 1)的颜色 +- 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- 考虑DP最后一个位置的情况(颜色选择):需要附带颜色status在DP[i]上: 定义二维数组, 其中一位是status +- dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. +- dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- 然后选ith 房子的 color, 再选(i-1)th 房子的color. Double for loop, skip same color + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 + + + +--- + +**1. [House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)** Level: Medium Tags: [DP, Sequence DP, Status DP] + + +和House Robber I 类似, 搜刮房子, 相邻不能动. 特点是: 现在nums排成了圈, 首尾相连. + +#### Sequence DP +- dp[i][status]: 在 status=[0,1] 情况下, 前i个 房子拿到的 max rob gain. status=0, 1st house robbed; status=1, 1st house skipped +- 根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]); +- 特别的是,末尾的last house 和 first house相连. 这里就需要分别讨论两种情况: 第一个房子被搜刮, 或者第一个房子没被搜刮 +- be careful with edge case nums = [0], only with 1 element. +- Time,space: O(n) + +#### 两个状态 +- 是否搜刮了第一个房子, 分出两个branch, 可以看做两种状态. +- 可以考虑用两个DP array; 也可以加一dp维度, 补充这个状态. +- 连个维度表示的是2种状态(1st house being robbed or not); 这两种状态是平行世界的两种状态, 互不相关. + +#### Rolling array +- 与House Robber I一样, 可以用%2 来操作rolling array, space reduced to O(1) + + + +--- + +**2. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**3. [Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + +time: O(NK^2): +space: (NK) + +一排n个房子, 每个房子可涂成k种颜色, 涂每个房子的价钱不一样, 用costs[][]表示. + +costs[0][1]表示涂了index是0的房子, 用了color 1. + +规则: 相邻的两个房子不能使同一种颜色 + +求: 最少的cost + +#### DP +- 跟Paint House I 几乎一模一样, 只不过paint color更多了: k colors. +- 先考虑单纯地用dp[i]表示涂前 i 个房子的最小cost +- 但是 dp[i] 和 dp[i-1] 两个index选什么颜色会互相影响, 难讨论, 于是加状态: 序列DP被加了状态变成2D. +- 考虑最后位, 而前一位i-1又被i位的颜色限制, 于是在考虑 min dp[i] 时候, 又多了一层iteration. +- 做dp[i][j]: # cost for 前 i 个房子, 所以要先pick (i-1) 房子的cost, 然后在找出 (i-2)房子的cost +- K种颜色 => O(NK^2) +- 如果不优化, 跟Paint House I 几乎是一模一样的代码 +- Time O(NK^2), space(NK) +- Rolling array: reduce space to O(K) + +#### 注意 +- 序列型dp[i]表示'前i-1个'的结果. 所以dp最好设定为 int[n + 1] size. +- 然而, 颜色在这里是状态, 所以保留在 j: [ 0~k) +- [[8]] 这样的edge case. 跑不进for loop, 所以特殊handle. + +#### Optimization Solution +- Time: O(NK) +- 如果已知每次都要从cost里面选两个不同的最小cost,那么先把最小两个挑出来, 就不必有第三个for loop 找 min +- 每次在数列里面找: 除去自己之外的最小值, 利用最小值/次小值的思想 +- 维持2个最值: 最小值/次小值. +- 计算的时候, 如果除掉的不是最小值的index, 就给出最小值; 如果除掉的是最小值的index, 就给出次小值. +- Every loop: 1. calculate the two min vlaues for each i; 2. calcualte dp[i][j] +- 如何想到优化: 把表达式写出来, 然后看哪里可以优化 +- 另外, 还是可以rolling array, reduce space complexity to O(K) + + + +--- + +**4. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**5. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**6. [Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)** Level: Medium Tags: [Coordinate DP, DP, Status DP] + + + +#### DP +- 特点: 上一步可能是swaped也可能是fixed +- 考虑A,B之间的现状: `A[i] > A[i - 1] && B[i] > B[i - 1]` 或者 `A[i] > B[i - 1] && B[i] > A[i - 1]` +- 问题: 如何把这个状态变成合理的strick-increasing状态? +- `A[i] > A[i - 1] && B[i] > B[i - 1]`: 1. 已经合理, 也不动. 2. [i], [i-1] 全部都swap +- `A[i] > B[i - 1] && B[i] > A[i - 1]`, 交错开来, 所以调换[i], 或者[i-1]: 1. 换[i-1]. 2. 换[i] +- 注意因为求min, 所以init value应该是 Integer.MAX_VALUE; + + + +--- + diff --git a/review/String.md b/review/String.md new file mode 100644 index 0000000..11e6fae --- /dev/null +++ b/review/String.md @@ -0,0 +1,1088 @@ + + + +## String (60) +**0. [Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)** Level: Medium Tags: [String] + + + + +--- + +**1. [Judge Route Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/Judge%20Route%20Circle.java)** Level: Easy Tags: [String] + + +简单的character checking. 各个方向, 加加减减. + + + +--- + +**2. [Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i: + 一旦有重复, rest map. + 没有重复时候, 不断map.put(), 然后求max值 + +问题: 每次reset map之后就开始从新从一个最早的index计算, 最坏情况是O(n^2): +'abcdef....xyza' + + + + +--- + +**9. [Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, String, Two Pointers] + + +基本思想: 用个char[]存string的frequency. 然后2pointer, end走到底, 不断validate. +符合的就process as result candidate. + +HashMap的做法比char[]写起来要复杂一点, 但是更generic + + + +--- + +**10. [Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String] + + +大清洗 O(nk) +map.size一旦>k,要把longest string最开头(marked by pointer:start)的那个char抹掉 +一旦某一个char要被清除,所以在这个char 的1st and last appearance之间的char都要被清洗from map + + + + +--- + +**11. [Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)** Level: Hard Tags: [Hash Table, String, Trie] + + +Obvious的做法是全部试一遍, 判断, 变成 O(n^2) * O(m) = O(mn^2). O(m): isPalindrome() time. + +当然不行了, 那就看是O(nlogN), 还是O(n)? + +#### 方法1: Hash Table + Palindrome的性质. 复合型. +O(mn) + +##### 思路 +- 每一个word, 都可以拆分成 front + mid + end. 如果这个word + 其他word可以组成palindrome,那就是说 +- 砍掉 (mid+end), front.reverse() 应该存在 words[] 里面. +- 砍掉 (front+mid), end.reverse() 应该存在 words[] 里面. +- 我们用HashMap存所有的, 然后reverse, 找配对就好. + +##### Corner case +- 如果有 empty string "", 那么它跟任何一个palindrome word, 都可以配对, 并且根据位置前后变换, 凑成2份 distinct indexes. +- 这样就有了那个 `if (reverseEnd.equals("")) {...}` 的logic. +- 注意: 虽然在处理砍头/砍尾的两个 for loop里面都在根据 empty string 重复记录, + 但因为 "" 自己本身不能作为起点, 所以overall只会在被其他palindrome配对时记录一次. + + +#### 方法2: Trie +还要做一下那. + + + +--- + +**12. [Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Change%20to%20Anagram.java)** Level: Easy Tags: [String] + + +HackerRank里面的random 题目: 给一个string, 一切成两半, 看两半要变化多少个字符, 能变成anagram. + +- 切两半成两个String A,B. 分别在int count[26]里面++, --. +- 记录 26个小写字母的频率. 如果全部抵消, 就是anagram. +- 注意: 最后count出来要除以2:字母不同,会在不同的字母位上加减count,那么就是刚好重复计算了一遍。所以除以二 + +- Note: HackerRank里要注意自己写: Scanner, import java.util, non-static method ...etc. + + + +--- + +**13. [Compare Version Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Version%20Numbers.java)** Level: Medium Tags: [String] + + +给两串version number, 由数字和'.' 组成. 比较先后顺序. + +If version1 > version2 return 1, if version1 < version2 return -1, otherwise return 0. + +#### divide and conquer +- 用 str.split("\\.") 分割string +- Convert成integer, 逐个击破 + +#### 注意 +- '1.0' 和 '0' 是相等的 +- 如果可以假设version integer都是valid, 直接Integer.parseInt()就可以了 +- 不然的话, 可以compare string + + + +--- + +**14. [Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Strings.java)** Level: Easy Tags: [String] + + +看StringA是不是包括所有 StringB的字符. + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + + + +--- + +**15. [Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)** Level: Easy Tags: [Basic Implementation, String] + + +介绍一种count数字的方法, 然后每一行读出上一行的结果, 一行一行推算. 问nth行是啥样? + +#### Basic Implementation +- 主要是题意很难理解, 非常misleading, 等到看明白题目, 其实没有什么算法要求. +- Count duplicates and print + + + +--- + +**16. [Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Partition DP +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- 确定末尾的2种状态: single letter or combos. 然后计算出单个letter的情况, 和双数的情况 +- 定义`dp[i] = 前i个digits最多有多少种decode的方法`. new dp[n + 1]. +- 加法原理: 把不同的情况, single-digit, double-digit 的情况加起来 +- dp[i] += dp[i - x], where x = 1, 2 +- note: calculate number from characters, need to - '0' to get the correct integer mapping. +- 注意: check value != '0', 因为'0' 不在条件之中(A-Z) +- Space, Time O(n) + +#### 引申 +- 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + + + +--- + +**17. [Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Anagrams.java)** Level: Medium Tags: [Hash Table, String] + + +给一串string, return list of list, 把anagram 放在一起. + +#### Hash Table, key 是 character frequency +- 存anagram +- 用 character frequency 来做unique key +- 用固定长度的char[26] arr 存每个字母的frequency; 然后再 new string(arr). +- 因为每个位子上的frequency的变化,就能构建一个unique的string +- O(nk), k = max word length + + +#### Hash Table, key 是 sorted string +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**18. [Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game.java)** Level: Easy Tags: [String] + + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + + + +--- + +**19. [Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20strStr().java)** Level: Easy Tags: [String, Two Pointers] + + +给两个string A, B, 找一个 B 在 A 种的起始位置. + +#### Two Pointer +- 找到B在A中的起始位置, 然后看一下从这个点开始的substring是否等于B就可以了 +- 还挺多坑的, 这些可以帮助优化: +- 1. 当B是“”的时候,也就是能在A的其实位置找到B....index = 0. +- 2. edge condition: 如果 haystack.length() < needle.length() 的话, 必须错, return -1 +- 3. 如果在某个index, A后面剩下的长度, 比B的长度短, 也是误解, return -1 + + + +--- + +**20. [Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/Length%20of%20Last%20Word.java)** Level: Easy Tags: [String] + + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + + + +--- + +**21. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**22. [Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Check Palindrome +- 前后两个指针, 往中间移动, 查看是否字母重合 + +#### 过滤 alphanumeric +- 可以用 ASCII code 来手动过滤, 只要 '0' ~ '9', 'a' ~ 'z', 'A' - 'Z' 之间的 +- 也可以用 regular expression: match 所有这些字母, 是 [a-zA-Z0-9] +- 那凡是不是这些字母的 match, 就是取反: "[^a-zA-Z0-9]". 测试: https://regex101.com/ + + + +--- + +**23. [Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20String.java)** Level: Easy Tags: [String] + + +给两个String, 看A rotate之后 能不能变成B + +#### LeetCode +- Basics +- StringBuffer.deleteCharAt(xx), StringBuffer.append(xx) +- O(n) + + +#### LintCode +- Different problem: 给一个char[], 要rotate offset times. +- *三步rotate* +- 有个坑:offset可能很长,那么要%length,才能得到真正需要rotate的部分。 +- Note: rotate 一个 full length之后,是string 不变 + + + +--- + +**24. [Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)** Level: Medium Tags: [Basic Implementation, Enumeration, String] + + +给一个时间string"12:09", 用里面的4个integer组合成其他时间string, 目标找最小的next time. + +如果组合出的time string 在input time之前, 默认 + 24 hours. + +#### String +- enumerate all candidates and filter to keep the correct ones +- String.compareTo(string) -> gives lexicographical comparision + + + +--- + +**25. [Encode and Decode Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20Strings.java)** Level: Medium Tags: [String] + + +如题. + +#### String +- 'word.length()#word' 这样encode, 可以避免遇到# +- 基于我们自己定的规律, 在decode的里面不需要过多地去check error input, assume所有input都是规范的. +- decode就是找"#",然后用"#"前的数字截取后面的string. + + + + +--- + +**26. [Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Prefix.java)** Level: Easy Tags: [String] + + +找一串String里面最长的公共prefix. + +#### Sort, compare string +- Sort O(nlogn) +- first and last string should share common prefix +- 这里假设题目要求的是所有string的公共 prefix, 而不是部分strings + +#### Brutle +- Nested loop, 每一次比较所有string 同位是否相等 +- 相等,append string. 不等,return. +- O(mn) + + + +--- + +**27. [Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)** Level: Medium Tags: [Backtracking, DFS, String] + + +给一串数字, 检查是否是valid IP, 如果合理, 给出所有valid 的IP组合方式. + +#### Backtracking +- 递归的终点:list.zie() == 3, 解决最后一段 +- 递归在一个index上面, pass s.toCharArray() +- validate string要注意leading '0' +- 注意: 递归的时候可以用一个start/level/index来跑路 +- 但是尽量不要去改变Input source, 会变得非常confusing. +- note: code有点messy, 因为要考虑IP的valid情况 +- 那个'remainValid', 其实是一个对于remain substring的判断优化, 不成立的就不dfs了 + + + +--- + +**28. [Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String.java)** Level: Medium Tags: [String] + + +#### Break by space, then flip +- 结尾不能有空格 +- trim() output +- 如果Input是 ""的话,split以后就啥也没有了 +- 另个题目Reverse Words in String (char[]) 可以in-place, 条件是char[]里面是没有首尾空格. +- Time, Space: O(n) + +#### Other methods +- flip entire string, then flip each individual string (代码有点多, 这道题犯不着) + + + +--- + +**29. [Reverse Words in a String II.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20II.java)** Level: Medium Tags: [String] + + +#### In-place reverse +- reverse用两回. 全局reverse。局部:遇到空格reverse +- 注意ending index: `i == str.length - 1`, 结尾点即使没有' '也要给reverse一下最后一个词 + + + + +--- + +**30. [Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20III.java)** Level: Easy Tags: [String] + + +给一个String, 里面的Word被single space split开来, 目的是reverse里面所有的Word, 但preserve Word 和 space order. + +#### Reverse function +- Reverse Words in a String II 的降级版, 去掉第一个overall reverse就好了 + + + +--- + +**31. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**32. [Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)** Level: Hard Tags: [KMP, String] + + +#### Divide by mid point, Brutle +- check (mid, mid+1), or (mid-1, mid+1). +- If the two position matches, that is a palindrome candidate +- 比较front string 是否是 end string 的substring +- O(n^2) +- timeout on last case: ["aaaaaa....aaaacdaaa...aaaaaa"] + +#### KMP +- TODO + + + +--- + +**33. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**34. [Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)** Level: Hard Tags: [Bit Manipulation, String] + + +#### String +- 首先要分两半解决,断点是'.': str.split("\\."); +- Integer那一半好弄,whie loop里: num%2, num/2. 做一个 `parseInteger()` function +- Decimal那边复杂点. 做一个 `parseDecimal()` function: +- bit == 1的数学条件: 当下num * 2 >= 1。 更新: num = num * 2 - 1; +- bit == 0的数学条件: num * 2 < 1. 更新: num = num * 2 + +#### 注意 +- num是 double, 小数在 `num = num * 2 - 1` 的公式下可能无限循环 +- 因此check: num重复性,以及binary code < 32 bit. +- 所以题目也才有了32BIT的要求! + + + +--- + +**35. [Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Substring.java)** Level: Medium Tags: [DP, String] + + +给一个string, 找到最长的palindrome substring. + +Related: Longest Palindromic Subsequence, Palindrome Partioning II + +O(n^2) is not too hard to think of. How about O(n)? + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + +#### DP: isPalin[][] +- 穷举double for loop. O(n^2) +- boolean isPalin[i][j], 每次确认有palindrome就记录下来true / false +- 穷举的for loop计算顺序: end point j, and stat point i = [0, j] +- 在计算 isPalin[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- double for loop: O(n^2). slower, because it guarantees O(n^2) due to the for loop + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**36. [Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)** Level: Hard Tags: [Coordinate DP, Stack, String] + + +给一串string, 里面只有`(`, `)`. 找最长valid parentheses 的长度. + +#### 1D Coordinate DP +- use dp[i] track local max, maintain global max +- int[] dp. dp[i]: longest valid string that ends on i. +- 结尾是 ')', 2种情况: 1. 刚好s[i-1]是'('; 2. s[i]的')'更前面的一个起始'(' 对应 +- 注意, 结尾如果是'('属于不合理情况, 忽略. +- init: dp[0] = 0, 单个char不可能成型. +- 计算顺序: 从左到右, 找local max, maintain global max +- O(n) space, O(n) runtime + +#### Stack +- Stack 里面存所有的open/close parentheses. +- 如果遇到stack.top()刚好开合结掉, 就stack.pop(). +- 剩下的都是不合理的elements. +- 有点像negatively找 solution: `endIndex - 最后一个failedIndex(stack.pop()) - 1`, 应该就是最后一个succeeded string的长度 +- 每次更新 endIndex 为stack.top(), 然后从stack继续找下一个failedIndex +- 所有的length作比较, 就可以找出最长length +- O(n) stack space, O(n) runtime. 应该比dp慢一点, 因为做了2遍O(n) + + + + +--- + +**37. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + +**38. [Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Words.java)** Level: Easy Tags: [Hash Table, String] + + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**39. [Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Characters.java)** Level: Easy Tags: [Array, String] + + +determine if characters are unique in string + +#### HashSet +- space O(n), time O(n) + +#### char[] +- space O(n), time O(nlogn) + +#### no additional data structure +- double for loop: O(n^2) + + + + +--- + +**40. [Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)** Level: Medium Tags: [DP, String] + + +根据题意, count # of palindromic substring. (不同index截取出来的substring算不同的情况) + +#### isPalin[][] +- build boolean[][] to check isPalin[i][j] with DP concept +- check all candidates isPalin[][] +- O(n^2) + +#### odd/even split check +https://leetcode.com/problems/palindromic-substrings/discuss/105689/Java-solution-8-lines-extendPalindrome + + + +--- + +**41. [Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Multiply%20Strings.java)** Level: Medium Tags: [Math, String] + + +给两个integer String, 求乘积 + +#### String calculation, basic implementation +- let num1 = multipier, num2 = base. +- mutiply and save into int[m + n], without carry. Loop over num1, each row num1[x] * num2 +- move carry to the correct index and direclty save result +- calculate carry on rst[]: sb.insert(0, c) such that no need to reverse() later +- remove leading '0', but do not delete string "0" +- time,space O(mn) + +#### Previous notes. +- Bad solution: reversing makes it complicated, no need to reverse. +- 1. 数字‘123’, 在数组里面, index == 0 是 ‘1’。 但是我们平时习惯从最小位数开始乘积,就是末尾的'3'开始。 +- 所以!翻转两个数字先!我去。这个是个大坑。 +- 2. 乘积product,和移动Carrier都很普通。 +- 3. !!最后不能忘了再翻转。 +- 4. 最后一个看坑。要是乘积是0,就返回‘0’。 但是这个其实可以在开头catch到没必要做到结尾catch。 +- 用到几个StringBuffer的好东西: reverse(), sb.deleteCharAt(i) +- 找数字,或者26个字母,都可以: s.charAt(i) - '0'; s.charAt(i) - 'a'; + + + +--- + +**42. [Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + +time: O(n) +space: O(n) + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path 的情况, 不懂得要问: +- 1. `.` 代表current directory, 可以忽略. +- 2. `../` 表示previous level. +- 3. double slash 可以忽略. +- 4. empty string 要output `/` +- 最终就是用stack (`上一个加进去的item, 用来备选pop() out`), 遇到 `../` pop()掉上一个加上去的item, 其余加进stack +- 最终用 '/' 把所有item连接起来. + + + +--- + +**43. [Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + +time: O(n) + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**44. [Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4.java)** Level: Easy Tags: [Enumeration, String] + + +Read4 题目. 理解题目: 是有个input object buff, 会被populated with data. + +#### String in char[] format +- 理解题目: 其实就是track `可以read多少bytes by read4() response` +- 另外一个有用的function `System.arraycopy(src, srcIndex, dest, destIndex, length)` + + + +--- + +**45. [Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)** Level: Hard Tags: [Enumeration, Math, String] + + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 + +#### 注意 +- StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 +- 注意加 " " 的时候, 如果多余, 要`trim()` +- 注意, 小于20的数字, 有自己的特殊写法, 需要额外handle +- 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 + + + + +--- + +**46. [Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome%20II.java)** Level: Easy Tags: [String] + + +#### Palindrome String +- delete an index = jump over the index +- 注意 boolean chance 可以用一个helper function + + + +--- + +**47. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**48. [Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- Summing space = `width + (size-1)`. maintain: 1. list of candidates, 2. width of actual words +- calculate space in between: `remain/(size - 1)` +- overall for loop; clean up list: 1. over size; 2. last item +- 一点也不难, 但是要小心: deal with list of string的时候, 注意处理干净sum size of list, 就行了. +- `干净处理space`: 只处理 (n-1) items, 然后最后一个拿到for loop 外面, 特殊处理. + +#### Notes +- Clarification, observation: +- can start with greedy approach to stack as many words as possible +- once exceed the length, pop the top, and justify the added words (untouched words tracked by index) +- left justify: given list/stack of words with size t, overall remaining space length m, +- deal with last line with special care: just fill one space, and fill the rest of the row with space +- Does not seem very complicated, but need additional care of calculating the amount of space needed. +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**49. [Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)** Level: Hard Tags: [Enumeration, String] + + +Read N Character using `Read4(char[] buf)` 的加强版: 可以不断读 read(buf, n) + +#### String +- 注意String的index handle, 慢慢写edge case +- 理解题目意思: `read4(char[] buf)` 这样的 `populate input object` 的function稍微少一点. +- 遇到时候, 仔细理解function用法, 不要慌乱. 其实思考方式很简单, 仔细handle string 还有 edge case就好了. + + + +--- + +**50. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**51. [String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- check sign, leading-0, overall size > 11, check max/min in Long format +- if passed all tests, parseInt() + +#### regular expression +- if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**52. [Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Roman%20to%20Integer.java)** Level: Easy Tags: [Math, String] + + +#### String +- 熟悉罗马字母规则 +- 1. 'I V X L C D M' 分别代表的数字 +- 2. 列举combo的情况,需要从原sum里面减掉多加的部分: 'IV, IX'减2, 'XL, XC'减20, 'CD, CM'减200. +- Leading `I(1*2)`, `X(10*2)`, `C(100*2)` causes double counting + +https://en.wikipedia.org/wiki/Roman_numerals + + + +--- + +**53. [Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +剥皮过程。解铃还须系铃人 +左边的外皮'{['在stack底部 +右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**54. [First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +方法1: 按照题意, 找到第一个 first index == last index的字母. + +方法2: 用hashmap存字母的index, 有些重复字母的index就会是个list. 找到单一index, 结合成list, sort, return list.get(0) + + + +--- + +**55. [Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)** Level: Easy Tags: [Math, String, Two Pointers] + + +#### Two pointers +- Use two pointers i, j to track the 2 strings +- Add when i and j are applicable. While (i >= 0 || j >= 0) +- StringBuffer.insert(0, x); +- handle carry + +#### wrong: convert to int +- 土办法没技术,把binary换成数字,加起来,再换成binary +- 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**56. [One Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/One%20Edit%20Distance.java)** Level: Medium Tags: [String] + + +如果S, T只用一个operation就能变成相等, return true. + +#### Edit: 删除,增加,和替换 +- 换完之后,理论上换成的String 就应该全等 +- for loop, 一旦找到不一样的char, 就判断那三种可能性: insert/delete/replace +- insert/delete 对于2个string来说, 效果是类似的 +- O(n) + + + +--- + +**57. [Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)** Level: Medium Tags: [Hash Table, String] + + + +#### Convert to orginal string +- shit by offset. `int offset = s.charAt(0) - 'a';` +- increase if less than 'a': `if (newChar < 'a') newChar += 26;` + +#### Previous notes +- 相同shift规则的string, 能被推算到同一个零起始点,就是共同减去一个char,最后就相等。以此作为key,用HashMap。一目了然。 +- 记得根据题目意思,一开始要String[] sort一下。 + + + +--- + +**58. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**59. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + diff --git a/review/Subarray.md b/review/Subarray.md new file mode 100644 index 0000000..ee07ba9 --- /dev/null +++ b/review/Subarray.md @@ -0,0 +1,242 @@ + + + +## Subarray (11) +**0. [Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### DP +- 求最值, 想到DP. Time/Space O (n) +- 两个特别处: +- 1. 正负数情况, 需要用两个DP array. +- 2. continuous prodct 这个条件决定了在Math.min, Math.max的时候, +- 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. +- 这也就注定了需要一个global variable 来hold result. + +#### Space optimization, rolling array +- maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins +- Time: O(n), space: O(1) + + + +--- + +**1. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + +time: O(n) +space: O(1) + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + + + +--- + +**2. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**3. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**4. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**5. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**6. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**7. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**8. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**9. [Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)** Level: Medium Tags: [Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +#### Map +- use `Map` to store inline preSum and its index. +- 1. Build presum incline +- 2. Use map to cache current preSum value and its index: `Map` +- 3. Each iteration: calculate possible preSum candidate that prior target sequence. ex: `(preSum - k)` +- 4. Use the calculated preSum candidate to find index +- 5. Use found index to calculate for result. ex: calculate range. + + + +--- + +**10. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + diff --git a/review/Sweep Line.md b/review/Sweep Line.md new file mode 100644 index 0000000..e588d49 --- /dev/null +++ b/review/Sweep Line.md @@ -0,0 +1,115 @@ + + + +## Sweep Line (5) +**0. [Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### 方法1: +找是否有overlap. priorityQueue 按照start time排序好以后, 比较current和peek: current.end > peek.start? + +#### 方法2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 + + + +--- + +**1. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**2. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**3. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**4. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + diff --git a/review/Topological Sort.md b/review/Topological Sort.md new file mode 100644 index 0000000..e201c88 --- /dev/null +++ b/review/Topological Sort.md @@ -0,0 +1,179 @@ + + + +## Topological Sort (5) +**0. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**1. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**2. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**3. [Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Topological Sort] + + +#### Topological Sort BFS +- indegree tracking: Track all neighbors/childrens. 把所有的children都存在 inDegree里面 +- Process with a queue: 先把所有的root加一遍(indegree == 0),可能多个root。并且全部加到queue里面。 +- BFS with Queue: +- Only when map.get(label) == 0, add into queue && rst. (indegree剪完了, 就是root啦) +- inDegree在这里就 count down indegree, 确保在后面出现的node, 一定最后process. + + +#### Basics about graph +- 几个graph的condition: +- 1. 可能有多个root +- 2. directed node, 可以direct backwards. + +TODO: +- build`Map inDegree = new HashMap<>();` and include the root itself +- that is more traditional indegree building + + + +--- + +**4. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + diff --git a/review/Tree DP.md b/review/Tree DP.md new file mode 100644 index 0000000..668ef5d --- /dev/null +++ b/review/Tree DP.md @@ -0,0 +1,27 @@ + + + +## Tree DP (1) +**0. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + diff --git a/review/Tree.md b/review/Tree.md new file mode 100644 index 0000000..4f77caf --- /dev/null +++ b/review/Tree.md @@ -0,0 +1,1139 @@ + + + +## Tree (55) +**0. [Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DP, Tree] + + +Not quite clear. +根据左右分割而总结出了原理, 每次分割, 左右两边都会有一定数量的permutation, 总体上的情况数量当然是相乘. +然后每一个不同的分割点都加一遍: +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +然后把数学公式转换成DP的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**1. [Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, Tree] + + +方法1: +适合复习BST. 用DFS对待每个node. 注意BST的特征: 所有left nodes都小于当下node, 所有right nodes都大于当下node. + +根据题意用[L,R]切割.如果node.val index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**6. [Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Balanced%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个binary tree, 看是否是height-balanced + +#### DFS +- DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. +- 一旦有 <0 或者差值大于1, 就全部返回Integer.MIN_VALUE. Integer.MIN_VALUE比较极端, 确保结果的正确性。 +- 最后比较返回结果是不是<0. 是<0,那就false. +- Traverse 整个tree, O(n) + + +#### DFS, maxDepth function +- Same concept as in 1, but cost more traversal efforts. + + + +--- + +**7. [Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +给一个特殊的binary tree, treeNode里面有一个 next pointer. + +写一个function, 把所有node都更同level的node 连在一起. 最右边的node.next = NULL + +#### DFS + Divide and Conquer +- 题目要求DFS. 想清楚了如何在DFS level把几种情况都考虑了, 写起来很简单. NOT BFS, because requires O(1) space +- 对于一个root来说, 只有几个点可以顾忌到: root.left, root.right, root.next. +- 想办法把这三个方向的点, 能连起来的都连起来: +- 1. `node.left.next = node.right` +- 2. If `node.next != null`, link `node.right.next = node.next.left`; +- 然后在dfs(root.left), dfs(root.right) +- Time: visit && connect all nodes, O(n) + +#### BFS +- 不和题意,用了queue space,与Input成正比。太大。 +- BFS over Tree。 用Queue 和 queue.size(),老规矩。 +- process每层queue时, 注意把next pointer加上去就好. + + + +--- + +**8. [Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +如题, 验证是否是BST. + +#### DFS +- 查看每个parent-child关系: leftchild < root < rightChild; +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest +- imagine we know the two extreme border: Integer.MIN_VALUE, Integer.MAX_VALUE; pass node around and compare node vs. node.parent. +- 方法: 把root.val 传下来作为 max 或者 min, 然后检查children +- + +##### Note: +- min/max需要时long type. +- 如果题目真的给node.val = Integer.MAX_VALUE, 我们需要能够与之比较, long就可以. + + + +--- + +**9. [Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)** Level: Medium Tags: [Stack, Tree] + + +给一串数字, 做一个 maximum binary tree: 最顶上的root最大; 左child也是一个max tree, 右child也必须是max tree. + +#### Monotonous Stack +- 用到bottom->top递减的stack: 最底下的root维持成最大的element. +- 过程当中, 一旦遇到currNode.val > stack.peek(), 就意味着需要把这个currNode放在 stack的底层位置. +- 也就是说, 遇到这个条件, process, pop()所有 currNode.val > stack.peek(), 最后把currNode加进去. + +- maxTree题目本身的要求是: 大的在最中间, 左右两边的subTree也要是maxTree: +- Monotonous Stack在这里帮助 keep/track of max value, 但是left/right tree的logic是MaxTree独有的. +- left/right node的assignment是根据题目要求: 中间最大值分开后, 左边的是左边subTree, 右边的作为右边subTree. + +#### Previous notes +- Should memorize MaxTree. 依次类推,会做Min-Tree, Expression Tree +- Stack里,最大的值在下面。利用此性质,有这样几个step: + +##### Step1 +- 把所有小于curr node的,全Pop出来, while loop, keep it going. +- 最后pop出的这个小于Curr的node:它同时也是stack里面pop出来小于curr的最大的一个,最接近curr大小。(因为这个stack最大值靠下面) +- 把这个最大的小于curr的node放在curr.left. + +##### Step2 +- 那么,接下去stack里面的一定是大于curr: +- 那就变成curr的left parent. set stack.peek().right = curr. + +##### Step3 +- 结尾:stack底部一定是最大的那个,也就是max tree的头。 + + + + + +--- + +**10. [Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Recursive +- 在自己的基础上recursive, 不用helper function +- Divide and Conquer, with helper(dfs) method +- O(n) time, no extra space + +#### Iterative: Stack +- Add left nodes all the way +- Print curr +- Move to right, add right if possible +- O(n) time, O(h) space + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. + +若不右移, 很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + +#### HashMap +? How? + + + +--- + +**11. [Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Recursive +trivial, 先加left recursively, 再加right recursively, 然后组成头部. + +#### Stack +- 双stack的思想, 需要在图纸上画一画 +- 原本需要的顺序是: 先leftChild, rightChild, currNode. +- 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild +- 这样出来的结果是reverse的, 那么翻转一下就可以了. +- v1做的时候用了stack1, stack2, 因为根据这个双stack的思想而来 +- v2简化, 可以放在一个stack里面, 每次record result 的时候: rst.add(0, item); + +##### 利用stack的特点 +- 每次加element进stack的时候, 想要在 bottom/后process的, 先加 +- 想要下一轮立刻process的, 最后push进stack. + +##### 注意 +这些binary tree traversal的题目.常常有多个做法:recursive or iterative + + + +--- + +**12. [Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +#### Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, +- 找到 node.val == target, 或者走位走完, return closest + + + +--- + +**13. [Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样, 如果一样, 直接 2 ^ h - 1就好 +- 不一样的话, 再DFS + +##### Trick +- 直接DFS会timeout, O(n), 其实可以optimize +- to pass the test with O(h^2), 位运算: Math.pow(2, h) = 2 << (h - 1). 神奇! +- 2 << 1就是把所有bits往左移动一位, 也就是 * 2 + +#### Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + + + +--- + +**14. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**15. [Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +A complete binary tree is a binary tree in which every level, except possibly the last, + +is completely filled, and all nodes are as far left as possible + +#### BFS +- 当出现了第一次有 null children的node的时候, 说明到了leaf level, mark flag = true; +- 自此以后,queue再不该有node再有child; queue后面出现的node的left/right child应该都是null +- 否则就是有问题, return false; + + + + +--- + +**16. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**17. [Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### DFS +- 简单处理swap +- recursively swap children + +#### BFS +- BFS with Queue +- 每次process一个node, swap children; 然后把child加进queue里面 +- 直到queue process完 + + + +--- + +**18. [Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree, 找最深depth + +#### DFS +- 这里要走过所有的node, 所以dfs非常合适 +- Divide and conquer. +- 维持一个最大值: Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +- 注意check root == null + +#### Note +- BFS is doable as well, but a bit more code to write: tracks largest level we reach + + + +--- + +**19. [Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### BFS +- Shortest path; minimum depth: 想到BFS, check level by level, BFS更能确保更快找到结果 +- depth definition: reach to a leaf node, where node.left == null && node.right == null +- BFS using queue, track level. + + +#### DFS +- Divide and Conquery一个最小值. +- 注意处理Leaf的null: null leaf 出现的时候, 就忽略这个leaf, 直接return算有leaf +- 另一种count的方法: 用Integer.MAX_VALUE代替 null leaf,这样可以避免错误counting. (不能直接recursive) +- 这个无论如何都要走所有node, 所以dfs应该比较适合. + + + + +--- + +**20. [Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**21. [Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +检查binary tree是否 identical. + +特点: subtree如果是有旋转的, 只要tree node value相等, 就可以算是identical + +#### DFS +- 在DFS的基础上, 比对左左,左右,右左,右右 + + + +--- + +**22. [Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)** Level: Easy Tags: [DFS, Tree] + + +#### DFS +- 基础binary tree traversal. 注意对null child的判断 + + + +--- + +**23. [Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree s, 和一个binary tree t, 检查t是不是s的subtree. + +#### DFS +- 跟 identical binary tree的写法很像 +- 只有 current s.val = t.val 的时候才需要compare same tree. +- 其他情况, 继续recursively isSubtree +- 注意:即使找到T1 == T2, 但很可能只是数字相同(这里不是binary search tree!!), 而children不同 +- 所以同时要继续recursively isSubtree(T1.left, T2) ...etc. + + + +--- + +**24. [Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个Binary Tree root, 以及两个node p, q. 找 p 和 q 的 lowest common ancestor + +#### DFS +- 因为是 binary tree, 所以直接盲目搜索搜索path不efficient, use extra space and waste time +- 巧用DFS来找每一个node的common ancestor. +- Need the assumption: 1. unique nodes across tree; 2. must have a solution +- 当root == null或者 p q 任何一个在findLCA底部被找到了(root== A || root == B),那么就return 这个root. +- 三种情况: +- 1. A,B都找到,那么这个level的node就是其中一层的ancestor: 其实,最先recursively return到的那个,就是最底的LCA parent. +- 2. A 或者 B 找到,那就还没有公共parent, return 非null得那个。 +- 3. A B 都null, 那就找错了没有呗, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time/space O(n) + + + +--- + +**25. [Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Tree] + + +给一个Binary Tree root, 以及两个node A, B. 特点: node里面存了parent pointer. 找 lowest common ancestor + + +#### Hash Set +- 这个题有个奇葩的地方, 每个node还有一个parent, 所以可以自底向上. +- save visited in hashset. 第一个duplicate, 就是A B 的 lowest common ancestor + +#### Save in lists +- 自底向上。利用parent往root方向返回 +- 把所有parent存下来, 然后在两个list里面找最后一个 common node + +#### 注意 +- 无法从root去直接搜target node 而做成两个list. 因为根本不是Binary Search Tree! + + + + +--- + +**26. [Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的list +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Brutly寻找p和q的common ancestor, 然后recursively drive left/right +- 非常巧妙, 但是也比较局限; 稍微变条件, 就很难recursive. +- 几种情况: +- 1. one of p, q 在leaf, 那么此时的root其实就是lowest common ancestor +- 2. 如果p, q 在root的左右两边, 这就是分叉口, 那么root就是lowest common ancestor +- 3. 如果p,q 在root的同一边 (左,右), 那么继续dfs +- O(1) extra space, O(logn) time + + + +--- + +**27. [Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**28. [Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)** Level: Medium Tags: [BFS, Tree] + + +如题, 但是output要倒序. + +#### BFS +- 跟Binary Tree Level Order Traversal一样,只不过存result一直存在存在0位. + + +#### DFS +- 根据level来append每个list +- rst里面add(0,...)每次都add在list开头 + + + +--- + +**29. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**30. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + +**31. [Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum.java)** Level: Easy Tags: [DFS, Tree] + + +给一个inputSum, 然后dfs, 找到是否有一条path, 得出的path sum 跟 inputSum 一样. + +#### DFS +- 确定好结尾的条件: is leaf && val == sum +- 每一层减掉node.val, 然后dfs. +- 写一写: root == null => false 对parent nodes的影响. 这里发现没影响, 所以可以简化成用1个functionDFS. + + + + +--- + +**32. [Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)** Level: Easy Tags: [Backtracking, DFS, Tree] + + +给一个inputSum, 然后dfs, 找到所有path, 满足: path sum 跟 inputSum 一样. + +#### DFS, Backtracking +- 用remaining sum 来检测是否满足 input path sum 条件 +- 满足的时候add to result list +- 两种backtracking: +- 1. backtrack 当下node, 加进list, 然后dfs. dfs结束后删掉之前加进去的元素. 非常clean. +- 2. backtrack 下一个dfs level增加的value. dfs return 之后, 删掉list里面的末尾元素: 但是删掉的dfs余下的value. +- 第一种backtrack更加好掌握. + +#### Previous Notes: +- Binary Tree的一个基本题: 找到所有满足条件的path +- 遍历到底,比较sum vs. target +- 注意divide的情况。要把遍历的例子写写 + + + +--- + +**33. [Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)** Level: Easy Tags: [DFS, Double Recursive, Tree] + + +count所有存在的 path sum == target sum. 可以从任意点开始. 但是只能parent -> child . + +#### DFS +- 对所给的input sum 做减法, 知道 sum 达到一个目标值截止 +- 因为可以从任意点开始, 所以当sum达标时候, 需要继续recursive, 从而找到所有情况 (有正负数, sum可能继续增加/减少) +- 经典的 helper dfs recursive + self recursive +- 1. helper dfs recursive 处理包括root的情况 +- 2. self recursive 来引领 skip root的情况. + +#### 特点 +- 与 `Binary Tree Longest Consecutive Sequence II` 在recursive的做法上很相似: +- 利用dfs做包括root的recursive computation +- 利用这个function自己, 做`不包括root的recursive computation` + + + +--- + +**34. [Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右:即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list + +#### DFS +- Use Map 来存每一个level的结果 +- dfs function 里, 如果 input depth 不存在, 就add to map. +- dfs function 里面先: dfs(node.right), 然后 dfs(node.left) +- 由于always depth search on right side, 所以map会被right branch populate; 然后才是 leftChild.right + + + + +--- + +**35. [Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)** Level: Medium Tags: [DFS, Tree] + + +找到从max path sum from root. 条件: 至少有一个node. + +#### DFS +- 比Binary Tree Maximum Path Sum I 简单许多. 因为条件给的更多:at least 1 node + have to start from root +- root一定用到 +- 3种情况: curr node, curr+left, curr+right +- 因为一定包括root, 说以从 `dfs(root, sum=0)` 开始, 每个level先加root, sum += root.val + + + +--- + +**36. [Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +找到binary tree 里的最长 consecutive sequence. + +#### DFS +- Divide and Conquer. dfs +- 分开 看左边/右边 +- 如果左边满足连续递增的规则, dfs (depth + 1), 如果不满足规则, dfs(depth = 1) +- 右边也是一样 +- 对结果跟max作比较, return + + + +--- + +**37. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**38. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**39. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**40. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**41. [Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST, DFS, Tree] + + +BST里面有2个node misplace, 要归为. 要求: O(1) extra space + +#### Observation +- BST inorder traversal should give small -> large sequence +- misplaced means: a **large**->small item would occur, and later a large>**small** would occur. +- The first large && second small item are the 2 candidates. Example +- [1, 5, 7, 10, 12, 15, 18] +- [1, 5, `15, 10`, `12, 7`, 18] + +#### dfs, O(1) extra space +- traverse, and take note of the candidate +- at the end, swap value of the 2 candidates + +#### O(n) space +- inorder traversal the nodes and save in array, find the 2 items misplanced and swap them +- But O(n) space should not be allowed + + + + +--- + +**42. [Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)** Level: Medium Tags: [DFS, Hash Table, Tree] + + +给一串3-digit 的数组. 每个数字的表达一个TreeNode, 3 digit分别代表: depth.position.value + +这串数字已经从小到大排列. 求: 所有可能的 root->leaf path 的所有可能的 path sum 总和. + +#### DFS, Hash Table +- 因为`前两个digit可以uniquely identify`一个node, 所以可以把前两个digit作为key, 定位node. +- 特点: 比如考虑root, 有 n 个leaf, 就会加 n 遍root, 因为有 n 个 unique path嘛. +- 实现: 每个node, 上来先把curr value加进sum; 只要有child, 到这个node位置的以上path sum 就要被重加一次. +- format: depth.position.value. (on same level, position may not be continuous) +- approach: map each number into: , and dfs. +- Start from dfs(map, rootKey, sum): +- 1. add node value to sum +- 2. compute potential child. +- 3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +- 4. also, if child exist, dfs into next level +- Space, time O(n) + + + +--- + +**43. [Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + +time: O(n) +space: O(n) + +给一个tree, 按照题意找最一个node满足: +1. 这个node的subtree涵盖最深level的所有leaves. +2. 这个node必须是能找到的最deep那个 + +条件2的需求是因为: root本身就是满足条件1的node, 还有很多Higher-level node也是如此, 所以要找那个deepest. + + +#### DFS on tree +- 分析题目, 思想是: 看到tree里面所有的leaves, 找到他们最deep的 common ancestor +- Maintain a map +- Recursively dfs: return deepest node that has all leaves by these comparisons: +- 1. If left,right child same depth, return root: they need common ancestor +- 2. If not same depth, return the one with larger depth +- 被传送去上一个level的, 永远都是subtree里面符合题意的: the node containing all leaf nodes +- Visit all nodes once O(n), space O(n) + +#### BFS +- Find all leaves at deepest level +- Use map to track each node-parent +- Backtrack all nodes to find common ancestor + + + +--- + +**44. [Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)** Level: Medium Tags: [Linked List, Stack, Tree] + +time: O(n) +space: O(n) + +给一个BST, convert成 sorted doubly DoublyListNode. + +#### Inorder Traversal, Linked List +- 会iterative traverse Binary Search Tree(Stack && handle left-dig-down) +- create Doubly-ListNode, 注意用一个dNode作为tail node of the list + +##### Iterative inorder traversal +- 在check right node的事后, +- 不论right == null or != null, 每次都要强行move to right. +- 如果不node = node.right, +- 很可能发生窘境: +- node always = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 + + + +--- + +**45. [Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + +time: O(n) +space: O(n) + +#### Queue +- 简单的level traversal.根据level奇数偶数而add到不同位子. +- Option1: based on level % 2, insert to front/end of list +- Option2: based on level, insert right/left of node into queue + + + +--- + +**46. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**47. [Populating Next Right Pointers in Each Node II.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node%20II.java)** Level: Medium Tags: [DFS, Tree] + +time: O(n) +space: O(1) + +给一个binary tree, 用constant space link 所有所有node.next to same level next node. + +#### DFS +- 用constant space 也就是不可以BFS, 但是mention了用dfs stack space没问题 (提示啊!) +- 1. link leftChild -> rightChild +- 2. resolve root.rightMost child -> first possible root.next.left/right child +- 3. dfs connect(rightChild), connect(leftChild) +- Each level should be fully linked from left side, so every reach to parent will have valid path or end. + +#### Trick +- 1. 处理 nextNode -> next -> next ...的case: 找到第一个有child的next node才可以罢休. 这个case很容易miss +- 2. 我们的假设是, 上一个level的所有node都应该是linked, 那么在dfs时候, 就应该先connect(root.right). 右孩子的全处理完毕, 那么trick1才可以施行. + + + +--- + +**48. [Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)** Level: Medium Tags: [BST, Tree] + + +找 Inorder traversal规则里的下一个. + +主要想法是考虑: + 1. 如果 node.right == null, 找上一个unprocessed node alone the inorder traversal path + 2. 如果 node.right != null, successor 一定在这个node.right那个subtree里面 +最后竟然可以简化成几行, 非常全面的BST问题: 有search, 有对inorder traversal的理解, 还有坑. + +#### Short Recursive and Iterative without Stack +- Previous solution, we use stack to hold previous cached/unprocessed items: but do we need use catch to hold them? +- If moving left: `p.val < root.val`, then root (parent of left child) is a successor candidate, so save `rst = root`. +- If moving right or equal: `p.val >= root.val`, the successor has nothing to do with curr node, so just directly dive into root.right. +- Both iterative and recursive solution can be simplified as such. + + +#### Previous Iterative + stack +- Iteratively search +- Still need stack to store previously unprocessed items along the path + +#### Previous Recursive + Stack +- 画inorder图,发现规律.每个node的后继node(successor)有几种情况: +- 1. node.right 是个leaf到底了。那么就return. +- 2. set rightNode = node.right, 但发现rightNode has a lot left children to leaf. +- 3. 比如, node.right == null, 也就是node自己是leaf,要回头看山顶找Inorder traversal规则里的下一个。 +- 发现:其实就是每层都把路过的curr node放在stack里,最上面的,就是当下改return的那个successor:) Done. + + + +--- + +**49. [Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +#### Tree +- Traverse tree: left, right +- Concept of partial compare vs. whole compare + + + +--- + +**50. [Two Sum IV - Input is a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20IV%20-%20Input%20is%20a%20BST.java)** Level: Easy Tags: [Tree] + + +HashSet to store visited items. Same old 2 sum trick. + + + +--- + +**51. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + +**52. [Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Diameter%20of%20Binary%20Tree.java)** Level: Easy Tags: [Tree] + + +找longest path (include or not include root) + +跟Binary Tree Maximum Path Sum 的想法一样: 处理single path, 或者combined path (do not include curr root) + +#### Singlepath, combined path +- `int[]{combinedPath, singlePath}`; +- pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; +- complete left/right child, or join curr root: `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`; + + + +--- + +**53. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**54. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + diff --git a/review/TreeMap.md b/review/TreeMap.md new file mode 100644 index 0000000..039866b --- /dev/null +++ b/review/TreeMap.md @@ -0,0 +1,41 @@ + + + +## TreeMap (1) +**0. [My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)** Level: Medium Tags: [Array, TreeMap] + + +Given a list of interval as calendar items. Check if newly added calendar item is overlapping. + +Understand it is only checking time, but not requiring to insert into right spot. No need to overthink. + +#### Simply O(n) check on array +- number of test cases is small, like 1000, so less concern about the time complexity +- simply loop over the list of intervals, and check if any overlapping. +- where to insert does not really matter: every time we are just checking for overlaopping, not merging any range +- **IMPORTANT**: if interval over lapping, they will have this property `Math.max(s1, s2) < Math.min(e1, e2)`. This will help detect the overlapping very easily. +- O(n^2) runtime, with simple code. But somehow this approach is faster than the TreeMap solution: maybe the test cause causes avg O(n)? + +#### TreeMap +- One constraint from the simply array solution: it always cost O(n) to find the potential overlapping interval +- We can manually sort and always manually try to find the correct element via binary search, or we could store the interval in a treeMap, where the intervals are sorted by `start`. +- As result, all we need to do for book(start, end) is to find the next element ceiling(start), last element floor(start), and check for overlapping +- This approach also saves the custom data structure +- Overall cost O(nlogn) + +##### About TreeMap +- always with key sorted ascendingly +- more costly than regular HashMap because of the sorting. Building treemap of n items: O(nlogn) + +#### Sweep line +- use `Point{int start, end; boolean start}` to mark start/end of class. Add to pq. +- Adding new item to pq, sort, and check if overlapping occurs by counting started classes +- If started classes > 1, that means we overlapped. +- Every time it could consume all classes to find the overlap, O(n^2). +- Not quite need to sort or insert at correct point, and this solution requires longer code. Not quite worthy it for a simple problem. + + + + +--- + diff --git a/review/TreeSet.md b/review/TreeSet.md new file mode 100644 index 0000000..8e233cc --- /dev/null +++ b/review/TreeSet.md @@ -0,0 +1,51 @@ + + + +## TreeSet (2) +**0. [K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)** Level: Hard Tags: [Array, BST, TreeSet] + + +题目解析后: find 2 number, that: 1. k slots between the 2 number, 2. no slots taken between the two number. + +#### BST +- BST structure not given, use TreeSet to build BST with each node +- Every time find last/next inorder element +- `treeSet.lower(x)`, `treeSet.higher(x)` +- 一旦位置相隔(k + 1), 就满足题目条件 +- O(nlogn), good enough + +#### Track slots of days +- Reverse the array, save days index into days[], where the new index is slot. +- days[i]: at slot i, which day a flower will be planted +- O(n) +- Needs to understand: http://www.cnblogs.com/grandyang/p/8415880.html + + + +--- + +**1. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + diff --git a/review/Trie.md b/review/Trie.md new file mode 100644 index 0000000..7c25e74 --- /dev/null +++ b/review/Trie.md @@ -0,0 +1,292 @@ + + + +## Trie (11) +**0. [Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Prefix%20and%20Suffix%20Search.java)** Level: Hard Tags: [Trie] + + + + +--- + +**1. [Maximum XOR of Two Numbers in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20XOR%20of%20Two%20Numbers%20in%20an%20Array.java)** Level: Medium Tags: [Bit Manipulation, Trie] + + +比较难想到. 利用到XOR性质A^B=C, then A=B^C. +1. 枚举可能的A, 2. 然后一个个猜. + +1. 枚举A: 因为求MAX肯定是找leading-1最多的数字, 那么枚举A从(1000000...000)开始, +每次多一位取1或者0 +2. 因为枚举A的时候是按照每个bit来, 那么B和C也要以同样数位出现. +这里吧B和C变成了prefix的形式, 放在了set里面. +跟2sum用hashmap的思想类似, 每次用枚举的 A^B=C, 看看结果C是否已经在set里面. +如果在, 证明枚举的A可能被B^C得出, 那么就找到了一种情况. + +还用到一些技巧: +mask = (1 << i); // i位mask +mask = mask | (1 << i); // prefix mask + + + +--- + +**2. [Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- HashMap构建Trie. Trie三个Method: +- 1. Inset: 加 word +- 2. Search: 找word +- 3. StartWith: 找prefix + +##### 特点 +- 只有两条children的是binary tree. 那么多个children就是Trie +- 那么没有left/right pointer怎么找孩子? +- 用HashMap,以child的label为Key,value就是child node。 HashMap走位 + +##### 其他 +- node里的char在这是optional. 只要在每个TrieNode里面用map存储向下分布的children就好了. +- 另外有种题目,比如是跟其他种类的search相关,在结尾要return whole string,就可以在node里存一个up-to-this-point的String。 + +##### Previous Note +- 如果是遇到一个一个字查询的题,可以考虑一下。 +- 构建TrieNode的时候要注意:如何找孩子?如果是个map的话,其实就挺好走位的。 +- 而且,每个node里面的 char 或者string有时候用处不大, +- 可以为空。但是有些题目,比如在结尾要return一些什么String,就可以在end string那边存一个真的String。 + + + + + +--- + +**3. [Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +Trie结构, prefix tree的变形: '.'可以代替任何字符,那么就要iterate这个node所有的children. + +节点里面有char, isEnd, HashMap +Build trie = Insert word:没node就加,有node就移动。 +Search word:没有node就报错. 到结尾return true + +这题因为'.'可以代替任何possible的字符,没一种都是一个新的path,所以recursive做比较好些。 +(iterative就要queue了,麻烦点) + + + +--- + +**4. [Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)** Level: Hard Tags: [Backtracking, DFS, Trie] + + +给一串words, 还有一个2D character matrix. 找到所有可以形成的words. 条件: 2D matrix 只可以相邻走位. + +#### Trie, DFS +- 相比之前的implementation, 有一些地方可以优化: +- 1. Backtracking时候, 在board[][] 上面mark就可以, 不需要开一个visited[][] +- 2. 不需要implement trie的所有方程, 用不到: 这里只需要insert. +- 普通的trie题目会让你search a word, 但是这里是用一个board, 看board的每一个字母能不能走出个Word. +- 也就是: 这里的search是自己手动写, 不是传统的trie search() funcombination +- 3. TrieNode里面存在 end的时候存string word, 表示到底. 用完了 word = null, 刚好截断重复查找的问题. + +##### 关于Trie +- Build Trie with target words: insert, search, startWith. Sometimes, just: `buildTree(words)` and return root. +- 依然要对board matrix做DFS。 +- no for loop on words. 直接对board DFS: +- 每一层,都会有个up-to-this-point的string. 在Trie里面check它是不是存在。以此判断。 +- 若不存在,就不必继续DFS下去了。 +- Trie solution time complexity, much better: +- build Trie: n * wordMaxLength +- search: boardWidth * boardHeight * (4^wordMaxLength + wordMaxLength[Trie Search]) + + +#### Regular DFS +- for loop on words: inside, do board DFS based on each word. +- Time cpmplexity: word[].length * boardWidth * boardHeight * (4^wordMaxLength) + +#### Previous Notes +- Big improvement: use boolean visited on TrieNode! +- 不要用rst.contains(...), 因为这个是O(n) 在leetcode还是会timeout(lintcode竟然可以pass)! +- 在Trie search() method 里面,凡是visit过的,mark一下。 + + + + +--- + +**5. [Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)** Level: Hard Tags: [Backtracking, Trie] + + +可以开Trie class, 里面用到TrieNode. 开Trie(words) 可以直接initalize with for loop +TrieNode 里面可以有一个 List startWith: 记录可以到达这个点的所有string: 有点像树形, ancestor形状的存储. + +神操作: +根据square的性质, 如果选中了list of words, 设定 int prefixIndex = list.size(). +取出list里面的所有word[prefixedIndex], 并且加在一起, 就是下一个word candidate的 prefix. + +形象一点: +list = ["ball", "area"]; +prefixIndex = list.size(); ball[prefixIndex] = 'l'; area[prefixIndex] = 'e'; +//then +candidatePrefix = ball[prefixIndex] + area[prefixIndex] = "le"; +这里就可以用到Trie的那个 findByPrefix function, 在每个点, 都存有所有这个点能产生的candidate. +这时, 试一试所有candidate: dfs + +能想到这种倒转的结构来存prefix candidates 在 Trie里面, 这个想法非常值得思考. + + + +--- + +**6. [Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)** Level: Hard Tags: [Hash Table, String, Trie] + + +Obvious的做法是全部试一遍, 判断, 变成 O(n^2) * O(m) = O(mn^2). O(m): isPalindrome() time. + +当然不行了, 那就看是O(nlogN), 还是O(n)? + +#### 方法1: Hash Table + Palindrome的性质. 复合型. +O(mn) + +##### 思路 +- 每一个word, 都可以拆分成 front + mid + end. 如果这个word + 其他word可以组成palindrome,那就是说 +- 砍掉 (mid+end), front.reverse() 应该存在 words[] 里面. +- 砍掉 (front+mid), end.reverse() 应该存在 words[] 里面. +- 我们用HashMap存所有的, 然后reverse, 找配对就好. + +##### Corner case +- 如果有 empty string "", 那么它跟任何一个palindrome word, 都可以配对, 并且根据位置前后变换, 凑成2份 distinct indexes. +- 这样就有了那个 `if (reverseEnd.equals("")) {...}` 的logic. +- 注意: 虽然在处理砍头/砍尾的两个 for loop里面都在根据 empty string 重复记录, + 但因为 "" 自己本身不能作为起点, 所以overall只会在被其他palindrome配对时记录一次. + + +#### 方法2: Trie +还要做一下那. + + + +--- + +**7. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**8. [Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序, 排序以后才能逐个看是否partial string已经存在 +- 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 +- 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: 1. 长 string 排在前; 2. 相等length, 按照dictionary order 排序 +- 全部放入Trie. Trie.insert() +- 针对sorted words array, 从最长的开始找 Trie.startWith. +- 一旦找到, 就是符合题意的, 直接return. +- 注意: startWith 必须每一个node都是 isEnd, 才满足'逐个字母拼出' 的条件. +- Time: build Trie O(mn) + sort:O(nlogn) => O(nlogn) +- Space: O(mn) + +#### +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**9. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**10. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + diff --git a/review/Two Pointers.md b/review/Two Pointers.md new file mode 100644 index 0000000..e677f15 --- /dev/null +++ b/review/Two Pointers.md @@ -0,0 +1,649 @@ + + + +## Two Pointers (38) +**0. [Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)** Level: Easy Tags: [Array, Two Pointers] + + +- 更正常的start/end partition pointer类似: when condition meet, swap +- Clean up TODO + + + +--- + +**1. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + + +--- + +**2. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + + +--- + +**3. [Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i target,那么其中所有num[left++]的加上num[right]都>target. +也就是,num[right]不动,计算加入挪动left能有多少组,那就是: right-left这么多。 全部加到count上去。 +然后right--.换个right去和前面的left部分作比较。 + + + +--- + +**6. [3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/3%20Sum%20Closest.java)** Level: Medium Tags: [Array, Two Pointers] + + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**7. [3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum.java)** Level: Medium Tags: [Array, Two Pointers] + + + +#### sort array, for loop + two pointer. O(n^2) +- 处理duplicate wthin triplets: +- 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. +- 如果是triplet内有重复, 用完start point, 移动到结尾. + +Previous notes: +注意: + 1. 找 value triplets, 多个结果。注意,并非找index。 + 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 + +步骤: + 1. For loop 挑个数字A. + 2. 2Sum 出一堆2个数字的结果 + 3. Cross match 步骤1里面的A. + +时间 O(n^2), 两个nested loop + +另外, 还是可以用HashMap来做2Sum。稍微短点。还是要注意handle duplicates. + + + + +--- + +**8. [3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum%20Smaller.java)** Level: Medium Tags: [Array, Two Pointers] + + +一般的O(n3)肯定不行。在此基础上优化。 +发现j,k满足条件时候,(k - j)就是所有 sum target, 又因为j不能后退,只能k--,那么问题就被锁定了. 这样可以做到O(n2) + + + +--- + +**9. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**10. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**11. [Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)** Level: Medium Tags: [Hash Table, String, Two Pointers] + + +方法1: +Two Pointers +双指针: 从start开始遍历, 但是第一步是while loop来推进end; 直到推不动end, 然后start++ +巧妙点: 因为end是外围variable, 在start的loop上, end不会重置;[star ~ end] 中间不需要重复计算. +最终可以O(n); + +Previous verison of two pointers: +用两个pointer, head和i. +注意:head很可能被退回到很早的地方,比如abbbbbba,当遇到第二个a,head竟然变成了 head = 0+1 = 1. +当然这是不对的,所以head要确保一直增长,不回溯 + +方法2: + HashMap: + 一旦有重复, rest map. + 没有重复时候, 不断map.put(), 然后求max值 + +问题: 每次reset map之后就开始从新从一个最早的index计算, 最坏情况是O(n^2): +'abcdef....xyza' + + + + +--- + +**12. [Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, String, Two Pointers] + + +基本思想: 用个char[]存string的frequency. 然后2pointer, end走到底, 不断validate. +符合的就process as result candidate. + +HashMap的做法比char[]写起来要复杂一点, 但是更generic + + + +--- + +**13. [Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Two Pointer: Slow Fast Pointer +- O(1) sapce: 用快慢指针。一个跑.next, 一个跑.next.next。 总有一次,fast会因为cycle而追上slow。 +- 那个时候其实slow.val = fast.val. + +#### Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**14. [Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +O(n), one pace, no extra space +找到窗口, 然后平移, 最后pre 和 head之间 skip一个node就好. + + + +--- + +**15. [Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Linked List, Math, Two Pointers] + + +LinkedList 里面有 cycle, 找到cycle的起始点(第一个重复出现的element). + +#### Slow, fast Pointer +- 快慢指针, O(1)space. +- 1. 确认有cycle后 2. 数学问题:找到开头. +- 当head == slow.next时候, head就是cycle starting point. +- 也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +#### 证明 +- 1. 假设慢指针走t步, 快指针走快一倍, 也就是2t. +- 2. 我们假设cycle的长度是Y, 而进入cycle之前的长度为X. +- 3. 假设慢指针走了m圈cycle, 而快指针走了n圈cycle之后, 两个pointer相遇. +- 4. 最终在Y cycle里面的K点相遇, 也就是两个指针都在这最后一圈里面走了K 步. +- 那么: +- t = X + mY + K +- 2t = X + nY + K +- 整合公式: X + K = (n - 2m)Y +- 这里的m和n不过是整数的跑圈数, 也就是说X和K加在一起, 总归是结束cycle. X 和 K 互补 +- 结论: 当slow/fast 指针在K点相遇后, 再走X步, 就到了cycle的起点, 也就是题目要求的起点. + +#### Hash Table, O(n) space + + + + +--- + +**16. [Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. +#### 方法1 +Array, 维持一个左手最高墙array, 右手最高强array. +对于每个index而言, vertically 能存放的最大水柱, 就是靠左右最高墙决定的: +min(leftHighestWall, rightHighestWall) - currHeight. + +#### 方法2 +方法1上面的优化, two pointer, 还是找左边最高和右边最高. O(1) space. +利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +左边按照maxLeft来计算, 右边按照maxRight来计算. + +#### 方法3 +2 Pointers, 双面夹击: +1. 找中间最高bar的index +2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条。 +3. 每次还要减去block自身的height + +#### 方法4 +主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. + + + + +--- + +**17. [Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +- 注意不要思维定式: 以为mid是index +- 这里mid其实是binary search on value [1, n] 的一个value. +- 再次用到validate() function + +Time: O(nLogN) + + + +--- + +**18. [Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20in%20String.java)** Level: Medium Tags: [Two Pointers] + + +#### Two Pointer +- 如果做s1的permudation, 时间复杂度是O(n!) 肯定不可以 +- 这里用HashTable的做法 (因为26字母, 所以用int[26]简化) 来记录window内的 character count +- 如果window内的character count 相等, 那么就是permudation +- 更进一步优化: 找两个map相互对应, 不如用一个 int[26]: s1对遇到的character做加法, s2对遇到的character做减法 +- two pointer 运用在 n1, n2 的把控; 以及 s2.charAt(i - n1) 这一步 + + + +--- + +**19. [Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20strStr().java)** Level: Easy Tags: [String, Two Pointers] + + +给两个string A, B, 找一个 B 在 A 种的起始位置. + +#### Two Pointer +- 找到B在A中的起始位置, 然后看一下从这个点开始的substring是否等于B就可以了 +- 还挺多坑的, 这些可以帮助优化: +- 1. 当B是“”的时候,也就是能在A的其实位置找到B....index = 0. +- 2. edge condition: 如果 haystack.length() < needle.length() 的话, 必须错, return -1 +- 3. 如果在某个index, A后面剩下的长度, 比B的长度短, 也是误解, return -1 + + + +--- + +**20. [Interleaving Positive and Negative Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20Positive%20and%20Negative%20Numbers.java)** Level: Medium Tags: [Two Pointers] + + +给一串数组 有正负数. 重新排列, 让数组里面 正数 和 负数 相隔开. 原来的order无所谓 + +#### Two pointer +- 需要知道正负的位置, 所以排序 O(nlogN) +- 考虑: 正数多还是负数多的问题, 举栗子就看出来端倪了 +- 然后Two Pointer, swap +- Time O(nlogn), space O(n) + +#### extra space +- 用extra O(n) space, 把正负分成两个list +- 然后分别按照index填回去 +- time O(n). space O(n) +- 但是就么有用到Two pointer了 + + + +--- + +**21. [Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单, 但是要在Linkde List random access坐标, 是很难得: 所以需要把一半 ListNode 翻转 +- reverse linked list: 遍历接开头 +- 用快慢指正找到mid point +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + +#### Previous Note +- Palindrome都是要两边回溯相等 +- linkedlist不能reverse iterating, 那么就reverse the list, 从中间开花作比较。 + + + +--- + +**22. [Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Check Palindrome +- 前后两个指针, 往中间移动, 查看是否字母重合 + +#### 过滤 alphanumeric +- 可以用 ASCII code 来手动过滤, 只要 '0' ~ '9', 'a' ~ 'z', 'A' - 'Z' 之间的 +- 也可以用 regular expression: match 所有这些字母, 是 [a-zA-Z0-9] +- 那凡是不是这些字母的 match, 就是取反: "[^a-zA-Z0-9]". 测试: https://regex101.com/ + + + +--- + +**23. [Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- Two pointers 其实也可以是一个 for loop pointer, 另一个 dynamic variable. +- track unique index +- skip duplicated items +- O(n) + +#### 思考模式: +- Remove Duplicate from Array 不同于remove from linked list. +- LinkedList里面我们是最好不要动node.val的,直接把node去掉。 +- 而array我们很难直接把node去掉,又不能用新array,那么就要: +- 把不重复的element一个个放到最前面。 +- 这个思想跟merge two sorted array (其中一个后续非常长的array可以放下arr1,arr2) 类似。 +- 就是找个不会事后mess up,不会去动得index,把满足条件的element 填进去。这样保证了in place. +- *反向思维*:remove duplicate, 实际上也是找unique elements, and insert into original array + + + +--- + +**24. [Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- 跟 `Remove Duplicates from Sorted Array` 几乎一模一样, 只不过unique index现在可以 validate 2 位 +- 其余一模一样, use index to track unique item; skip if duplicated for more than 2 times +- O(n) time, O(1) space +- 这里也可以真的用2个pointers 写while loop, 但是没有必要, 只是单纯地走一个for loop其实就足够. + + + +--- + +**25. [Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +给一个single linked list, 右移k steps. k non-negative. + +#### Linked List basics +- 记得用dummy.next来存head. +- 特殊: 这里k可能大于list总长. 写一写linked node 移动的步数, 然后 k = k % n. +- 找到newTail, newHead, 然后利用dummy, 换位子 + + + +--- + +**26. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**27. [Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Two Pointers +- 木桶理论。盛水的最高取决于最低的那面墙。 +- 左右两墙,往中间跑动。 +- 另:若一面墙已经小于另外一面,就要移动,换掉矮墙(可能下一面更高,或更低) +- 但决不能换掉当下的高墙,因为低墙已经limit的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**28. [Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Linked List +- linked list 不能像partitioin array一样从两边遍历 +- 把小于value的加在前半段, 把 >= value的加在后半段 +- 做法很普通: 建造两个list, midTail pointer, post pointer +- 把满足条件(=x)的数字分别放到两个list里面 +- 记得用dummyNode track head. +- 最终midTail.next = post链接起来。 + + + +--- + +**29. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**30. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**31. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + +**32. [Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素。 +- 注意,从尾部,是大数字优先排末尾的. + + + +--- + +**33. [Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Move%20Zeroes.java)** Level: Easy Tags: [Array, Two Pointers] + + +Move non-zero elements to front of array; preseve order. + +#### Two Pointers +- Outside pointer that moves in certain condition. +- Save appropirate elements + + + +--- + +**34. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + + +--- + +**35. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**36. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**37. [Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)** Level: Easy Tags: [Math, String, Two Pointers] + + +#### Two pointers +- Use two pointers i, j to track the 2 strings +- Add when i and j are applicable. While (i >= 0 || j >= 0) +- StringBuffer.insert(0, x); +- handle carry + +#### wrong: convert to int +- 土办法没技术,把binary换成数字,加起来,再换成binary +- 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + diff --git a/review/Two Stacks.md b/review/Two Stacks.md new file mode 100644 index 0000000..524ffc6 --- /dev/null +++ b/review/Two Stacks.md @@ -0,0 +1,33 @@ + + + +## Two Stacks (1) +**0. [Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Recursive +trivial, 先加left recursively, 再加right recursively, 然后组成头部. + +#### Stack +- 双stack的思想, 需要在图纸上画一画 +- 原本需要的顺序是: 先leftChild, rightChild, currNode. +- 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild +- 这样出来的结果是reverse的, 那么翻转一下就可以了. +- v1做的时候用了stack1, stack2, 因为根据这个双stack的思想而来 +- v2简化, 可以放在一个stack里面, 每次record result 的时候: rst.add(0, item); + +##### 利用stack的特点 +- 每次加element进stack的时候, 想要在 bottom/后process的, 先加 +- 想要下一轮立刻process的, 最后push进stack. + +##### 注意 +这些binary tree traversal的题目.常常有多个做法:recursive or iterative + + + +--- + diff --git a/review/Union Find.md b/review/Union Find.md new file mode 100644 index 0000000..fea9a0e --- /dev/null +++ b/review/Union Find.md @@ -0,0 +1,334 @@ + + + +## Union Find (15) +**0. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**1. [Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)** Level: Medium Tags: [Union Find] + + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + + + +--- + +**2. [Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)** Level: Medium Tags: [Union Find] + + +Lint还不能跑, 全部按照题意和答案document的. + + + +--- + +**3. [Connecting Graph III.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20III.java)** Level: Medium Tags: [Union Find] + + +还是UnionFind的变形, 这次是算有剩下多少个union. 其实非常简单, 维持一个全局变量count: +一开始count=n, 因为全是散装elements; 每次union, count--. + + + +--- + +**4. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**5. [Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands%20II.java)** Level: Hard Tags: [Union Find] + + +给一个island grid[][], and list of operations to fill a particualr (x,y) position. + +count # of remaining island after each operation. + +#### Union Find, model with int[] +- 把board转换成1D array, 就可以用union-find来判断了. +- 用int[] father 的unionFind, 需要转换2D position into 1D index. 这样比较clean +- 判断时,是在四个方向各走一步,判断是否是同一个Land. +- 每走一次operator,都会count++. 若发现是同一个island, count-- +- count的加减, 都放在了UnionFind自己的function里面, 方便tracking, 给几个helper function就对了. +- Time: O(k * log(mn)) + +#### Union Find, model with Hashmap +- 用HashMap的Union-find. + + +#### Note: +- Proof of UnionFind log(n) time: https://en.wikipedia.org/wiki/Proof_of_O(log*n)_time_complexity_of_union%E2%80%93find + + + +--- + +**6. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + +**7. [Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)** Level: Hard Tags: [Array, Hash Table, Union Find] + + +给一串数字, unsorted, 找这串数字里面的连续元素序列长度 (consecutive序列, 是数字连续, 并不是说要按照原order) + +#### HashSet +- 要想看连续元素, 必须要num++, num--这样搜索 +- 1. 需要O(1)找到元素 +- 2. 需要简单快速找到 num - 1, num + 1. +- 如果用min,max开array, 耗费空间 +- 用HashSet来存, 用set.contains() 来查找 num - 1, num + 1 存在与否 +- for loop. O(n) +- 里面的while loop 一般不会有O(n); 一旦O(n), 也意味着set 清零, for loop也不会有更多 inner while 的衍生. +- overall O(n) 时间复杂度 + + +#### Union Find +- 最终是要把相连的元素算一下总长, 其实也就是把元素group起来, 相连的group在一起, 于是想到UnionFind +- 这里用到了一个`int[] size` 来帮助处理 `合并的时候parent是哪个`的问题: 永远往group大的union里去 +- main function 里面, 有一个map来track, 每个元素, 只处理1遍. +- union的内容: current number - 1, current number + 1 +- https://www.jianshu.com/p/e6b955ca208f + +##### 特点 +- Union Find 在index上做好像更加容易 +- 其他union find function: `boolean connected(a,b){return find(a) == find(b)}` + + + +--- + +**8. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**9. [Find the Weak Connected Component in the Directed Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Weak%20Connected%20Component%20in%20the%20Directed%20Graph.java)** Level: Medium Tags: [Union Find] + + +遍历 weak connected graph, 将结果存在 List>种. + +#### Union Find +- 跟传统的UnionFind有两点不同: +- 1. 用 Map 代替 int[], 因为没有给出 graph node label的 boundary. +- 2. find(x)时候, 没有去update `parent[x]/map.put(x, ..)`. 因为我们最终需要找到这个path. +- 无法用传统dfs: directed node 无法point到上一个点; 必须用`存parent的方式把所有node遍历掉` + +#### Identify这是个union-find问题 +- 看到了weak component的形式: 一个点指向所有,那么所有的点都有一个公共的parent,然后就是要找出这些点。 +- 为何不能从一个点出发,比如A,直接print它所有的neighbors呢: +- 如果轮到了B点,那因为是directed,它也不知道A的情况,也不知道改如何继续加,或者下手。 +- 所以,要把所有跟A有关系的点,或者接下去和A的neighbor有关系的点,都放进union-find里面,让这些点有Common parents. +- 最后output的想法: +- 做一个 map 。 +- 之前我们不是给每个num都存好了parent了嘛。 +- 每个num都有个parent, 然后不同的parent就创造一个不同的list。 +- 最后,把Map里面所有的list拿出来就好了。 + + + +--- + +**10. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**11. [Bricks Falling When Hit.java](https://github.com/awangdev/LintCode/blob/master/Java/Bricks%20Falling%20When%20Hit.java)** Level: Hard Tags: [Union Find] + + +给一个matrix of 1 and 0, `1` 代表brick. 连着ceiling的brick就不会drop. 给一串coordinate hits[][], 记录每次take down 1 brick 后, 会drop多少个. + +#### UnionFind +- 1. 我们知道大部分的brick可能都是连着ceiling, 所以每次正向检查都traverse all and timeout +- 2. 能否用union, 把connect都装在一起, 然后drop brick的时候把连着的都drop掉? 难: 因为还是要check所有brick当下的status. +- 受其他人的解答启发, 由于是计算count,我们可以`反向考虑`: +- 把hit-brick全部mark=2 (就当舍弃不算), 观察整个局面的最后一步, 先把所有还连着ceiling的brick算一下总数, 统计在unionFind的 全部统计在count[0] 里面. +- 剩下的不连着ceiling的也就是一个个isolated island +- 做法: 把hit-brick 一个个加回去, 然后再做一次union, 看看最终连到ceiling的有多少个. 增加的count, 就是正向思考时 dropped brick 数量! + +##### Union Find 变种 +- 还是用数字index做union find, 但是把每一个index都+1, 右移一位, 而[0]留下来做特殊用途: +- 用union at 0来 统计总共的remain count of ceiling-connected bricks, where `x = 0`. +- 如果在其他其他题目种, 条件可能就不是`x=0`, 但也可以用这个 union index = 0 来做一个root的统计 +- 关键: 把最后一个hit brick加回去, 然后再重新union一下这个hit-brick周围: count增加的变化, 不就是缺少hit-brick时候掉下去的数量. + + +#### DFS (timeout) +- 考虑每个hit的四周, 全部traverse, 没有连着ceiling就全部: +- 比如是 200 x 200 的 全部是1的matrix, 任何一次traverse都要到顶; 重复计算, 所以timeout +- 算法是没错, 但是不efficient. +- 想要减少重复计算, 但是又不能提前计算: grid在不断变化. 所以看能不能把连着ceiling的都group起来, 可以O(1)快速check? + + + + +--- + +**12. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**13. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**14. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + diff --git a/review/level/Easy.md b/review/level/Easy.md new file mode 100644 index 0000000..78433f6 --- /dev/null +++ b/review/level/Easy.md @@ -0,0 +1,2382 @@ + + + +## Easy (153) +**0. [Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Hamming%20Distance.java)** Level: Easy Tags: [] + +bit: XOR, &, shift>> + + + +--- + +**1. [Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Happy%20Number.java)** Level: Easy Tags: [] + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + + + +--- + +**2. [HashWithArray.java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithArray.java)** Level: Easy Tags: [] + + + + +--- + +**3. [Heaters.java](https://github.com/awangdev/LintCode/blob/master/Java/Heaters.java)** Level: Easy Tags: [] + +第一步: +生题型, 理解题意需要时间: +从字面和画图而言, 就是定住房子一个个过,房子左右的distance需要足够达到heater. 目标是招尽可能小的radius, 所以house和heater紧贴着是必要的. +在for loop里面定下house,把heater当作一个区间移动, 达到的第一个合适区间,这就是当下最小的理想radius,取这个值跟既定radius作比较。 +比较之后,继续移动house,再试着移动heater区间去match。 + +第二步: +Binary Search + +注意! +题目没有说given array是否sort, 我们必须sort才能够区间移动或者binary search. +TODO: +http://www.cnblogs.com/grandyang/p/6181626.html + + + +--- + +**4. [IndexMatch.java](https://github.com/awangdev/LintCode/blob/master/Java/IndexMatch.java)** Level: Easy Tags: [] + +有序, 假设有这样的数字:target. +target 左边的数字,一定不比index大,target右边的数字,一定比index大。 +这样可以binary search.O(logn) + + + +--- + +**5. [Insert Node in a Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Node%20in%20a%20Binary%20Search%20Tree%20.java)** Level: Easy Tags: [BST] + + +往Binary Search Tree里面加东西,一定会找到一个合适的leaf加上去。 + +那么:就是说someNode.left or someNode.right是null时,就是insert node的地方。 + +找到那个someNode就按照正常的Binary Search Tree规律。 + + + +--- + +**6. [Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/Jewels%20and%20Stones.java)** Level: Easy Tags: [Hash Table] + +1524017454 + +给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 + +#### Basic HashSet + + + +--- + +**7. [Longest Univalue Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Univalue%20Path.java)** Level: Easy Tags: [] + +弄明白path的意义: 连接node的edge. +要找MAX, 可以在class scope里面定义一个max variable. + +用minimum amount of code来概括几种不同的情况: left == root, right == root, or left == root == right. + + + +--- + +**8. [Matrix Zigzag Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Matrix%20Zigzag%20Traversal.java)** Level: Easy Tags: [] + +分析4个step:right, left-bottom,down,right-up +implement时注意index.有点耐心 + + + +--- + +**9. [Minimum Absolute Difference in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Absolute%20Difference%20in%20BST.java)** Level: Easy Tags: [BST] + + +BST: inorder-traversal: 先left node(adding to stack till left leav), 再process stack.peek (mid node), 再 add rightNode && dive to rightNode.left leaf + + + +--- + +**10. [O(1) Check Power of 2.java](https://github.com/awangdev/LintCode/blob/master/Java/O(1)%20Check%20Power%20of%202.java)** Level: Easy Tags: [Bit Manipulation] + + + + +--- + +**11. [Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)** Level: Easy Tags: [Array, Two Pointers] + + +- 更正常的start/end partition pointer类似: when condition meet, swap +- Clean up TODO + + + +--- + +**12. [Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Pascal's%20Triangle%20II.java)** Level: Easy Tags: [] + +简单处理array list. + + + +--- + +**13. [Permutation Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Index.java)** Level: Easy Tags: [] + +和Permutation Sequence相反的题目。思想类似。 + +题目为Easy,琢磨很久,分析: +每个数位的数字,都是跳过了小于这数字开头的多种可能。 + +举例【6,5,2】吧。我们找6,5,2是permudation里面的第几个。 +正常排序,也就是permutation的第一个,应该是【2,5,6】 +如果要从首位,2,变成6,要跨过多少可能性呢? +很简单,就是问:小于6的数字有多少个呢?(2,5).每个数字变成head,都有各自的一套变化,都有(n-1)!种可能。 + +本题做法:每个(n-1)!加起来。 Note:(n-1) means, 开头的数字(2,5)各带出多少种排列,也就是不就是(n-1)!嘛。 + 这一步,计算数量很简单: (有几个小于6的数字) ×(除去head剩下有多少个数字)! + +以上 ,都是为了把6推上皇位,而牺牲的条数。 + +那么把6推上去以后,还有接下去的呢。 + +接下去要看5,2. +6确定,后面permudation可变的情况有可能是【6,5,2】,那还可能是【6,2,5】呢。 + +Same process, 看given 数组的第二位5,算它接下去: +1. 有几个数字小于5呢? +2. 除去5,还有几个数字可以 factorial呢? +3. 一样的。第一步就结果乘以第二步。 + +最后接下去要看最后一个元素2了。 + + +6,5,2全看过了以后,加起来。 +就是【6,5,2】上位,所踏过的所有小命啊! + +我这解释太生动了。因为耗费了好长时间思考... + + + +--- + +**14. [Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array] + + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**15. [Reshape the Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Reshape%20the%20Matrix.java)** Level: Easy Tags: [] + +读例子理解题意. +理清counter case. Basic implementation + + + +--- + +**16. [Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)** Level: Easy Tags: [] + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + + + +--- + +**17. [Search Insert Position.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Insert%20Position.java)** Level: Easy Tags: [] + +一般的binary search. +在结尾判断该return 哪个position。 + + +--- + +**18. [Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Word%20Distance.java)** Level: Easy Tags: [] + +找short distance, wordB可以在wordA的前后;而同一时间,只需要计算一个最近的up to date的distance。 +greedy不断变更A/B index再做比较即可。 + + + +--- + +**19. [Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number.java)** Level: Easy Tags: [] + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + + + +--- + +**20. [String Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/String%20Permutation.java)** Level: Easy Tags: [] + +把#of occurrences 存进HashMap, 第一个string 做加法,第二个string做减法。最后看是否有不等于0的作判断。 + + + +--- + +**21. [Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)** Level: Easy Tags: [Math] + + + + +--- + +**22. [Two Strings Are Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Strings%20Are%20Anagrams.java)** Level: Easy Tags: [] + +方法1:char ascii 用count[256] +坑:不要想象这个是个26letter lowercase. may not be true. + +方法2: 若是其他字符encoding, 而不只是utf16-encoding (java char)? +那么就继续用string去做 + + + +--- + +**23. [Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited value. +- 在nest for loop里面validate row,col,and block. +- validate block要利用i 和 j 增长的规律。 +- 说白了,i && j是按照0~n增长的index, 具体怎么用是可以flexible的。这个方法在同一个nest for loop解决所有运算。 +- `int c = 3 * (i % 3) + j % 3;` //make use of how i and j increases +- `int r = 3 * (i / 3) + j / 3;` + +#### A bit Slower approach +- 单独做block validation: validate block的时候虽然看到了4层for. 其实也就是n^2 +- 可能代码稍微复杂一点 + + + +--- + +**24. [Word Pattern.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Pattern.java)** Level: Easy Tags: [] + +每个char代表一个pattern。用HashMap. +但不够,如果a也match dog, b也match dog, 纠错了。比如pattern = "abba", str = "dog dog dog dog"。 +因此第二个HashMap 反过来。 +确保pattern和str一一对应。 + + + +--- + +**25. [Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Anagram%20Mappings.java)** Level: Easy Tags: [Hash Table] + + +比较简单, 用HashMap 存index list. 最后再遍历一遍数组A, 列举出所有元素. +O(n) + + + +--- + +**26. [Judge Route Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/Judge%20Route%20Circle.java)** Level: Easy Tags: [String] + + +简单的character checking. 各个方向, 加加减减. + + + +--- + +**27. [Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/Island%20Perimeter.java)** Level: Easy Tags: [Hash Table] + + +#### Brutle +- 每个格子4个墙;每个shared的墙要-2 (墙是两面, -1 * 2) +- 最后合计结果就好. + +#### Hash Table +- 不必想太多用HashMap做.但是也可以思考一下: +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么久可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不打,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + + + +--- + +**28. [Power of Three.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Three.java)** Level: Easy Tags: [Math] + + +方法1: +Power of 3: 3 ^ x == n ? +意思是 n / 3 一直除, 最后是可以等于1的, 那么就有了 n/=3, check n%3, 最后看结果是不是整除到1的做法. 用while loop. + +方法2: +如果n是power of 3, 那么 3 ^ x的这个 x一定是个比n小的数字. 那么可以在 0 ~ n 之间做binary serach, 但是就比较慢. + +方法3: +巧妙的想法.最大的3^x integer是 3^19. 那么找到这个数, 一定可以被n整除. 一步到位. + + + +--- + +**29. [Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)** Level: Easy Tags: [Array, Math] + + +简单的实现, 加1, 进位. 唯一取巧的地方, 最后如果要多一位, 一定是10000...这个模式, 可以走捷径, 直接来个+1size的array, 然后第一位=1. +注意,转换成long也不合理,用太多memory. + + +--- + +**30. [Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)** Level: Easy Tags: [Bit Manipulation, Math] + + +跟powerOfThree一样: 可以loop, check mod; 也可以用binary search找合适的数字. + + + +--- + +**31. [Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i>> 避免leading负数补位. + + + +--- + +**44. [Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +方法1: +用HashMap: 存一个nums1, 再拿nums2 check against map. 时间/空间:O(n) + +方法2: +Binary search? 需要array sorted. 否则时间O(nlogn)不值得. +[没做完, 有错] + + + +--- + +**45. [Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer] + + +#### Vote 计数 +- vote++, vote--到最后剩下的就是winner. Time O(n), Space O(1) +- Majority Number是指超半数. 超半数的数字, 最后都会至少有vote>=1: match current majority number,vote++;if not, vote--. +- 注意:assume valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 + +#### HashMap count occurance +- Time, Space: O(n) + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**46. [Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS] + + +给一串integers, list里面可能有nest list. 算总的sum. 规则, 如果是nested list, 每深一个depth, sum要乘以depth. + +#### DFS +- New interface to understand: object contains integer or object +- Visit all && sum, consider dfs. +- bottom->up is easier: pick nested object and execute dfs, which returns sum of it, add with (level value * weight). +- 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) +- Note1: not multiplying on overall level sum. Only multiply level with single value at this level. +- Note2:top->bottom is not necessary: there is not need of passing added object into next level. + +#### BFS +- bfs, queue, 处理queue.size(). +- use a level variable to track levels + + + +--- + +**47. [Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Same%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### BFS +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**48. [Convert Sorted Array to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20Array%20to%20Binary%20Search%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +如题, build balanced BST from sorted array + +#### DFS +- Binary Search Tree特点: 左边的node都比右边的node小. +- height balance, subtree height 相差<1, 必须左右sub tree均分. 做DFS(num, start, end) +- 在每一个level, 找到中间点, 然后分割2半, 继续dfs +- Divide and Conquer +- time/space: O(n), visit all nodes, no redundant visits. + + + +--- + +**49. [Add Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Digits.java)** Level: Easy Tags: [Math] + + +方法1: 普通做法就是按照题意, double-while loop把数字加起来. 第一层循环是O(n), 然后第二层循环就少很多数位, overall O(n) + +方法2: 找数学规律, 每过9个数字, 取mod就会开始重复, 所以给所有数字取mod 就可以间接找到答案. O(1) + + + +--- + +**50. [Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +HashMap + + + +--- + +**51. [Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Paths.java)** Level: Easy Tags: [Backtracking, Binary Tree, DFS] + + +给一个binary tree, 返回所有root-to-leaf path + +#### DFS, backtracking +- Find all paths, bfs/dfs all works. dfs will be simplier to write +- Recursive:分叉. dfs. +- top->bottom: enumerate current node into the list, carry to next level, and backtrack +- top->bottom is trivial to consider: path flows from top->bottom + +#### DFS, bottom->up +- We can also take current node.left or node.right to generate list of results from the subproblem +- let dfs return list of string candidates, and we can run pair the list with currenet node, once they come back. +- TODO: can write code to practice + +#### Iterative +- Iterative, 非递归练习了一下 +- 因为要每次切短list, 所以再加了一个Stack 来存level +- 单这道题用dfs更简单, 因为找的就是从头到尾的path, 是dfs的pattern + + + + +--- + +**52. [Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Two Pointer: Slow Fast Pointer +- O(1) sapce: 用快慢指针。一个跑.next, 一个跑.next.next。 总有一次,fast会因为cycle而追上slow。 +- 那个时候其实slow.val = fast.val. + +#### Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**53. [Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)** Level: Easy Tags: [Design, Stack] + + +双Stack:一个正常stack,另一个minStack存当下level最小值. 注意维护minStack的变化 + +另外. 如果要maxStack,也是类似做法 + + + +--- + +**54. [Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)** Level: Easy Tags: [Design, Stack] + + +#### 双Stack +画图, 知道最后maintain的stack是那个 reverseStack: pop(), peek(), empty() 都在这个stack上, 无需变换. +push()里面做stack和reverseStack的来回倾倒. +相比老的code, 在PUSH里面做倾倒, 更容易读. + +#### Previous notes +双Stack. 一个是等于是queue,一个是backfillStack. +Tricky: 是在pop()和peek()的时候backfill, 并且要等到stack用完再backfill. +写一下例子就知道,如果提早backfill,stack.peek()就不是queue的head了. + + + + +--- + +**55. [Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Integer.java)** Level: Easy Tags: [Math] + + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + + + +--- + +**56. [Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + + +#### s- qrt(int x) +- 理解题意, 从[0, x]找一个可以m*m=x的值. +- 注意, 如果找不到, 最后问考官该return一个什么值:按道理,因为return int, 会取整,那么return一个平方最close to x就可以. +- 注意 mid 用 long, 因为很可能超过最大int. + +#### sqrt(double x) +- 二分float number, 应该用精度来定义结尾. +- 还是二分, 但是判断条件变成: while ( end - start > eps) +- eps = 1e-12,也就是精度到1e-12 + + + +--- + +**57. [First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Bad%20Version.java)** Level: Easy Tags: [Binary Search] + + +Binary Search + +根据isBadVersion的性质,判断还如何end=mid or start=mid. +isBadVersion 是有方向的嘛,一个点错了,后面全错。 + + + +--- + +**58. [Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### 方法1: +找是否有overlap. priorityQueue 按照start time排序好以后, 比较current和peek: current.end > peek.start? + +#### 方法2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 + + + +--- + +**59. [Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Recursive +- 在自己的基础上recursive, 不用helper function +- Divide and Conquer, with helper(dfs) method +- O(n) time, no extra space + +#### Iterative: Stack +- Add left nodes all the way +- Print curr +- Move to right, add right if possible +- O(n) time, O(h) space + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. + +若不右移, 很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 + +#### HashMap +? How? + + + +--- + +**60. [Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/Change%20to%20Anagram.java)** Level: Easy Tags: [String] + + +HackerRank里面的random 题目: 给一个string, 一切成两半, 看两半要变化多少个字符, 能变成anagram. + +- 切两半成两个String A,B. 分别在int count[26]里面++, --. +- 记录 26个小写字母的频率. 如果全部抵消, 就是anagram. +- 注意: 最后count出来要除以2:字母不同,会在不同的字母位上加减count,那么就是刚好重复计算了一遍。所以除以二 + +- Note: HackerRank里要注意自己写: Scanner, import java.util, non-static method ...etc. + + + +--- + +**61. [Classical Binary Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Classical%20Binary%20Search.java)** Level: Easy Tags: [Binary Search] + + +#### Binary Search Template +- while: start + 1 < end +- mid = start + (end - start) / 2; +- 根据mid作比较 +- 末尾double check start, end. + + + + +--- + +**62. [Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Climbing%20Stairs.java)** Level: Easy Tags: [DP, Memoization, Sequence DP] + + +每一步可以走1步或者2步, 求总共多少种方法爬完梯子. + +#### Recursive + Memoization +- 递归很好写, 但是重复计算, timeout. time: O(2^n) +- O(2^n): each n can spawn 2 dfs child, at next level, it will keep spawn. Total 2^n nodes will spawn. +- 用全局变量int[] memo 帮助减少重复计算 +- O(n) time, space + +#### DP +- 加法原理, 最后一步被前两种走法决定: dp[i] = dp[i - 1] + dp[i - 2] +- 基础sequence DP, int[] dp = int[n + 1]; +- DP[]存的是以 1-based index的状态 +- dp[i]: count # of ways to finish 前i个 台阶 +- 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1] +- dp[0]往往是有特殊状态的 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**63. [Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +#### Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, +- 找到 node.val == target, 或者走位走完, return closest + + + +--- + +**64. [Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Preorder%20Traversal.java)** Level: Easy Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive +- 加root, left, then right. Obvious +- Divide and conquer +- 其实也不需要helper function + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**65. [Closest Number in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Number%20in%20Sorted%20Array.java)** Level: Easy Tags: [Binary Search] + + +- Binary Search 的一种变型, LintCode无法再跑一边. +- 考虑mid-1, mid+1. +- 一旦没有mid = target.index。 那么target最终就narrow down在(mid-1,mid) 或者(mid,mid+1) + + + +--- + +**66. [Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +A complete binary tree is a binary tree in which every level, except possibly the last, + +is completely filled, and all nodes are as far left as possible + +#### BFS +- 当出现了第一次有 null children的node的时候, 说明到了leaf level, mark flag = true; +- 自此以后,queue再不该有node再有child; queue后面出现的node的left/right child应该都是null +- 否则就是有问题, return false; + + + + +--- + +**67. [Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Strings.java)** Level: Easy Tags: [String] + + +看StringA是不是包括所有 StringB的字符. + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + + + +--- + +**68. [Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate.java)** Level: Easy Tags: [Array, Hash Table] + + +无序数组, 找是否有重复element, return true/false. + +#### HashSet +- No brain: HashSet. +- Time O(n), Space O(n) + +#### Sort, Binary Search +- Arrays.sort(x): Time O(nLogN), Space O(1) +- 排序后, 重复数会排在一起, 然后 binary search + + + +--- + +**69. [Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20II.java)** Level: Easy Tags: [Array, Hash Table] + + +Unsorted array, 找出是否有duplicate elemenets: 必要条件是, 这两个element的index i,j 的大小最多相差k. + +#### HashSet +- 很巧妙地根据k range地条件, 把HashSet里面的值控制在[i - k, i] +- 每次不断地往set里面加新元素, 从set里减去末尾index的元素 +- 而set.add(x)如果遇到重复, 会return false. +- 一旦在这个length k 的 range里面, 有重复, 就符合条件. +- Time O(n) + +#### HashTable +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k +- Time O(nm), m = # of duplicates + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**70. [Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)** Level: Easy Tags: [Brainteaser, DP, Game Theory] + + +#### Brainteaser +- 著名Nim游戏 +- 写一些,发现n=4,5,6,7,8...etc之后的情况有规律性: 谁先手拿到4就输了. +- 最终很简单n%4!=0就可以了, time, space O(1) + +#### DP +- 正规地找规律做, 就跟 coins in a line 一样, 按照先手后手来做 +- 可以rolling array 优化空间 +- Time O(n), 当然啦, 这个题目这样会timeout, 可以使用brainteaser的做法写出结果. + + + +--- + +**71. [Convert Integer A to Integer B.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Integer%20A%20to%20Integer%20B.java)** Level: Easy Tags: [Bit Manipulation] + + +把Integer A 转换成 Integer B 需要改变多少bits? + +#### Bit Manipulation +- a^b 显示出bit format里面有不同binary code的数位. +- 每次 (a^b)>>i 移动i位之后, 再 & 1时其实是指留下这一位的数字. +- count +- 其实用到了 ^ 找不同的bit, >> 移位, &1 mask + + + +--- + +**72. [Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)** Level: Easy Tags: [Basic Implementation] + + +根据 Cosine Similarity 的公式, basic implementation + + + +--- + +**73. [Count 1 in Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%201%20in%20Binary.java)** Level: Easy Tags: [Bit Manipulation] + + +count 一个 32-bit number binary format 里面有多少1 + +#### Bit Manipulation +- shift >> i +- apply mask & 1 + +#### Convert to string O(n) space +可以把integer -> string -> char array. + + + +--- + +**74. [Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)** Level: Easy Tags: [Basic Implementation, String] + + +介绍一种count数字的方法, 然后每一行读出上一行的结果, 一行一行推算. 问nth行是啥样? + +#### Basic Implementation +- 主要是题意很难理解, 非常misleading, 等到看明白题目, 其实没有什么算法要求. +- Count duplicates and print + + + +--- + +**75. [Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + +time: O(nm), m = # of colors +space: O(nm) + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, 但是不知道最后一个房子选什么颜色, 那么就遍历最后一个房子(i - 1)的颜色 +- 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- 考虑DP最后一个位置的情况(颜色选择):需要附带颜色status在DP[i]上: 定义二维数组, 其中一位是status +- dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. +- dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- 然后选ith 房子的 color, 再选(i-1)th 房子的color. Double for loop, skip same color + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 + + + +--- + +**76. [Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +找连续的持续上升子序列的长度. + +#### Coordinate DP +- 1D coordinate, dp 的角标, 就是代表 index i 的状态 +- 求最值, dp[i] = 在index i位置的最长子序列 +- 如果 nums[i] > nums[i - 1], dp[i] = dp[i - 1] + 1 +- 如果没有持续上升, 那么dp[i] = 1, 重头来过 +- maintain max + +#### Basic +- 用一个数存current count, maintain max + + + +--- + +**77. [House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) or rolling array O(1) + +搜刮房子, 相邻的不能碰. 每个房子里有value, 求max. + +#### Sequence DP +- dp[i]: 前i个房子拿到的max gain +- 看最后结尾状态的前一个或前两个的情况,再综合考虑当下的 +- 搞清楚当下[i]的和之前[i-x]的情况的关系: 不可以连着house, 那么就直接考虑 dp[i-2]的情况 +- Sequence DP, new dp[n + 1]; + +#### Rolling Array +- [i]'只和前两个位子 [i-1], [i - 2]'相关 +- 用%2来标记 [i], [i - 1], [i - 2]三个位置. +- 其他滚动时惯用curr/prev来表示坐标, 这里%2虽然抽象, 但是更加实用. + + + + +--- + +**78. [Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20All%20Anagrams%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, Sliding Window] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### HashTable +- count character apperance 就想到hashtable +- 注意countS, countP 的技巧: 作比较只需要O(26) +- Overall timeO(n) +- 小心不要用一个int[] count 来技术 查0, 复杂度是O(n) + + + +--- + +**79. [Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Primes.java)** Level: Easy Tags: [Hash Table, Math] + + +计数: 所有小于n的prime number. + +#### prime number定义 +- >=2的没有除自己和1以外公约数的数。 +- 还有另外一个定义方法: 这个n,有没有小于n的一个i, 而达到: i * i + # of i = n. 如果有,那就不是 prime + +#### Steps +- 一个boolean长条,存isPrime[]。 然后从i=2, 全部变true. +- hash key: the number itself +- 然后利用这个因子的性质,非prime满足条件: self*self, self * self + self ... etc. +- 所以就check每一个j, j+i, j+i+i, 然后把所有non-prime全部mark成false. +- 最后,数一遍还剩下的true个数就好了 + + + +--- + +**80. [Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Node%20in%20a%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +Given Singlely linked list, 删除一个任意node (不能是head node) + +#### Basic +- update node.val +- Link curr.next to curr.next.next + + + +--- + +**81. [Excel Sheet Column Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Number.java)** Level: Easy Tags: [Math] + + +#### Math +- 26位的运算, 根据10位运算去思考 +- 'A' - 'A' = 0. 所以 char - 'A' + 1 = 题目里的对应数位 +- 或者: 26位运算和10位一样:num += 每位的digit * Math.pow(26, 数位号) + + + + +--- + +**82. [Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Title.java)** Level: Easy Tags: [Math] + + +#### 基本换算 +- 26位 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + + + +--- + +**83. [Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game.java)** Level: Easy Tags: [String] + + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + + + +--- + +**84. [Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20strStr().java)** Level: Easy Tags: [String, Two Pointers] + + +给两个string A, B, 找一个 B 在 A 种的起始位置. + +#### Two Pointer +- 找到B在A中的起始位置, 然后看一下从这个点开始的substring是否等于B就可以了 +- 还挺多坑的, 这些可以帮助优化: +- 1. 当B是“”的时候,也就是能在A的其实位置找到B....index = 0. +- 2. edge condition: 如果 haystack.length() < needle.length() 的话, 必须错, return -1 +- 3. 如果在某个index, A后面剩下的长度, 比B的长度短, 也是误解, return -1 + + + +--- + +**85. [Last Position of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Last%20Position%20of%20Target.java)** Level: Easy Tags: [Binary Search] + + +给一个sorted integer array, 找target出现的最后的index. array 里有重复数字 + +有重复,不是末尾点,继续binary search + + + +--- + +**86. [Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/Length%20of%20Last%20Word.java)** Level: Easy Tags: [String] + + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + + + +--- + +**87. [Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP] + + +https://leetcode.com/problems/longest-continuous-increasing-subsequence/description/ + +O(n)跑2遍for. +O(1)是用了两个int来存:每次到i点时,i点满足条件或不满足条件所有的longestIncreasingContinuousSubsequence. +特点:返跑一回,ans还是继续和left轮的ans作比较;求的所有情况的最大值嘛。 + + + +--- + +**88. [Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + +time: O(n) +space: O(n), O(1) rolling array + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### Sequence DP +- dp[i]: 前i个element,包括 last element (i-1), 可能组成的 subarray 的最大sum. +- init: dp = int[n + 1], dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. That is: need to take curr num, regardless => can drop prev max in dp[i] +- track overall max +- init dp[0] = 0; max = MIN_VALUE 因为有负数 +- Time, space O(n) +- Rolling array, space O(1) + +#### Divide and Conquer, DFS +- 找一个mid piont, 考虑3种情况: 只要左边, 只要右边, cross-mid +- left/rigth 的case, 直接 dfs +- corss-mid case: continuous sum max from left + continous sum max from right + mid +- continuous sum max from one direction: + + +--- + +**89. [Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Median.java)** Level: Easy Tags: [Array, Quick Select, Quick Sort] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) +- quickSelect 可以找到 kth 最小的元素 +- 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- quick select 的template要熟悉一下, 一下子可能想得到, 但写不出来 +- 主要步骤: partition, dfs, only recur on one part of the array + + + + +--- + +**90. [Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Middle%20of%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +找Linked List的中间node + +- 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + + + +--- + +**91. [Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)** Level: Easy Tags: [Design] + + +让一个class 是 singleton + + + +--- + +**92. [Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Linked%20List%20Elements.java)** Level: Easy Tags: [Linked List] + + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + + + +--- + +**93. [Fibonacci.java](https://github.com/awangdev/LintCode/blob/master/Java/Fibonacci.java)** Level: Easy Tags: [DP, Math, Memoization] + + +#### Memoization +- fib[n] = fibonacci(n - 1) + fibonacci(n - 2); + +#### DP array. +- 滚动数组, 简化DP + +#### recursively calculate +- recursively calculate fib(n - 1) + fib(n - 2). 公式没问题, 但是时间太长, timeout. + + + + +--- + +**94. [Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单, 但是要在Linkde List random access坐标, 是很难得: 所以需要把一半 ListNode 翻转 +- reverse linked list: 遍历接开头 +- 用快慢指正找到mid point +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + +#### Previous Note +- Palindrome都是要两边回溯相等 +- linkedlist不能reverse iterating, 那么就reverse the list, 从中间开花作比较。 + + + +--- + +**95. [Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List.java)** Level: Easy Tags: [Linked List] + + +#### Reverse List +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + + + +--- + +**96. [Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Linked%20Lists.java)** Level: Easy Tags: [Linked List] + + +给两个 linked list, 问从哪个node开始, 两个 linked list 开始有重复? + +#### Basics +- 长短list,找重合点 +- 长度不同的话,切掉长的list那个的extra length +- 那么起点一样后,重合点就会同时到达 +- Time O(n) * 2, constant space + + + +--- + +**97. [Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation.java)** Level: Easy Tags: [Hash Table] + + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count occurrance +- 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assum lower case letter. 应该至少是所有ASCII code + + + +--- + +**98. [Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Check Palindrome +- 前后两个指针, 往中间移动, 查看是否字母重合 + +#### 过滤 alphanumeric +- 可以用 ASCII code 来手动过滤, 只要 '0' ~ '9', 'a' ~ 'z', 'A' - 'Z' 之间的 +- 也可以用 regular expression: match 所有这些字母, 是 [a-zA-Z0-9] +- 那凡是不是这些字母的 match, 就是取反: "[^a-zA-Z0-9]". 测试: https://regex101.com/ + + + +--- + +**99. [Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)** Level: Easy Tags: [Design, Stack] + + +如题. + +#### Queue, 倒水 +- 两个Queue,交互倒水 +- 用一个Temp做swap + +##### 做法1 +- 逻辑在push里面: +- 1. x 放q2。 +- 2. q1全部offer/append到q2. +- 3. 用一个Temp做swap q1, q2. +- q1的头,就一直是最后加进去的值. + + +##### 做法2 +- 逻辑在top()/pop()里, 每次换水,查看末尾项. + + + + +--- + +**100. [Implement Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack.java)** Level: Easy Tags: [Stack] + + +随便用一个data structure, implement stack. + +#### Stack, 先入, 后出 +- ArrayList: return/remove ArrayList的末尾项。 +- 2 Queues + + + +--- + +**101. [Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### DFS +- 简单处理swap +- recursively swap children + +#### BFS +- BFS with Queue +- 每次process一个node, swap children; 然后把child加进queue里面 +- 直到queue process完 + + + +--- + +**102. [Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree, 找最深depth + +#### DFS +- 这里要走过所有的node, 所以dfs非常合适 +- Divide and conquer. +- 维持一个最大值: Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; +- 注意check root == null + +#### Note +- BFS is doable as well, but a bit more code to write: tracks largest level we reach + + + +--- + +**103. [Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Depth%20of%20Binary%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +#### BFS +- Shortest path; minimum depth: 想到BFS, check level by level, BFS更能确保更快找到结果 +- depth definition: reach to a leaf node, where node.left == null && node.right == null +- BFS using queue, track level. + + +#### DFS +- Divide and Conquery一个最小值. +- 注意处理Leaf的null: null leaf 出现的时候, 就忽略这个leaf, 直接return算有leaf +- 另一种count的方法: 用Integer.MAX_VALUE代替 null leaf,这样可以避免错误counting. (不能直接recursive) +- 这个无论如何都要走所有node, 所以dfs应该比较适合. + + + + +--- + +**104. [Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**105. [Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + + +检查binary tree是否 identical. + +特点: subtree如果是有旋转的, 只要tree node value相等, 就可以算是identical + +#### DFS +- 在DFS的基础上, 比对左左,左右,右左,右右 + + + +--- + +**106. [Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)** Level: Easy Tags: [DFS, Tree] + + +#### DFS +- 基础binary tree traversal. 注意对null child的判断 + + + +--- + +**107. [Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)** Level: Easy Tags: [DFS, Tree] + + +给一个binary tree s, 和一个binary tree t, 检查t是不是s的subtree. + +#### DFS +- 跟 identical binary tree的写法很像 +- 只有 current s.val = t.val 的时候才需要compare same tree. +- 其他情况, 继续recursively isSubtree +- 注意:即使找到T1 == T2, 但很可能只是数字相同(这里不是binary search tree!!), 而children不同 +- 所以同时要继续recursively isSubtree(T1.left, T2) ...etc. + + + +--- + +**108. [Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Tree] + + +给一个Binary Tree root, 以及两个node A, B. 特点: node里面存了parent pointer. 找 lowest common ancestor + + +#### Hash Set +- 这个题有个奇葩的地方, 每个node还有一个parent, 所以可以自底向上. +- save visited in hashset. 第一个duplicate, 就是A B 的 lowest common ancestor + +#### Save in lists +- 自底向上。利用parent往root方向返回 +- 把所有parent存下来, 然后在两个list里面找最后一个 common node + +#### 注意 +- 无法从root去直接搜target node 而做成两个list. 因为根本不是Binary Search Tree! + + + + +--- + +**109. [Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/Hash%20Function.java)** Level: Easy Tags: [Hash Table] + + +#### Hash Function +- 解释Hash怎么做. +- Hash function例子: +- hashcode("abcd") = (ascii(a) * 33^3 + ascii(b) * 33^2 + ascii(c) *33^1 + ascii(d)*33^0) % HASH_SIZE +- 用到的参数比如: magic number 33, HASH_SIZE. + +- Hash的意义是:给一个string key, 转换成数字,从而把size变得更小。 +- 真实的implementation还要处理collision, 可能需要design hash function 等等。 + + +##### 每一步都%HASH_SIZE的原因 +- hashRst = hashRst * 33 + (int)(key[i]); +- hashRst = hashRst % HASH_SIZE; +- 原因是,hashRst会变得太大,所以不能算完和 再 %... + + + +--- + +**110. [Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Sorted%20Lists.java)** Level: Easy Tags: [Linked List] + + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + + + +--- + +**111. [Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)** Level: Easy Tags: [Array, Bit Manipulation, Math] + + +给一串unique数字, 数字取自 [0 ~ n], 无序, 找第一个skipped的数字. + +#### Swap +- 跟First Missing Positive 非常像, 只有一行代码的区别. +- swap 所有的数字, 到自己的correct position +- 最后一个for loop找到错位的index, 也就是缺的数字. + +#### Bit Manipulation +- XOR will only retain bits that are different 1 ^ 0 = 1, but 0^0, 1^1 == 0 +- Use that feature, 把所有value都和index XOR了 +- 剩下的多余的数字, 其实是那个index无法被XOR消掉, 也就是那个缺的number value. +- 注意: 题目告诉数字是 [0 ~ n], 然而缺一个数字, 那么在[0 ~ n - 1] 里面, 最大的数字(不管缺没缺), 一定是 n = nums.length. + +#### HastSet +- 全存, 找missing +- O(n) space, 不合题意 + +#### sorting +- sort, 找1st missing +- O(n log n) 太慢, 不合题意 + + + +--- + +**112. [Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- Two pointers 其实也可以是一个 for loop pointer, 另一个 dynamic variable. +- track unique index +- skip duplicated items +- O(n) + +#### 思考模式: +- Remove Duplicate from Array 不同于remove from linked list. +- LinkedList里面我们是最好不要动node.val的,直接把node去掉。 +- 而array我们很难直接把node去掉,又不能用新array,那么就要: +- 把不重复的element一个个放到最前面。 +- 这个思想跟merge two sorted array (其中一个后续非常长的array可以放下arr1,arr2) 类似。 +- 就是找个不会事后mess up,不会去动得index,把满足条件的element 填进去。这样保证了in place. +- *反向思维*:remove duplicate, 实际上也是找unique elements, and insert into original array + + + +--- + +**113. [Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List.java)** Level: Easy Tags: [Linked List] + + +从Linked list 里面摘掉重复元素, 只留下unique元素. + +#### Linked List +- sorted list, 重复元素都在一起 +- 知道如何构建Linked List. +- 一点遇到重复元素: node.val == node.next.val, 就去掉. +- 用一个dummy node 来跑路 +- 注意: +- 只有当没有重复的时候, 才node = node.next; +- 有重复的时候, 当后面第三个元素被提上来之后, 还是可能跟当下元素重复, 所以不能前移node. +- ex: A -> A -> A +- while loop 里面check node 和 node.next 比较好, 这样ending position会非常清晰 + + + +--- + +**114. [Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序, 排序以后才能逐个看是否partial string已经存在 +- 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 +- 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: 1. 长 string 排在前; 2. 相等length, 按照dictionary order 排序 +- 全部放入Trie. Trie.insert() +- 针对sorted words array, 从最长的开始找 Trie.startWith. +- 一旦找到, 就是符合题意的, 直接return. +- 注意: startWith 必须每一个node都是 isEnd, 才满足'逐个字母拼出' 的条件. +- Time: build Trie O(mn) + sort:O(nlogn) => O(nlogn) +- Space: O(mn) + +#### +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**115. [Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum.java)** Level: Easy Tags: [DFS, Tree] + + +给一个inputSum, 然后dfs, 找到是否有一条path, 得出的path sum 跟 inputSum 一样. + +#### DFS +- 确定好结尾的条件: is leaf && val == sum +- 每一层减掉node.val, 然后dfs. +- 写一写: root == null => false 对parent nodes的影响. 这里发现没影响, 所以可以简化成用1个functionDFS. + + + + +--- + +**116. [Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)** Level: Easy Tags: [Backtracking, DFS, Tree] + + +给一个inputSum, 然后dfs, 找到所有path, 满足: path sum 跟 inputSum 一样. + +#### DFS, Backtracking +- 用remaining sum 来检测是否满足 input path sum 条件 +- 满足的时候add to result list +- 两种backtracking: +- 1. backtrack 当下node, 加进list, 然后dfs. dfs结束后删掉之前加进去的元素. 非常clean. +- 2. backtrack 下一个dfs level增加的value. dfs return 之后, 删掉list里面的末尾元素: 但是删掉的dfs余下的value. +- 第一种backtrack更加好掌握. + +#### Previous Notes: +- Binary Tree的一个基本题: 找到所有满足条件的path +- 遍历到底,比较sum vs. target +- 注意divide的情况。要把遍历的例子写写 + + + +--- + +**117. [Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)** Level: Easy Tags: [DFS, Double Recursive, Tree] + + +count所有存在的 path sum == target sum. 可以从任意点开始. 但是只能parent -> child . + +#### DFS +- 对所给的input sum 做减法, 知道 sum 达到一个目标值截止 +- 因为可以从任意点开始, 所以当sum达标时候, 需要继续recursive, 从而找到所有情况 (有正负数, sum可能继续增加/减少) +- 经典的 helper dfs recursive + self recursive +- 1. helper dfs recursive 处理包括root的情况 +- 2. self recursive 来引领 skip root的情况. + +#### 特点 +- 与 `Binary Tree Longest Consecutive Sequence II` 在recursive的做法上很相似: +- 利用dfs做包括root的recursive computation +- 利用这个function自己, 做`不包括root的recursive computation` + + + +--- + +**118. [Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20String.java)** Level: Easy Tags: [String] + + +给两个String, 看A rotate之后 能不能变成B + +#### LeetCode +- Basics +- StringBuffer.deleteCharAt(xx), StringBuffer.append(xx) +- O(n) + + +#### LintCode +- Different problem: 给一个char[], 要rotate offset times. +- *三步rotate* +- 有个坑:offset可能很长,那么要%length,才能得到真正需要rotate的部分。 +- Note: rotate 一个 full length之后,是string 不变 + + + +--- + +**119. [Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Prefix.java)** Level: Easy Tags: [String] + + +找一串String里面最长的公共prefix. + +#### Sort, compare string +- Sort O(nlogn) +- first and last string should share common prefix +- 这里假设题目要求的是所有string的公共 prefix, 而不是部分strings + +#### Brutle +- Nested loop, 每一次比较所有string 同位是否相等 +- 相等,append string. 不等,return. +- O(mn) + + + +--- + +**120. [Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20III.java)** Level: Easy Tags: [String] + + +给一个String, 里面的Word被single space split开来, 目的是reverse里面所有的Word, 但preserve Word 和 space order. + +#### Reverse function +- Reverse Words in a String II 的降级版, 去掉第一个overall reverse就好了 + + + +--- + +**121. [Merge Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array%20II.java)** Level: Easy Tags: [Array] + + +如题, merge two sorted array into 新的 sorted array + +- 长度已经固定. Basic Implementation +- 如果一个array足够大, merge into this array, 那么就是从末尾merge. + + + +--- + +**122. [Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/Nth%20to%20Last%20Node%20in%20List.java)** Level: Easy Tags: [Linked List] + + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth + + + +--- + +**123. [Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum.java)** Level: Easy Tags: [Array, Hash Table] + + +#### HashMap +- 相对暴力简洁: 找到一个value, 存一个index +- 若在HashMap里面 match 到结果, 就return HashMap里存的index. +- O(n) space && time. + +#### Sort array, two pointer +- 前后++, --搜索. Sort 用时O(nlogn). +- 1. 第一步 two pointer 找 value. +- 2. 注意,要利用额外的空间保留original array, 用来时候找index. (此处不能用HashMap,因为以value 为key,但value可能重复) +- O(n) space, O(nlogn) time. + + + + +--- + +**124. [Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)** Level: Easy Tags: [Array, DFS] + + +#### DFS +- 虽然Easy, 但用到DFS最基本的想法. +- 1. dive deep +- 2. mark VISITED +- 3. sum it up +- Time: worst O(mn), traverse all possible nodes + +- 更要注意, 要从符合条件value==1的地方开始dfs. +- 对于什么island都没有的情况,area应该位0, 而不是Integer.MIN_VALUE, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**125. [Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的一个subarray的 [start, end] index, 条件: subarary sum == 0. + +#### Hash Table +- `subarray sum equals k` 的简单版: k = 0 +- 求preSum, 然后不断check `map.containsKey(preSum - k)`. +- 如果 `priorSum = preSum - k == 0`, 说明 [priorSum.index + 1, curr index] 就是我们要找的这一段 + +#### Previous notes, same preSum + map solution +- 分析出,如果sum[0~a]=x, 然后sum[0~b]=x, 说明sum[a+1 ~ b] == 0 +- 用hashMap存每个sum[0~i]的值和index i. 如果有重复,就找到了一组sum为0的数组. + + + +--- + +**126. [Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- 就是pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**127. [Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Words.java)** Level: Easy Tags: [Hash Table, String] + + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**128. [Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Characters.java)** Level: Easy Tags: [Array, String] + + +determine if characters are unique in string + +#### HashSet +- space O(n), time O(n) + +#### char[] +- space O(n), time O(nlogn) + +#### no additional data structure +- double for loop: O(n^2) + + + + +--- + +**129. [Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)** Level: Easy Tags: [Bit Manipulation] + +time: O(n), n = # of bits +space: O(1) + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + + + +--- + +**130. [Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array] + +time: O(n) +space: O(1) + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, track start/end +- start/end point, 然后比较大小记录dist +- 注意1: 如果第一个座位没有人, 特殊处理, dist = [0 ~ end] +- 注意2: 如果最后一个座位没有人, 特殊处理: dist = [n - 1 - start]; +- 其余: `dist = Math.max(dist, (end - start) / 2)` +- 相关题目: 几乎同样概念 `Binary Gap`, 升级复杂版`Exam Room` + + + + +--- + +**131. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + +time: O(n) +space: O(n) + +#### DP +- 最多2个fence 颜色相同 +- 假设i是和 i-1不同,那么结果就是 (k-1)*dp[i - 1] +- 假设i是何 i-1相同,那么根据条件,i-1和i-2肯定不同。那么所有的结果就是(k-1)*dp[i-2] +- dp[i]: count # of ways to paint 前i个 fence +- 加法原理 +- time, space: O(n) +- rolling array: space O(1) + +#### Previous Notes +- 这题目很有意思. 一开始分析的太复杂, 最后按照这个哥们的想法(http://yuanhsh.iteye.com/blog/2219891) 的来做,反而简单了许多。 +- 设定T(n)的做法,最后题目化简以后就跟Fibonacci number一样一样的。详细分析如下。 +- 做完,还是觉得如有神。本来是个Easy题,想不到,就是搞不出。 + + + + +--- + +**132. [Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### 理解意思是关键 +- 每天都就交易价格,n天只让买卖一次,那就找个最低价买进,找个最高价卖出 +- 记录每天最小值Min是多少. O(n) +- 每天都算和当下的Min买卖,profit最大多少. + +#### DP +- Find min value for first i items, new dp[n + 1]. +- dp[i]: 前i天, prices最小的price是多少: min cost of first i days +- 然后用当天的price做减法dp[i]算max profit. +- Time, Space: O(n) +- 更进一步, 用一个min来表示min[i], 因为计算中只需要当下的min. + +#### Rolling array +- index i only depend on [i - 2] +- Space O(n) + +#### Brutle Failed +- 每天都试着买进,然后之后的每一天尝试卖出. double for loop, O(n^2). timeout. +- 其中很多都是没必要的计算:[7, 1, 5, 3, 6, 4]。 if we know buyin with 1 is cheapest, we don't need to buyin at 5, 3, 6, 4 later on; we'll only sell on higher prices. + + + +--- + +**133. [Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种其他不同的思路: +- Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +- 如下, 从低谷找peek, sell. +- DP. (old dp solution BuyOn[], SellOn[]) +- DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- O(n) + +#### 找涨幅最大的区间,买卖: +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = 1. 今天买入, 昨天卖掉的dp[i-1][1]结果 - price[i]; 2. 今天不买, 跟昨天买的status dp[i-1][0] 结果 比较. +- `卖`的状态 `dp[i][1]` = 1. 今天卖出, 昨天买进的dp[i-1][0]结果 + price[i]; 2. 今天不卖, 跟昨天卖的status dp[i-1][1] 结果 比较. +- 注意init: +- dp[0][0] = dp[0][1] = 0; // 0 days, +- dp[1][0] = 0; // sell on 1st day, haven't bought, so just 0 profit. +- dp[1][0] = -prices[0]; // buy on 1st day, with cost of prices[0] + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**134. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + +time: O(m) +space: O(1) + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最小值 + +#### DP +- 看到 min value, 至少考虑dp: +- Consider last num: min sum will be (preMinSum + curr, or curr) +- Use preMinSum to cache previouly calcualted min sum, also compare with +curr. +- Have a global min to track: because the preMinSum can be dis-continuous. +- 也可以写成 dp[i] 但是没什么必要 + + + +--- + +**135. [Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)** Level: Easy Tags: [DFS, Divide and Conquer, Tree] + + +#### Tree +- Traverse tree: left, right +- Concept of partial compare vs. whole compare + + + +--- + +**136. [Two Sum IV - Input is a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20IV%20-%20Input%20is%20a%20BST.java)** Level: Easy Tags: [Tree] + + +HashSet to store visited items. Same old 2 sum trick. + + + +--- + +**137. [Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4.java)** Level: Easy Tags: [Enumeration, String] + + +Read4 题目. 理解题目: 是有个input object buff, 会被populated with data. + +#### String in char[] format +- 理解题目: 其实就是track `可以read多少bytes by read4() response` +- 另外一个有用的function `System.arraycopy(src, srcIndex, dest, destIndex, length)` + + + +--- + +**138. [Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素。 +- 注意,从尾部,是大数字优先排末尾的. + + + +--- + +**139. [Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Palindrome%20II.java)** Level: Easy Tags: [String] + + +#### Palindrome String +- delete an index = jump over the index +- 注意 boolean chance 可以用一个helper function + + + +--- + +**140. [Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison + + + +--- + +**141. [Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Move%20Zeroes.java)** Level: Easy Tags: [Array, Two Pointers] + + +Move non-zero elements to front of array; preseve order. + +#### Two Pointers +- Outside pointer that moves in certain condition. +- Save appropirate elements + + + +--- + +**142. [Flood Fill.java](https://github.com/awangdev/LintCode/blob/master/Java/Flood%20Fill.java)** Level: Easy Tags: [DFS] + + +Same as MS Paint + +#### DFS +- track `boolean[][] visited`, validate before dfs + + + +--- + +**143. [Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Diameter%20of%20Binary%20Tree.java)** Level: Easy Tags: [Tree] + + +找longest path (include or not include root) + +跟Binary Tree Maximum Path Sum 的想法一样: 处理single path, 或者combined path (do not include curr root) + +#### Singlepath, combined path +- `int[]{combinedPath, singlePath}`; +- pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; +- complete left/right child, or join curr root: `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`; + + + +--- + +**144. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + + +--- + +**145. [Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/Roman%20to%20Integer.java)** Level: Easy Tags: [Math, String] + + +#### String +- 熟悉罗马字母规则 +- 1. 'I V X L C D M' 分别代表的数字 +- 2. 列举combo的情况,需要从原sum里面减掉多加的部分: 'IV, IX'减2, 'XL, XC'减20, 'CD, CM'减200. +- Leading `I(1*2)`, `X(10*2)`, `C(100*2)` causes double counting + +https://en.wikipedia.org/wiki/Roman_numerals + + + +--- + +**146. [Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +- 方法1: 用到hashset找unique && duplicate: O(m+n) +- 方法2: 可以用binary search 找数字. Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**147. [Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math] + + +根据题意枚举, 再根据规则basic implementation + +#### Alter input + +#### HashTable + Two Pointer + + + +--- + +**148. [Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +剥皮过程。解铃还须系铃人 +左边的外皮'{['在stack底部 +右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**149. [First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +方法1: 按照题意, 找到第一个 first index == last index的字母. + +方法2: 用hashmap存字母的index, 有些重复字母的index就会是个list. 找到单一index, 结合成list, sort, return list.get(0) + + + +--- + +**150. [Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Binary.java)** Level: Easy Tags: [Math, String, Two Pointers] + + +#### Two pointers +- Use two pointers i, j to track the 2 strings +- Add when i and j are applicable. While (i >= 0 || j >= 0) +- StringBuffer.insert(0, x); +- handle carry + +#### wrong: convert to int +- 土办法没技术,把binary换成数字,加起来,再换成binary +- 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**151. [Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Isomorphic%20Strings.java)** Level: Easy Tags: [Hash Table] + + +#### HashMap +- two failture cases: +- same key, value not matching +- two key maps to same value + +#### Previous note +1. Match. 就是map.containsKey, map.containsValue, and char1 == char2. Perfect. +2. Either Key not exist, or Value not exit. False; +3. Both key and Value exist, but map.get(char1) != char2. Miss-match. False. +4. None of Key or Value exist in HashMap. Then add the match. + + + +--- + +**152. [Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack? + + + +--- + diff --git a/review/level/Hard.md b/review/level/Hard.md new file mode 100644 index 0000000..7d40062 --- /dev/null +++ b/review/level/Hard.md @@ -0,0 +1,2116 @@ + + + +## Hard (91) +**0. [Count of Smaller Number before itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number%20before%20itself.java)** Level: Hard Tags: [] + +与Count of Smaller Number非常类似。以实际的value来构成segment tree,leaf上存(count of smaller number)。 + +Trick: 先Query,再modify. +每次Query时候,A[i]都还没有加入到Segment Tree 里面,而A[i+1,...etc]自然也还没有加进去。 +那么就自然是coutning smaller number before itself. +刁钻啊! + +另外注意: +在modify里面:多Check了root.start <= index 和 index <= root.end。 过去都忽略了。以后可以把这个也写上。 +(其实是Make sense的,就是更加严格地check了index再 root.left 或者 root.right里面的站位) + + + +--- + +**1. [Kth Smallest Sum In Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Sum%20In%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [] + + +用priority queue. 每次把最小的展开,移位。分别x+1,或者y+1: +因为当下的Min里面x,y都是最小的。所以下一个最小的不是(x+1,y),就是(x,y+1)。 + +每次就poll()一个,放2个新candidate进去就好了。 +注意,这样的做法会用重复,比如例子(7,4)会出现两次。用一个HashSet挡一下。 + +注意,HashSet的唯一性,用一个"x,y"的string就可以代为解决。 + + + +--- + +**2. [LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)** Level: Hard Tags: [Design, Hash Table] + + +#### Hash Table +- 具体看thoughts, 几种不同的方式使用map +- `regular object map`: map of , where `item : {int val; int count}` +- Use a Map to track the frequency +- Track constant capacity, and minimum frequency +- Every get(): update all frequency map as well +- Every put(): update all frequency map as well, with optional removal (if over capacity) + +- Original post: http://www.cnblogs.com/grandyang/p/6258459.html +- TODO: one doubly linked list might be good enough to replace below: +- `frequency list map`: map of >, where the list preserves `recency` +- `item location in frequency map`: map of : +- index relative to the item in a particular list, not tracking which list here + + + +--- + +**3. [Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Prefix%20and%20Suffix%20Search.java)** Level: Hard Tags: [Trie] + + + + +--- + +**4. [Remove Node in Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Node%20in%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST] + + +方法1: Brutle一点。找到target和target的parent. +把target remove时,把target的children nodes 重新排列组成新的BST: inorder traversal, build tree based on inorder traversal list. + +方法2: 分析规律,先找到target和parent, 然后根据性质,把target remove时,移动children nodes, 保证还是BST。 + + + +--- + +**5. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + + +--- + +**6. [k Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/k%20Sum.java)** Level: Hard Tags: [DP] + + +DP. 公式如何想到, 还需要重新理解. + +dp[i][j][m]: # of possibilities such that from j elements, pick m elements and sum up to i. +i: [0~target] + +dp[i][j][m] = dp[i][j-1][m] + dp[i - A[j - 1]][j-1][m-1] + (i not included) (i included) + + + +--- + +**7. [Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)** Level: Hard Tags: [Binary Search, DP, Partition DP] + + +给一串书pages[i], k个人, pages[i] 代表每本书的页数. k个人从不同的点同时开始抄书. + +问, 最快什么时候可以抄完? + +#### Partition DP +- 第一步, 理解题目要求的问题: 前k个人copy完n本书, 找到最少的用时; 也可以翻译成: `n本书, 让k个人来copy, 也就是分割成k段`. +- 最后需要求出 dp[n][k]. 开: int[n+1][k+1]. +- 原理: +- 1. 考虑最后一步: 在[0 ~ n - 1]本书里, 最后一个人可以选择copy 1 本, 2 本....n本, 每一种切割的方法的结果都不一样 +- 2. 讨论第k个人的情况, 在 j = [0 ~ i] 循环. 而循环j时候最慢的情况决定 第k个人的结果(木桶原理): `Math.max(dp[j][k - 1], sum)`. +- 3. 其中: `dp[j][k-1]` 是 [k-1]个人读完j本书的结果, 也就是著名的`上一步`. 这里循环考虑的是第k个人不同的j种上一步 : ) +- 4. 循环的结果, 是要存在 dp[i][k] = Math.min(Math.max(dp[j][k - 1], sum[j, i]), loop over i, k, j = [i ~ 0]) +- Time: O(kn^2), space O(nk) + +##### Init +- Init: dp[0][0] = 0, 0个人0本书 +- Integer.MAX_VALUE的运用: +- 当 i = 1, k = 1, 表达式: dp[i][k] = Math.min(dp[i][k], Math.max(dp[j][k - 1], sum)); +- 唯一可行的情况就只有一种: i=0, k=0, 刚好 0 个人 copy 0 本书, dp[0][0] = 0. +- 其他情况, i = 1, k = 0, 0 个人读 1本书, 不可能发生: 所以用Integer.MAX_VALUE来冲破 Math.max, 维持荒谬值. +- 当 i=0, k=0 的情况被讨论时候, 上面的方程式才会按照实际情况计算出 dp[i][k] +- 这道题的init是非常重要而tricky的 + +##### 计算顺序 +- k个人, 需要一个for loop; +- k个人, 从copy1本书开始, 2, 3, ... n-1,所以 i=[1, n], 需要第二个for loop +- 在每一个i上, 切割的方式可以有[0 ~ i] 中, 我们要计算每一种的worst time + +##### 滚动数组 +- [k] 只有和 [k - 1] 相关 +- Space: O(n) + +#### Binary Search +- 根据: 每个人花的多少时间(time)来做binary search: 每个人花多久时间, 可以在K个人之内, 用最少的时间完成? +- time variable的范围不是index, 也不是page大小. 而是[minPage, pageSum] +- validation 的时候注意3种情况: 人够用 k>=0, 人不够所以结尾减成k<0, 还有一种是time(每个人最多花的时间)小于当下的页面, return -1 +- O(nLogM). n = pages.length; m = sum of pages. + + + + +--- + +**8. [Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)** Level: Hard Tags: [DP, Interval DP, String] + + +- 给两个string S, T. 检验他们是不是scramble string. +- scramble string 定义: string可以被分拆成binary tree的形式, 也就是切割成substring; +- 旋转了不是leaf的node之后, 形成新的substring, 这就是原来string的 scramble. + + +#### Interval DP 区间型 +- 降维打击, 分割, 按照长度来dp. +- dp[i][j][k]: 数组S从index i 开始, T从index j 开始, 长度为k的子串, 是否为scramble string + +##### Break down +- 一切两半以后, 看两种情况: , 或者不rotate这两半. 对于这些substring, 各自验证他们是否scramble. +- 不rotate分割的两半: S[part1] 对应 T[part1] && S[part2] 对应 T[part2]. +- rotate分割的两半: S[part1] 对应 T[part2] && S[part2] 对应 T[part1]. + +##### Initialization +- len == 1的时候, 其实无法旋转, 也就是看S,T的相对应的index是否字符相等. +- initialization非常非常重要. 很神奇, 这个initailization 打好了DP的基础, 后面一蹴而就, 用数学表达式就算出了结果. +- input s1, s2 在整个题目的主要内容里面, 几乎没有用到, 只是用在initialization时候. +- More details, 看解答 + + + +--- + +**9. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**10. [Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + +time: O(MN) +Space: O(N) + +两个字符串, A要变成B, 可以 insert/delete/replace, 找最小变化operation count + +#### Double Sequence +- 考虑两个字符串的末尾index s[i], t[j]: 如果需要让这两个字符一样, 可能使用题目给出的三种operation: insert/delete/replace? +- 先calculate最坏的情况, 3种operation count + 1; 然后在比较match的情况. +- 注意, 在i或者j为0的时候, 变成另外一个数字的steps只能是全变. +- 第一步, 空间时间都是O(MN), O(MN) +- 滚动数组优化, 空间O(N) + +##### Detail analysis +- insert: assume insert on s, `#ofOperation = (s[0 ~ i] to t[0 ~ j-1]) + 1;` +- delete: assume delete on t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j]) + 1;` +- replace: replace both s and t, `#ofOperatoin = (s[0 ~ i - 1] to t[0 ~ j - 1]) + 1;` +- dp[i][j]代表了两个 sequence 互相之间的性质: s[0 ~ i] 转换成 s[0~j] 所需要的最少 operation count +- init: 当i==0, dp[0][j] = j; 每次都要 + j 个character; 同理, 当j==0, dp[i][0] = i; +- 而dp[i][j]有两种情况处理: `s[i] == t[j]` or `s[i] != t[j]` + +##### 何时initialize +- 这种判断取决于经验: 如果知道initialization可以再 double for loop 里面一起做, 那么可以留着那么做 +- 这样属于 `需要什么, initialize什么` +- 事后在做space optimization的时候, 可以轻易在 1st dimension 上做rolling array + +#### Search +- 可以做, 但是不建议:这道题需要找 min count, 而不是search/find all solutions, 所以search会写的比较复杂, 牛刀杀鸡. + + + +--- + +**11. [Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)** Level: Hard Tags: [DP, String] + + +Double Sequence DP: +0. DP size (n+1): 找前nth的结果, 那么dp array就需要开n+1, 因为结尾要return dp[n][m] +1. 在for loop 里面initialize dp[0][j] dp[i][0] +2. Rolling array 优化成O(N): 如果dp[i][j]在for loop里面, 就很好替换 curr/prev + + + +--- + +**12. [Ones and Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Ones%20and%20Zeroes.java)** Level: Hard Tags: [DP] + + +还是Double Sequence, 但是考虑第三种状态: 给的string array的用量. +所以开了3维数组. + +如果用滚动数组优化空间, 需要把要滚动的那个for loop放在最外面, 而不是最里面. +当然, 这个第三位define在 dp[][][]的哪个位置, 问题都不大. + +另外, 注意在外面calcualte zeros and ones, 节约时间复杂度. + + + +--- + +**13. [Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary + +利用 memoization: `Map>` + +#### DFS + Memoization +- Realize the input s expands into a tree of possible prefixes. +- We can do top->bottom(add candidate+backtracking) OR bottom->top(find list of candidates from subproblem, and cross-match) +- DFS on string: find a valid word, dfs on the suffix. [NO backtraking in the solution] +- DFS returns List: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- IMPORANT: Memoization: `Map>`, which reduces repeated calculation if the substring has been tried. +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Regular DPs +- 两个DP一起用, 解决了timeout的问题: when a invalid case 'aaaaaaaaa' occurs, isValid[] stops dfs from occuring +- 1. isWord[i][j], subString(i,j)是否存在dict中? +- 2. 用isWord加快 isValid[i]: [i ~ end]是否可以从dict中找到合理的解? +- 从末尾开始查看i:因为我们需要测试isWord[i][j]时候,j>i, 而我们观察的是[i,j]这区间; +- j>i的部分同样需要考虑,我们还需要知道isValid[0~j+1]。 所以isValid[x]这次是表示[x, end]是否valid的DP。 +- i 从 末尾到0, 可能是因为考虑到isWord[i][j]都是在[0~n]之内,所以倒过来数,坐标比较容易搞清楚。 +- (回头看Word Break I, 也有坐标反转的做法) +- 3. dfs 利用 isValid 和isWord做普通的DFS。 + +#### Timeout Note +- Regarding regular solution: 如果不做memoization或者dp, 'aaaaa....aaa' will repeatedly calculate same substring +- Regarding double DP solution: 在Word Break里面用了set.contains(...), 在isValid里面,i 从0开始. 但是, contains()本身是O(n); intead,用一个isWord[i][j],就O(1)判断了i~j是不是存在dictionary + + + +--- + +**14. [Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, String, Two Pointers] + + +基本思想: 用个char[]存string的frequency. 然后2pointer, end走到底, 不断validate. +符合的就process as result candidate. + +HashMap的做法比char[]写起来要复杂一点, 但是更generic + + + +--- + +**15. [Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String] + + +大清洗 O(nk) +map.size一旦>k,要把longest string最开头(marked by pointer:start)的那个char抹掉 +一旦某一个char要被清除,所以在这个char 的1st and last appearance之间的char都要被清洗from map + + + + +--- + +**16. [Find Minimum in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Hard Tags: [Array, Binary Search] + + +一个需要严谨思考的题目. 因为有duplicate, 会导致不断平移, 所以最终time complexity是O(n) +所以不如直接扫一遍, 给出答案. + +但是还是写一个Binary Search的样子, 只不过worst结果是O(n) + + + +--- + +**17. [Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands%20II.java)** Level: Hard Tags: [Union Find] + + +给一个island grid[][], and list of operations to fill a particualr (x,y) position. + +count # of remaining island after each operation. + +#### Union Find, model with int[] +- 把board转换成1D array, 就可以用union-find来判断了. +- 用int[] father 的unionFind, 需要转换2D position into 1D index. 这样比较clean +- 判断时,是在四个方向各走一步,判断是否是同一个Land. +- 每走一次operator,都会count++. 若发现是同一个island, count-- +- count的加减, 都放在了UnionFind自己的function里面, 方便tracking, 给几个helper function就对了. +- Time: O(k * log(mn)) + +#### Union Find, model with Hashmap +- 用HashMap的Union-find. + + +#### Note: +- Proof of UnionFind log(n) time: https://en.wikipedia.org/wiki/Proof_of_O(log*n)_time_complexity_of_union%E2%80%93find + + + +--- + +**18. [Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)** Level: Hard Tags: [Backtracking, DFS, Trie] + + +给一串words, 还有一个2D character matrix. 找到所有可以形成的words. 条件: 2D matrix 只可以相邻走位. + +#### Trie, DFS +- 相比之前的implementation, 有一些地方可以优化: +- 1. Backtracking时候, 在board[][] 上面mark就可以, 不需要开一个visited[][] +- 2. 不需要implement trie的所有方程, 用不到: 这里只需要insert. +- 普通的trie题目会让你search a word, 但是这里是用一个board, 看board的每一个字母能不能走出个Word. +- 也就是: 这里的search是自己手动写, 不是传统的trie search() funcombination +- 3. TrieNode里面存在 end的时候存string word, 表示到底. 用完了 word = null, 刚好截断重复查找的问题. + +##### 关于Trie +- Build Trie with target words: insert, search, startWith. Sometimes, just: `buildTree(words)` and return root. +- 依然要对board matrix做DFS。 +- no for loop on words. 直接对board DFS: +- 每一层,都会有个up-to-this-point的string. 在Trie里面check它是不是存在。以此判断。 +- 若不存在,就不必继续DFS下去了。 +- Trie solution time complexity, much better: +- build Trie: n * wordMaxLength +- search: boardWidth * boardHeight * (4^wordMaxLength + wordMaxLength[Trie Search]) + + +#### Regular DFS +- for loop on words: inside, do board DFS based on each word. +- Time cpmplexity: word[].length * boardWidth * boardHeight * (4^wordMaxLength) + +#### Previous Notes +- Big improvement: use boolean visited on TrieNode! +- 不要用rst.contains(...), 因为这个是O(n) 在leetcode还是会timeout(lintcode竟然可以pass)! +- 在Trie search() method 里面,凡是visit过的,mark一下。 + + + + +--- + +**19. [Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)** Level: Hard Tags: [Backtracking, Trie] + + +可以开Trie class, 里面用到TrieNode. 开Trie(words) 可以直接initalize with for loop +TrieNode 里面可以有一个 List startWith: 记录可以到达这个点的所有string: 有点像树形, ancestor形状的存储. + +神操作: +根据square的性质, 如果选中了list of words, 设定 int prefixIndex = list.size(). +取出list里面的所有word[prefixedIndex], 并且加在一起, 就是下一个word candidate的 prefix. + +形象一点: +list = ["ball", "area"]; +prefixIndex = list.size(); ball[prefixIndex] = 'l'; area[prefixIndex] = 'e'; +//then +candidatePrefix = ball[prefixIndex] + area[prefixIndex] = "le"; +这里就可以用到Trie的那个 findByPrefix function, 在每个点, 都存有所有这个点能产生的candidate. +这时, 试一试所有candidate: dfs + +能想到这种倒转的结构来存prefix candidates 在 Trie里面, 这个想法非常值得思考. + + + +--- + +**20. [Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. +#### 方法1 +Array, 维持一个左手最高墙array, 右手最高强array. +对于每个index而言, vertically 能存放的最大水柱, 就是靠左右最高墙决定的: +min(leftHighestWall, rightHighestWall) - currHeight. + +#### 方法2 +方法1上面的优化, two pointer, 还是找左边最高和右边最高. O(1) space. +利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +左边按照maxLeft来计算, 右边按照maxRight来计算. + +#### 方法3 +2 Pointers, 双面夹击: +1. 找中间最高bar的index +2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条。 +3. 每次还要减去block自身的height + +#### 方法4 +主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. + + + + +--- + +**21. [Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)** Level: Hard Tags: [Array, Monotonous Stack, Stack] + + +给n个bar,组成柱状图histogram. 求在这一排柱状图里面可以找到的面积最大的长方形. + +思考: 找长方形面积, 无非是找两个index, 然后底边长度 * height. + +#### Monotonous Stack +- 重点是根据找Histogram里面rectangle的性质, 维持一个单调递增的Stack +- 在loop over indexes的时候: +- 如果高度>= previous peek(), 那么对于那个peek, 就意味着, 往下走, 一直走高嘛, 之前的peek总可以继续抄底 +- 什么时候不能抄底了呢? 就是有一个下降趋势的时候 +- 这时候并不是calculate所有前面的peek, 而是考虑 大于 current height的之前所有的peek. +- 把这些peek到 current height 前一格的rectangle全部找出来: stack.pop() +- 这个stack.pop()的过程里面, 其实没有算上 current height, 因为需要留到下一轮, 把current index加进stack 再说 +- 为什么用stack? 因为需要知道连续递增的peek, stack.peek() O(1), 好用 + 而其实不用stack, 也可以用其他方式记录所有height, 只不过要 O(n)去找peek不方便 + +#### 知识点 +- 理解monotonous stack 是如何被维护的 +- 维护monotonous stack 是题目需要, 而不是stack本身性质, 是一种借助 stack.peek() O(1)的巧妙用法. + + + + +--- + +**22. [Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)** Level: Hard Tags: [Binary Search, DFS, Divide and Conquer] + + +2Dmatrix, 里面的value有一些递增, 递减的特点(细节比较长, 看原题). 目标是找到peak element + +peak: 比周围4个方向的点value大 + +#### DFS + +##### 基本原理 +- 我们不可能一口气准确定位(x,y), 但是我们可以再一个row/col里面, 找到1D array的 peak. +- 根据这个点, 再往剩下两个方向移动 +- 1. 在中间的一行i=midX, 找到peak所在的y. +- 2. 在中间的一列j=midY, 找到peak所在的x. (有可能强势override之前找到的y, 也就是放弃那一行的peak, 在midY上找peak) +- 3. 根据 (x,y) 的4个neighbor check (x,y)是不是 peak, 如果不是, 像更高的位置移动一格 +- 4. 根据之前算的 midX, midY 把board分成4个象限, 在每一份里面再继续找 +- 这个题目LintCode不给做了, 所以思路对的, 但是解答还没有再次验证. + +##### 剪枝/切分象限 +- 每次只是找到一个row/col里面的peak而已! +- 找到这个点, 就等于把board切成了两半. +- 然后, 再跟剩下的相邻的两个位置比较, 就知道了哪里更大, 就去哪里找peak, 也就是又切了第二刀. +- 切第二刀的时候, 也要把(x, y) 移到需要取的象限. 进行DFS +- 根据mid row 切割: +- http://www.jiuzhang.com/solution/find-peak-element-ii/#tag-highlight-lang-java +- http://courses.csail.mit.edu/6.006/spring11/lectures/lec02.pdf + +##### 时间复杂度 +- 每一个level都减一半 +- T(n) = n + T(n/2) = n + n/2 + n/4 + ... + 1 = n(1 + 1/2 + .... + 1/n) = 2n = O(n) + +#### Binary Search +- TODO +- O(nLogN) + + + +--- + +**23. [Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)** Level: Hard Tags: [Hash Table, String, Trie] + + +Obvious的做法是全部试一遍, 判断, 变成 O(n^2) * O(m) = O(mn^2). O(m): isPalindrome() time. + +当然不行了, 那就看是O(nlogN), 还是O(n)? + +#### 方法1: Hash Table + Palindrome的性质. 复合型. +O(mn) + +##### 思路 +- 每一个word, 都可以拆分成 front + mid + end. 如果这个word + 其他word可以组成palindrome,那就是说 +- 砍掉 (mid+end), front.reverse() 应该存在 words[] 里面. +- 砍掉 (front+mid), end.reverse() 应该存在 words[] 里面. +- 我们用HashMap存所有的, 然后reverse, 找配对就好. + +##### Corner case +- 如果有 empty string "", 那么它跟任何一个palindrome word, 都可以配对, 并且根据位置前后变换, 凑成2份 distinct indexes. +- 这样就有了那个 `if (reverseEnd.equals("")) {...}` 的logic. +- 注意: 虽然在处理砍头/砍尾的两个 for loop里面都在根据 empty string 重复记录, + 但因为 "" 自己本身不能作为起点, 所以overall只会在被其他palindrome配对时记录一次. + + +#### 方法2: Trie +还要做一下那. + + + +--- + +**24. [Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)** Level: Hard Tags: [Array, DP, Hash Table, Stack] + + +#### 方法1: monotonous stack +分解开来, 其实是'Largest Rectangle in Histogram', 只不过这里要自己model heights. +一个2D array里面的rectangle, 最终也是用height * width做出来的. +巧妙在于, 把每一行当做底边, 算出这个底边, 到顶部的height: +- 如果底边上的一个value==0, 那么算作没有height(以这个底边做rectangle, value==0的位置是空中楼阁, 不能用) +- 如果底边上的value==1, 那么就把上面的height加下来, 做成histogram + +如果看具体实例, 有些row似乎是白算的, 但是没有办法, 这是一个搜索的过程, 最终会比较出最优解. + +#### 方法2: DP +Coordinate DP? + + + +--- + +**25. [Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)** Level: Hard Tags: [Coordinate DP, DFS, DP, Memoization, Topological Sort] + + +m x n 的matrix, 找最长增序的序列长度. 这里默认连续的序列. + +- 接成圈是不行的, 所以visit过得 (x,y)就不能再去了. +- 斜角方向不能走, 只能走上下左右 +- 无法按照坐标DP来做, 因为计算顺序4个方向都可以走. +- 最终要visit所有node, 所以用DFS搜索比较合适. + +#### DFS, Memoization +- 简单版: longest path, only allow right/down direction: +- `dp[x][y] = Math.max(dp[prevUpX][prevUpY], or dp[prevUpX][prevUpY] + 1)`; and compare the other direction as well +- This problem, just compare the direction from dfs result +- DFS太多重复计算; memoization (dp[][], visited[][]) 省去了重复计算 +- initialize dp[x][y] = 1, (x,y) 自己也算path里的一格 +- dfs(matrix, x, y): 每次检查(x,y)的4个neighbor (nx, ny), 如果他们到(x,y)是递增, 那么就考虑和比较: +- Maht.max(dp[x][y], dp[nx][ny] + 1); where dp[n][ny] = dfs(matrix, nx, ny) +- top level: O(mn), 尝试从每一个 (x,y) 出发 +- O(m * n * k), where k is the longest path + +#### Topological sort +还没有做 + + + +--- + +**26. [Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)** Level: Hard Tags: [Array, DP, Game Theory, Interval DP, Memoization] + + +LeetCode: Predict the Winner + +还是2个人拿n个coin, coin可以有不同的value. + +只不过这次选手可以从任意的一头拿, 而不限制从一头拿. 算先手会不会赢? + +#### Memoization + Search +- 跟Coins in a Line II 一样, MaxiMin的思想: 找到我的劣势中的最大值 +- `dp[i][j] 代表在[i,j]区间上 选手最多能取的value 总和` +- 同样, sum[i][j]表示[i] 到 [j]间的value总和 +- 对手的最差情况, 也就是先手的最好情况: +- dp[i][j] = sum[i][j] - Math.min(dp[i][j - 1], dp[i + 1][j]); +- 这里需要search, 画出tree可以看明白是如何根据取前后而分段的. + +#### 博弈 + 区间DP, Interval DP +- 因为是看区间[i,j]的情况, 所以可以想到是区间 DP +- 这个方法需要复习, 跟数学表达式的推断相关联: S(x) = - S(y) + m. 参考下面的公式推导. +- dp[i][j]表示 从index(i) 到 index(j), 先手可以拿到的最大值与对手的数字差. 也就是S(x). +- 其中一个S(x) = dp[i][j] = a[i] - dp[i + 1][j] +- m 取在开头, m 取在末尾的两种情况: +- dp[i][j] = max{a[i] - dp[i + 1][j], a[j] - dp[i][j - 1]} +- len = 1, 积分就是values[i] +- 最后判断 dp[0][n] >= 0, 最大数字和之差大于0, 就赢. +- 时间/空间 O(n^2) + +##### 公式推导 +- S(x) = X - Y, 找最大数字和之差, 这里X和Y是选手X的总分, 选手Y的总分. +- 对于选手X而言: 如果S(x)最大值大于0, 就是赢了; 如果最大值都小于0, 就一定是输了. +- 选手Y: S(y)来表示 对于Y, 最大数字和之差. S(y) = Y - X +- 根据S(x) 来看, 如果从 数字和X里面, 拿出一个数字 m, 也就是 X = m + Xwithout(m) +- S(x) = m + Xwithout(m) - Y = m + (Xwithout(m) - Y). +- 如果我们从全局里面索性去掉m, 那么 S(y'') = Y - Xwithout(m) +- 那么推算下来: S(x) = m + (Xwithout(m) - Y) = m - (Y - Xwithout(m)) = m - S(y'') +- 在这个问题里面, 我们model X 和 Y的时候, 其实都是 dp[i][j], 而区别在于先手/后手. +- 将公式套用, 某一个S(x) = a[i] - dp[i + 1][j], 也就是m=a[i], 而 S(y'') = dp[i + 1][j] + +##### 注意 +- 如果考虑计算先手[i, j]之间的最大值, 然后可能还需要两个数组, 最后用于比较先手和opponent的得分大小 => 那么就要多开维. +- 我们这里考虑的数字差, 刚好让人不需要计算先手的得分总值, 非常巧妙. +- Trick: 利用差值公式, 推导有点难想到. + +##### 区间型动态规划 +- 找出[i, j]区间内的性质: dp[i][j]下标表示区间范围 [i, j] +- 子问题: 砍头, 砍尾, 砍头砍尾 +- loop应该基于区间的length +- template: 考虑len = 1, len = 2; 设定i的时候一定是 i <= n - len; 设定j的时候, j = len + i - 1; + + + + +--- + +**27. [Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)** Level: Hard Tags: [DP, Divide and Conquer, Interval DP, Memoization] + + +一排球, 每个球有value, 每次扎破一个, 就会积分: 左*中间*右 的值. 求, 怎么扎, 最大值? + +TODO: Need more thoughts on why using dp[n + 2][n + 2] for memoization, but dp[n][n] for interval DP. + +#### Interval DP +- 因为数组规律会变, 所以很难找'第一个burst的球'. 反之, 想哪一个是最后burst? +- 最后burst的那个变成一堵墙: 分开两边, 分开考虑, 加法原理; 最后再把中间的加上. +- dp[i][j] represent max value on range [i, j) +- Need to calculate dp[i][j] incrementally, starting from range size == 3 ---> n +- Use k to divide the range [i, j) and conquer each side. + +##### Interval DP 三把斧: +- 中间劈开 +- 砍断首或尾 +- Range区间作为iteration的根本 + +##### Print the calculation process +- use pi[i][j] and print recursively. +- Print k, using pi[i][j]: max value taken at k + +#### Memoization +- 其实会做之后挺好想的一个DP +- dp[i][j] = balloons i~j 之间的 max. +- 然后找哪个点开始burst? 设为x。 +- For loop 所有的点作为x, 去burst。 +- 每次burst都切成了三份:左边可以recusive 求左边剩下的部分的最大值 + 中间3项相乘 + 右边递归下去求最大值。 +- Note: 这个是Memoization, 而不纯是DP +- 因为recursive了,其实还是搜索,但是memorize了求过的值,节省了Processing + + + + +--- + +**28. [K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, Trie] + + +给一串String, target string, int k. 找string array里面所有的candidate: 变化K次, 能变成target. + +#### Trie +TODO + +#### Double Sequence DP +- Edit Distance的follow up. +- 其实就是改一下 minEditDistance的function, 带入K作比较罢了. +- 写起来跟Edit Distance 的主要逻辑是一模一样的. +- 但是LintCode 86% test case 时候timeout. +- Time O(mnh), where h = words.length, 如果 n ~ m, Time 就几乎是 O(n^2), 太慢. + + + +--- + +**29. [Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + +time: O(NK^2): +space: (NK) + +一排n个房子, 每个房子可涂成k种颜色, 涂每个房子的价钱不一样, 用costs[][]表示. + +costs[0][1]表示涂了index是0的房子, 用了color 1. + +规则: 相邻的两个房子不能使同一种颜色 + +求: 最少的cost + +#### DP +- 跟Paint House I 几乎一模一样, 只不过paint color更多了: k colors. +- 先考虑单纯地用dp[i]表示涂前 i 个房子的最小cost +- 但是 dp[i] 和 dp[i-1] 两个index选什么颜色会互相影响, 难讨论, 于是加状态: 序列DP被加了状态变成2D. +- 考虑最后位, 而前一位i-1又被i位的颜色限制, 于是在考虑 min dp[i] 时候, 又多了一层iteration. +- 做dp[i][j]: # cost for 前 i 个房子, 所以要先pick (i-1) 房子的cost, 然后在找出 (i-2)房子的cost +- K种颜色 => O(NK^2) +- 如果不优化, 跟Paint House I 几乎是一模一样的代码 +- Time O(NK^2), space(NK) +- Rolling array: reduce space to O(K) + +#### 注意 +- 序列型dp[i]表示'前i-1个'的结果. 所以dp最好设定为 int[n + 1] size. +- 然而, 颜色在这里是状态, 所以保留在 j: [ 0~k) +- [[8]] 这样的edge case. 跑不进for loop, 所以特殊handle. + +#### Optimization Solution +- Time: O(NK) +- 如果已知每次都要从cost里面选两个不同的最小cost,那么先把最小两个挑出来, 就不必有第三个for loop 找 min +- 每次在数列里面找: 除去自己之外的最小值, 利用最小值/次小值的思想 +- 维持2个最值: 最小值/次小值. +- 计算的时候, 如果除掉的不是最小值的index, 就给出最小值; 如果除掉的是最小值的index, 就给出次小值. +- Every loop: 1. calculate the two min vlaues for each i; 2. calcualte dp[i][j] +- 如何想到优化: 把表达式写出来, 然后看哪里可以优化 +- 另外, 还是可以rolling array, reduce space complexity to O(K) + + + +--- + +**30. [Best Time to Buy and Sell Stock III.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20III.java)** Level: Hard Tags: [Array, DP, Sequence DP] + + +比stock II 多了一个限制:只有2次卖出机会. + +#### DP加状态 +- 只卖2次, 把买卖分割成5个状态模块. +- 在状态index 0, 2, 4: 没有持有股票. 1. 一直在此状态, max profit不变; 2. 刚卖掉, dp[i][前状态] + profit +- 在状态index 1, 3: 持有股票. 1. 一直在此状态, daily profit. 2. 刚刚买进, 状态改变, 但是没有profit yet: dp[i][前状态] + +##### Partial profit +- 把每天的partial profit (diff)加在一起, 最终的overall profit是一样的. 唯一更好的是, 不需要记录中间买入的时间点. +- 什么时候会积累profit呢? +- 1. 原本就持有股票的, 如果毫无动作, 那么状态不变, 积累profit diff. +- 2. 卖出了股票, 状态改变, 积累profit diff. +- 注意: 只有在状态index: 0, 2, 4, 也就是卖掉股票的时候, 才可以积累profit + +##### Rolling Array +- [i] 只有和 [i-1] 打交道, reduce space +- O(1) space, O(n) time + +#### 找峰头 +- 找峰头;然后往下再找一个峰头。 +- 怎么样在才能Optimize两次巅峰呢?从两边同时开始找Max!(棒棒的想法) +- leftProfit是从左往右,每个i点上的最大Profit。 +- rightProfit是从i点开始到结尾,每个点上的最大profit. +- 那么在i点上,就是leftProfit,和右边rightProfit的分割点。在i点,leftProfit+rightProfit相加,找最大值。 +- 三个O(n),还是O(n) + + + +--- + +**31. [Best Time to Buy and Sell Stock IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20IV.java)** Level: Hard Tags: [DP, Sequence DP] + + +有int[] price of stock, 最多做 k transactions. 求最大profit. + +#### DP +- 根据StockIII, 不难发现StockIV就是把状态划分为2k+1份. 那么同样的代码, 移植. + +##### 注意1: +- 如果k很大, k>n/2, 那么长度为n的数组里面, 最多也只能n/2个transaction +- 那么题目简化为stockII, 给n数组, 无限次transaction. +- 注意, status的数量是 2k+1 +- Time O(NK), Space O(2k+1) to store the status + +##### 注意2: +- 最后状态是'没有stock'的都该考虑, 做一个 for 循环比较max. +- 当然, 来一个profit variable, 不断比较, 也是可以的. + +#### 方法2 +- (previous notes, 熟练第一种方法的思考就可以) +- 记得要理解:为什么 i-1天的卖了又买,可以和第 i 天的卖合成一次交易? +- 因为每天交易的price是定的。所以卖了又买,等于没卖!这就是可以合并的原因。要对价格敏感啊少年。 +- Inspired from here: http://liangjiabin.com/blog/2015/04/leetcode-best-time-to-buy-and-sell-stock.html + +##### 局部最优解 vs. 全局最优解: +- local[i][j] = max(global[i – 1][j – 1] + diff, local[i – 1][j] + diff) +- global[i][j] = max(global[i – 1][j], local[i][j]) +- local[i][j]: 第i天,当天一定进行第j次交易的profit +- global[i][j]: 第i天,总共进行了j次交易的profit. + +- local[i][j]和global[i][j]的区别是:local[i][j]意味着在第i天一定有交易(卖出)发生。 +- 当第i天的价格高于第i-1天(即diff > 0)时,那么可以把这次交易(第i-1天买入第i天卖出)跟第i-1天的交易(卖出)合并为一次交易,即local[i][j]=local[i-1][j]+diff; +- 当第i天的价格不高于第i-1天(即diff<=0)时,那么local[i][j]=global[i-1][j-1]+diff,而由于diff<=0,所以可写成local[i][j]=global[i-1][j-1]。 +- (Note:在我下面这个solution里面没有省去 +diff) + +- global[i][j]就是我们所求的前i天最多进行k次交易的最大收益,可分为两种情况: +- 如果第i天没有交易(卖出),那么global[i][j]=global[i-1][j]; +- 如果第i天有交易(卖出),那么global[i][j]=local[i][j]。 + + + + + +--- + +**32. [Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)** Level: Hard Tags: [Binary Search, Coordinate DP, DP] + + +俄罗斯套娃, 这里用envelope来表现. 给一串array, 每一个[x, y] 是envelope 长宽. [[5,4],[6,4],[6,7],[2,3]]. + +看用这些套娃, 可以最多套几个. + +#### DP: 1D Coordinate +- envelopes没有顺序, 先排序 (主要根据第一个index排序) +- 然后观察: 排序过后, 就变成了1D的坐标动态规划. +- max number 取决于上一个成功Russian doll的 max value + 1 +- 上一个index不知道, 所以遍历找上一个index. +- 当下index i 的状态, 取决于前面index j 的状态, 所以遍历两个index. +- O(n^2)的DP, n = envelopes.length; + +#### DP: 2D Coordinate +- 这个方法是自己想出来的, 但是时间复杂度太大, timeout +- 把envelop标记在2D grid上面, 然后像走机器人一样, 求到最右下角的最大 count max. +- count 当下能存在多少Russian doll +- 两种情况: 当下coordinate 没有target, 当下coordinate有target +- 当下coordinate 没有target: 如同机器人走法, Math.max(dp[i - 1][j], dp[i][j - 1]) +- 当下coordinate 有target: dp[i - 1][j - 1] + dp[i][j] +- timeout: O(n^2), n = largest coordinate. + + + + +--- + +**33. [Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Minimum Binary Tree, Stack] + + +给一串字符, 表示的是 公式 expression. 把公式变成expression tree + +#### Monotonous Stack +- 和Max-tree一样,https://leetcode.com/problems/maximum-binary-tree +- 用到bottom->top递增的stack: 最底下的root维持成最小的element. +- 这个题目是Min-tree, 头上最小,Logic 和max-tree如出一辙 +- Space: O(n) +- Time on average: O(n). + +#### 特点 +- TreeNode: 用一个并不是最终结果的TreeNode, 存weight, 用来排序 +- 用base weight的概念权衡同一个层面的 符号, 数字 顺序 +- 每一个character都是一个节点, 都有自己的weight. 用一个TreeNode来存weight value, 利用用weight来判断: +- 1. (while loop) 如果node.val <= stack.peek().nodeValue, 把当前stack.peek() 变成 left child. +- 2. (if condition) 如果stack有残余, 把当前node变成 stack.peek().rightChild + + + + +--- + +**34. [Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack] + + +给一个公式 expression, array of strings, 然后evaluate expression 结果. + +#### DFS on Expression Tree +- 计算 expression 的值: 1. 建造 expression tree. 2. DFS计算结果 +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- build好Min Tree以后,做PostTraversal. +- Divde and Conquer: 先recursively找到 left和right的大小, 然后evaluate中间的符号 +- Time, Space O(n), n = # expression nodes + +#### Note +- 1. Handle数字时,若left&&right Child全Null,那必定是我们weight最大的数字node了。 +- 2. 若有个child是null,那就return另外一个node。 +- 3. prevent Integer overflow during operation:过程中用个Long,最后结局在cast back to int. + + + +--- + +**35. [Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Polish Notation (PN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Pre-order-traversal 就能记录下 Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. +- Note: label需要是String. 虽然 Operator是长度为1的char, 但是数字可为多位 + + + +--- + +**36. [Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)** Level: Hard Tags: [Binary Tree, DFS, Expression Tree, Stack] + + +给一串字符, 用来表示公式expression. 把这个expression转换成 Reverse Polish Notation (RPN). + +#### Expression Tree +- Expression Tree: Minimum Binary Tree (https://lintcode.com/en/problem/expression-tree-build/) +- 根据题意做出Expression Tree出来以后: 来个Post-order-traversal 就能记录下 Reverse Polish Notation +- 本题没有给'ExpressionTreeNode', 所以把TreeNode就当做成我们需要的node, 里面扩展成有left/right child就可以了. + + + +--- + +**37. [Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways%20II.java)** Level: Hard Tags: [DP, Enumeration, Partition DP] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +其中字符可能是 "*", 可以代表 [1 - 9] + +#### DP +- 乘法原理 +- 跟decode way I 一样, 加法原理, 切割点时: 当下是取了 1 digit 还是 2 digits 来decode +- 定义dp[i] = 前i个digits最多有多少种decode的方法. new dp[n + 1]. +- 不同的情况是: 每一个partition里面, 如果有"*", 就会在自身延伸出很多不同的可能 +- 那么: dp[i] = dp[i - 1] * (#variations of ss[i]) + dp[i - 2] * (#variations of ss[i,i+1]) + +##### 特点 +- 枚举的能力: 具体分析 '*' 出现的位置, 枚举出数字, 基本功. +- 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) +- 理解取MOD的原因: 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. +- 枚举好以后, 其实这个题目的写法和思考过程都不难 + + + + +--- + +**38. [Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)** Level: Hard Tags: [DP, Partition DP] + + +给一个String s, 找出最少用多少cut, 使致 切割出的每一个substring, 都是palindrome + +#### Partition DP +- Find minimum cut: 分割型DP +- dp[i]: 最少cut多少刀, 使得前 i 长度的string, 割出来都是palindrome +- 最终要得到 dp[n], 所以 int[n + 1] +- 移动切刀, 看在哪里切, index j in [0 ~ i] +- 考虑[j, i - 1] 是否是回文串, 如果是, 那么: dp[i]= min(dp[i], d[j] + 1). +- note: 估计遍历 j 的时候, 反过来遍历也可以. + +#### 计算Palindrome的优化 +- 利用palindrome的性质, 可以算出 boolean palindrome[i, j]的情况. +- 找一个任意mid point: +- 1. 假设palindrome是奇数长度, 那么 mid 是单独的字符, 而两边的字符 [mid-1], [mid+1] 应该完全相等. +- 2. 假设palindrome是偶数长度, 那么 [mid] 和 [mid + 1] 这样位置的字符应该相等. +- 这样做出来 palindrome[i, j]: 从字符 i 到 字符 j 的 substring 是否是 palindrome +- 这样就给我们的问题合理降维, 目前是time: O(n^2). +- 不然求一次palindrome, 就是n, 会变成O(n^3) + +#### Previous Notes +- Double for loop 检查每种substring string (i~j). 若i,j相邻或者同点,那么肯定isPal;否则,i,j之间的(i+1, j-1)一定得isPal。 +- 看上去,在检查i,j的时候,中间按的(i+1, j-1)怎么可能先知道? 其实不然..在j慢慢长大的时候,所有的0~j的substring都检查过。所以isPal[i+1][j-1]一定是已经知道结果的。 +- okay.那么假如以上任意一种情况成立,也就是说isPal[i][j] == true。那就要判断,切到第一层循环参数j的末尾点时,有多少种切法? +- 想法很顺:我们naturally会想到,把i之前的cut加上i~j之间发生的不就好了。 +- 反正现在j不变,现在就看吧i定在哪里,cut[i - 1]是否更小/最小; 再在cut[i-1]基础上+1就完了。 +- 当然,如果i==0, 而 i~j又是isPal,那没啥好谈的,不必切,0刀。 +- 最终,刷到cut[s.length() - 1] 也就是最后一点。 return的理所应当。 + + + + +--- + +**39. [Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)** Level: Hard Tags: [Backpack DP, DP] + + +给n种不同的物品, int[] A weight, int[] V value, 每种物品可以用无限次 + +问最大多少value可以装进size是 m 的包? + +#### DP +- 可以无限使用物品, 就失去了last i, last unique item的意义: 因为可以重复使用. +- 所以可以转换一个角度: +- 1. 用i **种** 物品, 拼出w, 并且满足题目条件(max value). 这里因为item i可以无限次使用, 所以考虑使用了多少次K. +- 2. K虽然可以无限, 但是也被 k*A[i]所限制: 最大不能超过背包大小. +- dp[i][w]: 前i种物品, fill weight w 的背包, 最大价值是多少. +- dp[i][w] = max {dp[i - 1][w - k*A[i-1]] + kV[i-1]}, k >= 0 +- Time O(nmk) +- 如果k = 0 或者 1, 其实就是 Backpack II: 拿或者不拿 + +#### 优化 +- 优化时间复杂度, 画图发现: +- 所计算的 (dp[i - 1][j - k*A[i - 1]] + k * V[i - 1]) +- 其实跟同一行的 dp[i][j-A[i-1]] 那个格子, 就多出了 V[i-1] +- 所以没必要每次都 loop over k times +- 简化: dp[i][j] 其中一个可能就是: dp[i][j - A[i - 1]] + V[i - 1] +- Time O(mn) + +#### 空间优化到1维数组 +- 根据上一个优化的情况, 画出 2 rows 网格 +- 发现 dp[i][j] 取决于: 1. dp[i - 1][j], 2. dp[i][j - A[i - 1]] +- 其中: dp[i - 1][j] 是上一轮 (i-1) 的结算结果, 一定是已经算好, ready to be used 的 +- 然而, 当我们 i++,j++ 之后, 在之前 row = i - 1, col < j的格子, 全部不需要. +- 降维简化: 只需要留着 weigth 这个 dimension, 而i这个dimension 可以省略: +- (i - 1) row 不过是需要用到之前算出的旧value: 每一轮, j = [0 ~ m], 那么dp[j]本身就有记录旧值的功能. +- 变成1个一位数组 +- 降维优化的重点: 看双行的左右计算方向 +- Time(mn). Space(m) + + + +--- + +**40. [First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/First%20Missing%20Positive.java)** Level: Hard Tags: [Array] + + +给一串无序数字, 有负数: 找这个array里面第一个 missing的 positive integer + +missing positive integer 其实是以 [1, n] 来做比较的. + +#### Array分析, index 技巧 +- 用while loop, 不断地尝试把 number 送到该放的地方 +- 如果 index = nums[i] 超过了nums.length, 当然就不移动了 +- 注意: 检查 val != nums[val], avoid infinitely loop +- 检验: nums[i] 是否等于 i, 如果不对, 就找到了结果 + +#### Edge Case +- 如果nums==null, 其实missing positive integer 自然而然是 1 +- validation时, 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) +- 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +- 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + +**41. [N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens.java)** Level: Hard Tags: [Backtracking] + + +N-Queen 问题, 给数字n, 和 nxn board, 找到所有N-queens的答案. + +#### Backtracking +- 用dfs找所有情况, 每一个iteration, 从找一行里挑合适的点, dfs +- 选中的点加进candidate list 里面, 记得要backtracking. +- 每一个candidate都需要validation, 检查 row, col, 2 diagnal 有没有queen + +#### validate n queue at certain (x, y) +- 1. array 里面不能有 target row# +- 2. diagnal. 记得公式: +- row1 - row2 == col1 - col2. Diagnal elelment.fail +- row1 - row2 == - (col1 - col2). Diagnal element. fail +- Draw a 3x3 board to test the 2 scanarios: +- (0,0) and (3,3) are diagnal +- (0,2) and (2,0) are diagnal + + + + +--- + +**42. [N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/N-Queens%20II.java)** Level: Hard Tags: [Backtracking] + + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + + + +--- + +**43. [LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LRU%20Cache.java)** Level: Hard Tags: [Design, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度。 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) + +##### 巧妙点 +- 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,就都特别快。 +- 2. 用双向的pointer: pre和next, 当需要除掉任何一个node的时候,只要知道要除掉哪一个, +- 直接把node.pre和node.next耐心连起来就好了,node就自然而然的断开不要了。 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法: +- moveToHead() +- insertHead() +- remove() + +#### O(n) 检查重复 +- timeout method, 天真的来了一个O(n) 的解法,结果果然timeout. +- 一个map存数值。一个queue来存排位。 +- 每次有更新,就把最新的放在末尾;每次超过capaticity,就把大头干掉。很简单嘛,但是跑起来太久,失败了。 + + + + +--- + +**44. [Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### Kinda, Tree DP +- 两个情况: 1. combo sum: left+right+root; 2. single path sum +- Note1: the path needs to be continuous, curr node cannot be skipped +- Note2: what about I want to skip curr node: handled by lower level of dfs(), where child branch max was compared. +- Note3: skip left/right child branch sum, by comparing with 0. 小于0的, 没必要记录 + +#### DP的思想 +- tree给我们2条branch, 每条branch就类似于 dp[i - 1], 这里类似于dp[left], dp[right] 这样 +- 找到 dp[left], dp[right] 以后, 跟 curr node结合. +- 因为是找max sum, 并且可以skip nodes, 所以需要全局变量max +- 每次dfs() return的一定是可以继续 `continuously link 的 path`, 所以return `one single path sum + curr value`. + +#### DFS, PathSum object +- that just solves everything + + +--- + +**45. [Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)** Level: Hard Tags: [Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack] + + +给一个expression String, 要evaluate这个expression的值. + +Expression string 里面包括 +, -, 整数, 开合括号, 还有space. + +#### Expression Tree +- Expression Tree是一个 weight-based的 min-tree +- 基于 运算符号 + 数字的 tree: 数字永远在leaf, 然后符号是tree node, 括号不出现在tree里面 +- 用 monotonuous stack 来构建这个tree + +##### Thinking points +- Understand Expression Tree +- Use stack to build the expression tree + understand the weight system +- Use post-order traversal to evaluate the tree +- 注意, input里面的数字不会是single digit, 所以需要一个buffer存number string +- 整个题目的做法, 可以参照 `Expression Evaluation` + + + +--- + +**46. [Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)** Level: Hard Tags: [Array, Hash Table, Union Find] + + +给一串数字, unsorted, 找这串数字里面的连续元素序列长度 (consecutive序列, 是数字连续, 并不是说要按照原order) + +#### HashSet +- 要想看连续元素, 必须要num++, num--这样搜索 +- 1. 需要O(1)找到元素 +- 2. 需要简单快速找到 num - 1, num + 1. +- 如果用min,max开array, 耗费空间 +- 用HashSet来存, 用set.contains() 来查找 num - 1, num + 1 存在与否 +- for loop. O(n) +- 里面的while loop 一般不会有O(n); 一旦O(n), 也意味着set 清零, for loop也不会有更多 inner while 的衍生. +- overall O(n) 时间复杂度 + + +#### Union Find +- 最终是要把相连的元素算一下总长, 其实也就是把元素group起来, 相连的group在一起, 于是想到UnionFind +- 这里用到了一个`int[] size` 来帮助处理 `合并的时候parent是哪个`的问题: 永远往group大的union里去 +- main function 里面, 有一个map来track, 每个元素, 只处理1遍. +- union的内容: current number - 1, current number + 1 +- https://www.jianshu.com/p/e6b955ca208f + +##### 特点 +- Union Find 在index上做好像更加容易 +- 其他union find function: `boolean connected(a,b){return find(a) == find(b)}` + + + +--- + +**47. [Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Serialize%20and%20Deserialize%20Binary%20Tree.java)** Level: Hard Tags: [BFS, DFS, Deque, Design, Divide and Conquer, Tree] + + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer +##### Serilize +- Divide and conquer: Pre-order traversal to link all nodes together +- build the string data: use '#' to represent null child. +- the preorder string, can be parsed apart by `split(',')` + +##### Deserialize +- Use a list (here we use `Deque` for the ease of get/remove in 1 function: remove()) +- to take all parts of the parsed sring data: dfs on the Deque +- first node from the list is always the head +- '#' will be a null child: this should break dfs +- Deque is a global variable, so dfs(right child) will happen after dfs(left child) completes + +#### DFS, Recursive [previous note] +- serilize: divide and conquer, pre-order traversal +- deserialize: 稍微复杂, 用dfs. 每次要truncate input string: +- 一直dfs找left child, 接着right child until leaf is found. +- 用一个StringBuffer来hold string, 因为string 是primitive, 我们这里需要pass reference + +#### BFS, Non-recursive +- using queue. 想法直观。level-order traversal. save到一个string里面就好。 +- 遇到null child, 不是直接忽略, 而是assign一个Integer.MIN_VALUE, 然后 mark as '#' +- BFS需要track queue size, 每一次只process特定数量的nodes + + + +--- + +**48. [Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Numbers%20After%20Self.java)** Level: Hard Tags: [BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree] + + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Binary Search +- sort and insert 进一个新list, 新的list是sorted +- 从末尾 i = n-1 遍历nums[] +- 每一次insert nums[i] 进list的位置, 就是# of smaller items on right side of nums[i] +- 每次记录下result[i] +- **问题**: 这里的binary search 是用 `end = list.size(); while(start elements processed from left-hand-side can be removed from segment tree +- Use `modify(root, target, -1)` to remove element count from segment tree. Reuse function +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element + +##### Segment Tree solution - tricky part: +- negative nubmer works oddly with mid and generates endless loop in build(): `[-2, -1]` use case +- build entire segment tree based on [min, max], where min must be >= 0. +- we can do this by adding Math.abs(min) onto both min/max, as well as +diff during accessing nums[i] + + + +#### Binary Indexed Tree +- TODO, have code + + + +--- + +**49. [Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)** Level: Hard Tags: [Greedy, Hash Table, Stack] + + +#### Hash Table, Greedy +- count[] = int[256], 不需要 `c-'a'` +- boolean visited[]: 一旦一个字母固定了位置后, 再次遇到时候, 直接跳过用过的character +- 如果tail字母可以变小, 那就delete掉tail, 重新接上新字母 (前提条件: 去掉的字母后面还会再出现, set visited[tail] = false) +- Space: O(1) count[], visited[]. +- Time: Go through all letters O(n) + +#### Stack +- Use stack instead of stringBuffer: keep append/remove last added item +- However, stringBuffer appears to be faster than stack. + + + +--- + +**50. [Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)** Level: Hard Tags: [Backtracking, DFS, Divide and Conquer, String] + + +给一个数字String, 数字来自`0-9`, 给3个操作符 `+`,`-`,`*`, 看如何拼凑, 可以做出结果target. + +output 所有 expression + +#### string dfs, use list to track steps (backtracking) +- 跟string相关, 写起来可能稍微繁琐一点 +- 数字有 dfs([1,2,3...]) 组合方法 +- operator有[`+`,`-`,`*`] 3种组合方法 +- 注意1: 乘号要特殊处理, pass along 连乘的数字, 计算下一步乘积的时候, 要 sum - preProduct + product +- 注意2: '01' 这种数字要skip +- 注意3: 第一个选中数字不需要加操作符, 直接加进去 +- Time: O(4^n), Space: O(4^n) +- T(n) = 3 * T(n-1) + 3 * T(n-2) + 3 * T(n-3) + ... + 3 *T(1); +- T(n-1) = 3 * T(n-2) + 3 * T(n-3) + ... 3 * T(1); +- Thus T(n) = 4T(n-1) = 4^2 * T(n - 1) = .... O(4^n) + +#### String dfs, use string as buffer +- 逻辑一样, 代码更短, 只不过不做list, 直接pass `buffer + "+" + curr` +- 因为每次都创建新string, 所以速度稍微慢一点. Time complexity 一样 + + + +--- + +**51. [Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort] + + +#### Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn) + + +#### Basic Implementation +- 这里已经给了 **sorted** intervals by start point. +- 直接找到可以insert newInterval的位子. Insert +- 然后loop to merge entire interval array +- 因为给的是个list, 所以方便`intervals.remove(i)` +- remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture +- O(n) + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**52. [Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)** Level: Hard Tags: [KMP, String] + + +#### Divide by mid point, Brutle +- check (mid, mid+1), or (mid-1, mid+1). +- If the two position matches, that is a palindrome candidate +- 比较front string 是否是 end string 的substring +- O(n^2) +- timeout on last case: ["aaaaaa....aaaacdaaa...aaaaaa"] + +#### KMP +- TODO + + + +--- + +**53. [K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)** Level: Hard Tags: [Array, BST, TreeSet] + + +题目解析后: find 2 number, that: 1. k slots between the 2 number, 2. no slots taken between the two number. + +#### BST +- BST structure not given, use TreeSet to build BST with each node +- Every time find last/next inorder element +- `treeSet.lower(x)`, `treeSet.higher(x)` +- 一旦位置相隔(k + 1), 就满足题目条件 +- O(nlogn), good enough + +#### Track slots of days +- Reverse the array, save days index into days[], where the new index is slot. +- days[i]: at slot i, which day a flower will be planted +- O(n) +- Needs to understand: http://www.cnblogs.com/grandyang/p/8415880.html + + + +--- + +**54. [Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BST, Divide and Conquer, Merge Sort, PreSum] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- 算法非常厉害就是了: 先做presum[], 那么 sum range [i,j] 就等于是preSum[j+1] - preSum[i] +- 分治: 考虑[start, mid] range里面的结果, 再考虑[mid, end] range里面的结果. (分开来 mergeSort) +- 最后考虑[low,high]总体的结果 +- 小技巧: PreSum 做成了 (n + 1) length, 那么求range sum [i,j] 就可以简化成 preSum[j] - preSum[i] +- NOTE: should write merge() function, but that is minor, just use `Arrays.sort(nums, start, end)`, OJ passed +- Every mergeSort() has a for loop => O(n log n) + +##### 如何 count range? +- 这里比较特别的一个做法: 找一个 [low, mid]里面的i, mid 之后的preSum作比较 (解释源自: https://blog.csdn.net/qq508618087/article/details/51435944) +- 即在右边数组找到两个边界, 设为`m, n`, +- 其中m是在右边数组中第一个使得`sum[m] - sum[i] >= lower`的位置, +- n是第一个使得`sum[n] - sum[i] > upper`的位置, +- 这样`n-m`就是与左边元素i所构成的位于`[lower, upper]`范围的区间个数. + +##### 神奇的重点: 为什么要 merge and sort +- 边界[lower, higher] 在 sorted array 好作比较, 一旦国界, 就可以停止计算, 减少不必要计算. +- 上面这个n,m的做法可行的前提: preSum[]里面前后两个 range[low, mid], [mid, high]已经sorted了 +- 也就是说, 在recursively mergeSort()的时候, 真的需要merge sorted 2 partitions +- 也许会问: 能不能sort呢, sort不久打乱了顺序? 对,打乱的是preSum[]的顺序. +- 但是不要紧: 很巧妙的, 分治的时候, 前半段/后半段 都在原顺序保留的情况下 分开process完了, 最后才merge +- 在做m,n 的range的时候, 原理如下, 比如preSum被分成这么两段: `[A,B,C]`, `[D,E,F]` +- 每一个preSum value `A` 在跟 preSum[i] 作比较的时候 `A - preSum < lower`, 都是单一作比较, 不牵扯到 B, C +- 因此, `[A, B, C]` 是否保留一开始 preSum的顺序在此时不重要 +- 此时最重要的是, `[A,B,C]`以及排序好, 那么在于 `lower` boundary 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BST +- TODO? + + + +--- + +**55. [Max Sum of Rectangle No Larger Than K.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Sum%20of%20Rectangle%20No%20Larger%20Than%20K.java)** Level: Hard Tags: [Array, BST, Binary Search, DP, Queue, TreeSet] + + +给定一个非空的二维矩阵matrix与一个整数k,在矩阵内部寻找和不大于k的最大矩形和。 + +#### BST, Array, preSum +- 将问题reduce到: row of values, find 1st value >= target. +- 1. loop over startingRow; 2. loop over [startingRow, m - 1]; 3. Use TreeSet to track areas and find boundary defined by k. +- When building more rows/cols the rectangle, total sum could be over k: +- when it happens, just need to find a new starting row or col, +- where the rectangle area can reduce/remain <= k +- 找多余area的起始点: extraArea = treeSet.ceiling(totalSum - k). 也就是找 减去k 后 起始的/左边的area. +- 去掉这些左边的起始area, 剩下的就 <=k. (num - extraArea) +- 为什么用TreeSet: area的大小无规律, 并且要找 >= 任意值 的第一个value. 给一串non-sorted数字, 找 >= target的数, 如果不写binary search, 那么用BST最合适 +- O(m^2*nlogn) + +#### 思想 +- 从最基本的O(m^2*n^2) 考虑: 遍历 startingRow/startingCol +- rectangle? layer by layer? 可以想到Presum的思想, 大于需要的sum的时候, 减掉多余的部分 +- 如何找到多余的area? 那么就是search: 把需要search的内容存起来, 可以想到用BST(TreeSet), 或者自己写Binary Search. + + + +--- + +**56. [Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)** Level: Hard Tags: [Design, Geometry, Hash Table] + + +看的list of coordinates 是否能组成perfect rectangle, 并且不允许overlap area. + +#### 画图发现特点 +- 特点1: 所有给出的点(再找出没有specify的对角线点), 如果最后组成perfect rectangle, 都应该互相消除, 最后剩下4个corner +- 特点2: 找到所有点里面的min/max (x,y), 最后组成的 maxArea, 应该跟过程中accumulate的area相等 +- 特点1确保中间没有空心的部分, 保证所有的重合点都会互相消除, 最后剩下4个顶点 +- 特点2确保没有重合: 重合的area会最终超出maxArea + + + +--- + +**57. [Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Points%20on%20a%20Line.java)** Level: Hard Tags: [Array, Geometry, Hash Table, Math] + + +给list of (x,y) coordinates. Determine # of points on the same line + +#### Observation +- If given n points, we can calculate all possible slopes. O(n^2) times +- For the two dots that generates the same slope, these dots could be on **parallel** slopes +- figure out how to prune the parallel dots + +#### Trick: prune parallel dots using greatest common divider +- GCD: greatest common divider +- Devide the x and y by their greatest common divider, such that x and y can be reduced to minimum value +- All other x and y can be reduced to such condition as well +- track the final reduced (x,y) in a map: they are the key to the count +- No need to use Map> to perform 2 level mapping; just `map`, where the key is "x@y" + + + +--- + +**58. [Number of Digit One.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Digit%20One.java)** Level: Hard Tags: [Math] + + +Pure math problem, not quite representative + +Explanation +https://leetcode.com/problems/number-of-digit-one/discuss/64381/4+-lines-O(log-n)-C++JavaPython + + + +--- + +**59. [Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)** Level: Hard Tags: [Bit Manipulation, String] + + +#### String +- 首先要分两半解决,断点是'.': str.split("\\."); +- Integer那一半好弄,whie loop里: num%2, num/2. 做一个 `parseInteger()` function +- Decimal那边复杂点. 做一个 `parseDecimal()` function: +- bit == 1的数学条件: 当下num * 2 >= 1。 更新: num = num * 2 - 1; +- bit == 0的数学条件: num * 2 < 1. 更新: num = num * 2 + +#### 注意 +- num是 double, 小数在 `num = num * 2 - 1` 的公式下可能无限循环 +- 因此check: num重复性,以及binary code < 32 bit. +- 所以题目也才有了32BIT的要求! + + + +--- + +**60. [Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)** Level: Hard Tags: [BST, DFS, Tree] + + +BST里面有2个node misplace, 要归为. 要求: O(1) extra space + +#### Observation +- BST inorder traversal should give small -> large sequence +- misplaced means: a **large**->small item would occur, and later a large>**small** would occur. +- The first large && second small item are the 2 candidates. Example +- [1, 5, 7, 10, 12, 15, 18] +- [1, 5, `15, 10`, `12, 7`, 18] + +#### dfs, O(1) extra space +- traverse, and take note of the candidate +- at the end, swap value of the 2 candidates + +#### O(n) space +- inorder traversal the nodes and save in array, find the 2 items misplanced and swap them +- But O(n) space should not be allowed + + + + +--- + +**61. [Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)** Level: Hard Tags: [Array, Coordinate DP, DP, Greedy] + + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Greedy +- always aiming for the `farest can go` +- if the `farest can go` breaches the end, return steps +- otherwise, send `start=end+1`, `end=farest` and keep stepping from here +- though trying with 2 loops, worst case [1,1,1,...1,1] could have O(n^2) +- But on average should be jumpping through the array without looking back +- time: average O(n) + +#### Previous Notes, Greedy +- 维护一个range, 是最远我们能走的. +- index/i 是一步一步往前, 每次当 i <= range, 做一个while loop, 在其中找最远能到的地方 maxRange +- 然后更新 range = maxRange +- 其中step也是跟index是一样, 一步一步走. +- 最后check的condition是,我们最远你能走的range >= nums.length - 1, 说明以最少的Step就到达了重点。Good. + +#### Even simpler Greedy +- 图解 http://www.cnblogs.com/lichen782/p/leetcode_Jump_Game_II.html +- track the farest point +- whenver curr index reachest the farest point, that means we are making a nother move, so count++ +- there seems to have one assumption: must have a solution. Otherwise, count will be wrong number. +- 其实跟第一个greedy的思维模式是一模一样的. + + +#### DP +- DP[i]: 在i点记录,走到i点上的最少jump次数 +- dp[i] = Math.min(dp[i], dp[j] + 1); +- condition (j + nums[j] >= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**62. [Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)** Level: Hard Tags: [Coordinate DP, Stack, String] + + +给一串string, 里面只有`(`, `)`. 找最长valid parentheses 的长度. + +#### 1D Coordinate DP +- use dp[i] track local max, maintain global max +- int[] dp. dp[i]: longest valid string that ends on i. +- 结尾是 ')', 2种情况: 1. 刚好s[i-1]是'('; 2. s[i]的')'更前面的一个起始'(' 对应 +- 注意, 结尾如果是'('属于不合理情况, 忽略. +- init: dp[0] = 0, 单个char不可能成型. +- 计算顺序: 从左到右, 找local max, maintain global max +- O(n) space, O(n) runtime + +#### Stack +- Stack 里面存所有的open/close parentheses. +- 如果遇到stack.top()刚好开合结掉, 就stack.pop(). +- 剩下的都是不合理的elements. +- 有点像negatively找 solution: `endIndex - 最后一个failedIndex(stack.pop()) - 1`, 应该就是最后一个succeeded string的长度 +- 每次更新 endIndex 为stack.top(), 然后从stack继续找下一个failedIndex +- 所有的length作比较, 就可以找出最长length +- O(n) stack space, O(n) runtime. 应该比dp慢一点, 因为做了2遍O(n) + + + + +--- + +**63. [Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)** Level: Hard Tags: [Greedy, Hash Table, Heap] + + +给一个string, 全是lowercase letter, 要求重新排列: 然后每个unique的character要有k distance apart. + +跟Task Scheduler有点像, 只不过Task那道题里面还可以用其他方法求count, 这道题要求出排列结果 + +#### PriorityQueue + HashTable +- PriorityQueue排序+分布排列的一个经典用法. +- Count frequency and store in pq. +- Consume element of pq for k rounds, each time pick one element from queue. +- Exception: if k still has content but queue is consumed: cannot complete valid string, return ""; +- space, O(n) extra space in sb, O(26) constant space with pq. +- time: O(n) to add all items + + + +--- + +**64. [Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + +time: O(n) + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**65. [Bricks Falling When Hit.java](https://github.com/awangdev/LintCode/blob/master/Java/Bricks%20Falling%20When%20Hit.java)** Level: Hard Tags: [Union Find] + + +给一个matrix of 1 and 0, `1` 代表brick. 连着ceiling的brick就不会drop. 给一串coordinate hits[][], 记录每次take down 1 brick 后, 会drop多少个. + +#### UnionFind +- 1. 我们知道大部分的brick可能都是连着ceiling, 所以每次正向检查都traverse all and timeout +- 2. 能否用union, 把connect都装在一起, 然后drop brick的时候把连着的都drop掉? 难: 因为还是要check所有brick当下的status. +- 受其他人的解答启发, 由于是计算count,我们可以`反向考虑`: +- 把hit-brick全部mark=2 (就当舍弃不算), 观察整个局面的最后一步, 先把所有还连着ceiling的brick算一下总数, 统计在unionFind的 全部统计在count[0] 里面. +- 剩下的不连着ceiling的也就是一个个isolated island +- 做法: 把hit-brick 一个个加回去, 然后再做一次union, 看看最终连到ceiling的有多少个. 增加的count, 就是正向思考时 dropped brick 数量! + +##### Union Find 变种 +- 还是用数字index做union find, 但是把每一个index都+1, 右移一位, 而[0]留下来做特殊用途: +- 用union at 0来 统计总共的remain count of ceiling-connected bricks, where `x = 0`. +- 如果在其他其他题目种, 条件可能就不是`x=0`, 但也可以用这个 union index = 0 来做一个root的统计 +- 关键: 把最后一个hit brick加回去, 然后再重新union一下这个hit-brick周围: count增加的变化, 不就是缺少hit-brick时候掉下去的数量. + + +#### DFS (timeout) +- 考虑每个hit的四周, 全部traverse, 没有连着ceiling就全部: +- 比如是 200 x 200 的 全部是1的matrix, 任何一次traverse都要到顶; 重复计算, 所以timeout +- 算法是没错, 但是不efficient. +- 想要减少重复计算, 但是又不能提前计算: grid在不断变化. 所以看能不能把连着ceiling的都group起来, 可以O(1)快速check? + + + + +--- + +**66. [Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)** Level: Hard Tags: [Binary Search, Lint, Segment Tree] + + +SegmentTree大集合. Methods: `build, query, modify`. 不难。只是要都记得不犯错. + +#### Segment Tree +- build: recursively build children based on index-mid and link to curr node +- query: recursively try to find `node.start == targetStart && node.end == targetEnd`. Compare with node.mid +- modify: recursively try to find `node.start == targetStart && node.end == targetEnd`; modify and recursively assign upper interval with updated interval property. + + + +--- + +**67. [HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/HashHeap.java)** Level: Hard Tags: [HashHeap, Heap] + + +非题.是从九章找来的HashHeap implementation. + + + +--- + +**68. [Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)** Level: Hard Tags: [BFS, Heap, MinHeap, PriorityQueue] + + +给一个2Dmap, 每个position 有 height. 找Trapping water sum. + + +#### Min Heap +- 用PriorityQueue把选中的height排序,为走位, create class Cell (x,y, height). + +##### 注意几个理论 +- 1. 从matrix四周开始考虑,发现matrix能Hold住的水,取决于height低的block +- 2. 必须从外围开始考虑,因为水是被包裹在里面,外面至少需要现有一层 +- 以上两点就促使我们用min-heap: 也就是natural order的PriorityQueue. + +##### Steps +- 1. process的时候,画个图也可以搞清楚: 就是四个方向都走走,用curr cell的高度减去周围cell的高度. +- 2. 若大于零,那么周围的cell就有积水: 因为cell已经是外围最低, 所以内部更低的, 一定有积水. +- 3. 每个visited的cell都要mark, avoid revisit +- 4. 根据4个方向的走位 `(mX, mY)` 创建新cell 加进queue里面: cell(mX, mY) 已经计算过积水后, 外围墙小时, `(mX, mY)`就会变成墙. +- 5. 因为做的是缩小一圈的新围墙, height = Math.max(cell.h, neighbor.h); +- 和trapping water I 想法一样。刚刚从外围,只是能加到跟外围cell高度一致的水平面。往里面,很可能cell高度变化。 +- 这里要附上curr cell 和 move-to cell的最大高度。 + +##### 为什么想到用Heap (min-heap - priorityQueue) +- 要找到bucket的最短板 +- 每次需要最先处理最短的那条 (on top) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**69. [Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### 原理 +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**70. [Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap, Sliding Window] + + +Data Stream Median 的同理题目: 不只是不断增加的Sequence, 而且要remove item (保持一个window size) + +#### MaxHeap, MinHeap +- Median还是用min-heap 和 max-heap. Time(logN) +- 加/减: prioirtyQueue, log(n) +- findMedian: O(1) +- 加一个数, 减一个数。 +- 加减时看好,是从前面的maxheap里面抽,还是从后面的minHeap里面抽。 +- 抽完balance一下 + +#### 注意 +- 用maxHeap, minHeap时候, 习惯选择让maxHeap多一个数字: +- 左边的maxHeap总有 x+1或者x个数字 +- 后边minHeap应该一直有x个数字 + + + +--- + +**71. [Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)** Level: Hard Tags: [Design, Hash Table, MinHeap, PriorityQueue, Trie] + +time: input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words +space: O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map` + +Description is long, but in short: 做 search auto complete. + +Best problem to review Trie (prefix search), Top K frequent elements (Hash Map), and MinHeap (PriorityQueue) + +Easier to revisit https://leetcode.com/problems/design-search-autocomplete-system/description/ + +#### 思考方向 +- 做text的search, 毋庸置疑要用Prefix tree, trie. + +##### Find all possible word/leaf, 两种方案: +- Trie造好之后, 做prefix search, 然后DFS/BFS return all leaf items. [high runtime complexity] +- 在TrieNode里面存所有的possible words. [high space usage] +- in memory space 应该不是大问题, 所以我们可以选择 store all possible words + +##### Given k words, find top k frequent items. 肯定用MinHeap, 但也有两种方案: +- Store MinHeap with TrieNode: 因为会不断搜索新此条, 同样的prefix (尤其是在higher level), 会被多次搜索. +- [complexity: need to update heaps across all visited TrieNodes once new sentence is completed] +- Compute MinHeap on the fly: 当然我们不能每次都来一个DFS不然会很慢, 所以就必须要store list of possible candidates in TrieNode. +- 这里就用到了`Top K Frequent Words` 里面的 `Map`, 这样O(m) 构建 min-heap其实很方便. + +##### Train the system +- 每次 `#` 后 标记一个词条被add进search history. 那么就要 `insert it into trie`. +- 这一条在最后遇到`#`再做就可以了, 非常简洁 + +#### Trie, PriorityQueue, HashMap +- Trie Prefix Search + maintain top k frequent items +- + + + +--- + +**72. [Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Integer%20to%20English%20Words.java)** Level: Hard Tags: [Enumeration, Math, String] + + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 + +#### 注意 +- StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 +- 注意加 " " 的时候, 如果多余, 要`trim()` +- 注意, 小于20的数字, 有自己的特殊写法, 需要额外handle +- 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 + + + + +--- + +**73. [Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph +- 本质: 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先 +- 把 string array 变成topological sort的 graph: `map>` +- 也可以`List[26] edges` (Course Schedule problem) +- Build edges: find char diff between two row, and store the order indication into graph +- 注意: indegree 永远是反向的 (跟 node to neighbors 相反的方式建立) + +#### BFS +- topological sort 本身很好写, 但是要在题目中先了解到字母排序的本质 +- 其实上面这个排序的本质很好想, 但是把它具体化成构建graph的代码, 会稍微有点难想到 +- 算indegree, 然后用 BFS 来找到那些 inDegree == 0的 node +- 最先inDegree == 0的node, 就排在字母表前面. +- 下面的解法, 用了Graph: map>, 而不是 List[26], 其实更加试用超过26个字母的dictionary. +- 如果 `inDegree.size() != result.length()`, there is nodes that did not make it into result. +- ex: cycle nodes from input, where inDegree of a one node would never reduce to 0, and will not be added to result +- In this case, it will be treated as invalid input, and return "" + +#### DFS +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**74. [Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)** Level: Hard Tags: [Array, BFS, Backtracking, DFS, Hash Table, String] + + +给一串string, start word, end word. 找到所有从 startWord -> endWord的最短路径list. + +变化方式: mutate 1 letter at a time. + +#### BFS + Reverse Search +- 用BFS找最短路径. +- 问题: how to effectively store the path, if the number of paths are really large? +- If we store Queue>: all possibilities will very large and not maintainable +- 用BFS做出一个反向structure, 然后再reverse search + +##### BFS Prep Step +- BFS 找到所有start string 可以走到的地方 s, 放在一个overall structure里面: 注意, 存的方式 Map +- BFS时候每次都变化1step, 所以记录一次distance, 其实就是最短路径candidate (止步于此) +- 1. 反向mutation map: `destination/end string -> all source candidates` using queue: `Mutation Map` +- Mutation Map>: list possible source strings to mutate into target key string. +- 2. 反向distance map: `destination/end string -> shortest distance to reach dest` +- Distance Map: shortest distance from to mutate into target key string. +- BFS prep step 并没解决问题, 甚至都没有用到end string. 我们要用BFS建成的反向mapping structure, 做search + +##### Search using DFS +- 从结尾end string 开始扫, 找所有可以reach的candidate && only visit candidate that is 1 step away +- dfs 直到找到start string. + +##### Bi-directional BFS: Search using BFS +- reversed structure 已经做好了, 现在做search 就可以: 也可以选用bfs. +- `Queue>` to store candidates, searching from end-> start + + + +--- + +**75. [Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- Summing space = `width + (size-1)`. maintain: 1. list of candidates, 2. width of actual words +- calculate space in between: `remain/(size - 1)` +- overall for loop; clean up list: 1. over size; 2. last item +- 一点也不难, 但是要小心: deal with list of string的时候, 注意处理干净sum size of list, 就行了. +- `干净处理space`: 只处理 (n-1) items, 然后最后一个拿到for loop 外面, 特殊处理. + +#### Notes +- Clarification, observation: +- can start with greedy approach to stack as many words as possible +- once exceed the length, pop the top, and justify the added words (untouched words tracked by index) +- left justify: given list/stack of words with size t, overall remaining space length m, +- deal with last line with special care: just fill one space, and fill the rest of the row with space +- Does not seem very complicated, but need additional care of calculating the amount of space needed. +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**76. [Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)** Level: Hard Tags: [Enumeration, String] + + +Read N Character using `Read4(char[] buf)` 的加强版: 可以不断读 read(buf, n) + +#### String +- 注意String的index handle, 慢慢写edge case +- 理解题目意思: `read4(char[] buf)` 这样的 `populate input object` 的function稍微少一点. +- 遇到时候, 仔细理解function用法, 不要慌乱. 其实思考方式很简单, 仔细handle string 还有 edge case就好了. + + + +--- + +**77. [Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)** Level: Hard Tags: [DP, Hash Table] + + +Frog jump 的题目稍微需要理解: 每个格子可以 jump k-1, k, k+1 steps, 而k取决于上一步所跳的步数. 默认 0->1 一定是跳了1步. + +注意: int[] stones 里面是stone所在的unit (不是可以跳的步数, 不要理解错). + +#### DP +- 原本想按照corrdiante dp 来做, 但是发现很多问题, 需要track 不同的 possible previous starting spot. +- 根据jiuzhang答案: 按照定义, 用一个 map of > +- 每次在处理一个stone的时候, 都根据他自己的 set of , 来走下三步: k-1, k, or k+1 steps. +- 每次走一步, 查看 stone + step 是否存在; 如果存在, 就加进 next position: `stone+step`的 hash set 里面 + +##### 注意init +- `dp.put(stone, new HashSet<>())` mark 每个stone的存在 +- `dp.get(0).add(0)` init condition, 用来做 dp.put(1, 1) + +##### 思想 +- 最终做下来思考模式, 更像是BFS的模式: starting from (0,0), add all possible ways +- 然后again, try next stone with all possible future ways ... etc + + + +--- + +**78. [Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Two Pointer + HashMap +- 原本想用 DP, 但是其实用 sliding window 的思想 +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border + + + +--- + +**79. [Shortest Distance from All Buildings.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Distance%20from%20All%20Buildings.java)** Level: Hard Tags: [BFS] + + +给Walls and Gates很像, 不同的是, 这道题要选一个 coordinate, having shortest sum distance to all buildings (marked as 1). + +#### BFS +- BFS 可以 mark shortest distance from bulding -> any possible spot. +- Try each building (marked as 1) -> BFS cover all 0. +- time: O(n^2) * # of building; use new visited[][] to mark visited for each building. +- O(n^2) find smallest point/aggregation value. +- 注意, 这道题我们update grid[][] sum up with shortest path value from building. +- 最后找个min value 就好了, 甚至不用return coordinate. +- 分析过, 还没有写. + + + +--- + +**80. [Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Deque, Monotonous queue +- 维持monotonuous queue: one end is always at max and the other end is min. Always need to return the max end of queue. +- when adding new elements x: start from small-end of the queue, drop all smaller elements and append to first element larger than x. +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`. +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! + + + +--- + +**81. [Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)** Level: Hard Tags: [Array, Binary Search, DFS, Divide and Conquer] + + +著名的找两个sorted array的中位数. 中位数定义: 如果两个array总长为偶数, 取平均值. +题目要求在 log(m + n) 时间内解决 + +- 看到log(m+n), 就想到binary search, 或者是recursive 每次砍一半 +- 两个sorted array 参差不齐, 肯定不能做简单的binary search + +#### Divide and Conquer, recursive +- 这里有个数学排除思想: 考虑A, B各自的中间点. +- 如果A[mid] < B[mid], 那么 A[0 ~ mid - 1] 就不在 median的range里面, 可以排除. divide/conquer就这么来的. +- 具体逻辑看代码, 大致意思就是: 每次都取比较A 和 B [x + k / 2 - 1] 的位置, 然后做range 排除法 +- end cases: +- 1. 如果我们发现dfs()里面A或者B的start index溢出了, 那么就是最简单的case: midian一定在另外那个array里面 +- 2. 如果 k == 1: 就是找A/B 里面的1st item, 那么做个 `Math.max(A[startA], B[startB])` 就可以 +- 总共的数字长度是 (m + n) 而且每次都有一般的内容被删除, 那么time就是 O(log(m + n)) + + + + +--- + +**82. [Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)** Level: Hard Tags: [BFS] + + + + +--- + +**83. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + + +--- + +**84. [Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)** Level: Hard Tags: [DFS, Greedy, Math] + + +#### Greedy, Iterative +- For 2 passwords, the shortest situation is both passwords overlap for n-1 chars. +- We can use a window to cut out last (n-1) substring and append with new candidate char from [k-1 ~ 0] +- Track the newly formed string; if new, add the new char to overall result +- Note: this operation will run for k^n times: for all spots of [0 ~ n - 1] each spot tries all k values [k-1 ~ 0] +- Same concept as dfs + +#### DFS +- Same concept: use window to cut out tail, and append with new candidate +- do this for k^n = Math.pow(k, n) times + + + +--- + +**85. [Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)** Level: Hard Tags: [DFS, Graph, Tree, Union Find] + + +#### Union Find +- 讨论3种情况 +- http://www.cnblogs.com/grandyang/p/8445733.html + + + +--- + +**86. [The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)** Level: Hard Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- 跟 Maze I, II 类似, 用一个 Node[][] 来存每一个(x,y)的state. +- Different from traditional BFS(shortest path): `it terminates BFS when good solution exists (distance), but will finish all possible routes` +- 1. `Termination condition`: if we already have a good/better solution on nodeMap[x][y], no need to add a new one +- 2. Always cache the node if passed the test in step1 +- 3. Always offer the moved position as a new node to queue (as long as it fits condition) +- 4. Finally the item at nodeMap[target.x][target.y] will have the best solution. + + + +--- + +**87. [Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +这里的区别是, '*' 需要有一个preceding element, 那么: +- repeat 0 times +- repeat 1 times: need s[i-1] match with prior char p[i-2] + + + +--- + +**88. [Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String] + + +Double sequence DP. 与regular expression 很像. + +#### Double Sequence DP +- 分析字符 ?, * 所代表的真正意义, 然后写出表达式. +- 搞清楚initialization 的时候 dp[i][0] 应该always false. 当p为empty string, 无论如何都match不了 (除非s="" as well) +- 同时 dp[0][j]不一定是false. 比如s="",p="*" 就是一个matching. +- A. p[j] != '*' + 1. last index match => dp[i - 1][j - 1] + 2. last index == ? => dp[i - 1][j - 1] +- B. p[j] == "*" + 1. * is empty => dp[i][j - 1] + 2. * match 1 or more chars => dp[i - 1][j] + + + + +--- + +**89. [Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)** Level: Hard Tags: [Backtracking, DFS] + + +#### DFS +- Different from regular dfs to visit all, the robot move() function need to be called, backtrack needs to move() manually and backtracking path shold not be blocked by visited positions +- IMPORTANT: Mark on the way in using set, but `backtrack directly without re-check against set` +- Mark coordinate 'x@y' +- Backtrack: turn 2 times to revert, move 1 step, and turn 2 times to revert back. +- Direction has to be up, right, down, left. +- `int [] dx = {-1, 0, 1, 0};`, `int[] dy = {0, 1, 0, -1};` + + + +--- + +**90. [Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)** Level: Hard Tags: [DP] + + + + +--- + diff --git a/review/level/Medium.md b/review/level/Medium.md new file mode 100644 index 0000000..35390a8 --- /dev/null +++ b/review/level/Medium.md @@ -0,0 +1,5161 @@ + + + +## Medium (247) +**0. [Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Division.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +#### DFS +- build map of `x#y -> val` to store values[i] and 1/values[i] +- build map of `x -> list children` +- dfs to traverse the graph + +#### BFS +- BFS should also work: build graph and valueMap +- for each starting item, add all next candidate to queue +- mark visited, loop until end item is found + + + +--- + +**1. [Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)** Level: Medium Tags: [Hash Table, Math] + + +TODO: no need of hashMap, just use set to store the existing + +不难想到处理除法:考虑正负,考虑小数点前后。主要是小数点以后的要着重考虑。 +很容易忽略的是integer的益处。 + + +--- + +**2. [Gray Code.java](https://github.com/awangdev/LintCode/blob/master/Java/Gray%20Code.java)** Level: Medium Tags: [Backtracking] + + +TODO: +1. backtracking, using set to perform contains() +2. BFS: use queue to keep the mutations + +题目蛋疼,目前只接受一种结果。 + +BackTracking + DFS: + Recursive helper里每次flip一个 自己/左边/右边. Flip过后还要恢复原样.遍历所有. + +曾用法(未仔细验证): +基本想法就是从一个点开始往一个方向走,每次flip一个bit, 碰壁的时候就回头走。 + + + +--- + +**3. [Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)** Level: Medium Tags: [Enumeration, Greedy] + + +#### Array +- 分三份:a b c考虑 +- 若a: countA++; 或b: countB++ +- 或c:countA--, countB-- +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 +- 最后出现的两个count>0的a和b,自然是potentially大于1/3的。其中有一个大于1/3. +- 比较countA和countB哪个大,就return哪一个。 + + + +--- + +**4. [Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)** Level: Medium Tags: [Hash Table, Linked List] + + +TODO: +1. hash table solution not passing +2. Find O(n) solution + +#### Hash Table +- 与其他Majority Number一样。 +- 出现次数多余1/k,就要分成k份count occurance.用HashMap。 存在的+1;不存在map里的,分情况: +- 若map.size() == k,说明candidate都满了,要在map里把所有现存的都-1; +- 若map.size() < k, 说明该加新candidate,那么map.put(xxx, 1); +- 最后在HashMap里找出所留下的occurance最大的那个数。 +- 但这样的worst case是 O(nk) + + + +--- + +**5. [Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)** Level: Medium Tags: [BFS, Graph] + + +#### Graph + BFS +- Build graph `map` +- BFS to find the shortest path: when the neibhbor has the curr node as the only one neighbor, it is leaf. +- record shortest path in Map> as result +- TODO: code it up. + +#### Previous Solution +- removing leaf && edge + + + +--- + +**6. [Missing Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Ranges.java)** Level: Medium Tags: [Array] + + +#### Basic Implementation +- O(n) +- 两个pointer, 每次计较prev和curr之间的部分. +- 然后prev = curr,向前移动一格 +- TODO: check the edge case and make sure max/min of int are checked + + + +--- + +**7. [Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Permutation.java)** Level: Medium Tags: [Array] + + +需斟酌: why reverse is need? why we are looking for k? + +Permutation的规律: +1. 从小的数字开始变化因为都是从小的数字开始recursive遍历。 +2. 正因为1的规律,所以找大的断点数字要从末尾开始: 确保swap过后的permutation依然是 前缀固定时 当下最小的。 + +steps: +1. 找到最后一个上升点,k +2. 从后往前,找到第一个比k大的点, bigIndex +3. swap k && bigIndex +4. 最后反转 (k+1,end) + + + + +--- + +**8. [Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)** Level: Medium Tags: [Backtracking, Permutation] + + +TODO: need to review permutation + +permutation的综合题: +1. validate Input 是不是可以做palindromic permutation. 这个就是(Palindrome Permutation I) +2. 顺便存一下permutation string的前半部分和中间的single character(if any) +3. DFS 做unique permutation: given input有duplicate characters。 + + + +--- + +**9. [Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)** Level: Medium Tags: [Backtracking, Math] + + +TODO: what about regular DFS/backtracking to compute the kth? dfs(rst, list, candiate list, k) + +k是permutation的一个数位。而permutation是有规律的。 + +也就是说,可以根据k的大小来判断每一个数位的字符(从最大数位开始,因为默认factorio从最大数位开始变化)。 + +于是先求出n!, 然后 k/n!就可以推算出当下这一个数位的字符。然后分别把factorio 和 k减小。 + +另外, 用一个boolean[] visited来确保每个数字只出现一次。 + +这个方法比计算出每个permutation要efficient许多。 + + + + +--- + +**10. [Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array] + + + + + +--- + +**11. [Remove Duplicates from Unsorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Unsorted%20List.java)** Level: Medium Tags: [Linked List] + + +基本方法: O(n) sapce, time +遍历。 +遇到duplicate(可能多个), while直到node.next不是duplicate. +接下去,既然不是duplicate,那就add 进 set + + +如果不用extra memory, do it in place: +那就要sort linked list. 用merge sort. + +复习merge sort: +1. find middle. +2. recursively: right = sort(mid.next); left = sort(head). +3. within sort(), at the end call merge(left, right) + + +--- + +**12. [Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)** Level: Medium Tags: [Array, Enumeration] + + +#### 找公式规律 +- 找到个转角度的规律公式: r = c; c = (w - r) +- 用temp variable, swap in place. + + + +--- + +**13. [Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +Allow duplicates之后: +因为最终binary search的结果也是O(n) +所以这道题要记得: 既然是O(n), 那来个简单的for loop 也就好了。 + +当然,要跟面试官提起来原因。别一上来就只有for。。。 + + +--- + +**14. [Single Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20II.java)** Level: Medium Tags: [Bit Manipulation] + + +一串数字里面, 所有数字都重复三次, 除了一个数字. 找到这个数字, linear time, without extrace space (constant space) + +TODO: bits + + + +--- + +**15. [Single Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20III.java)** Level: Medium Tags: [Bit Manipulation] + + +TODO: wut? + + +--- + +**16. [Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)** Level: Medium Tags: [String] + + + + +--- + +**17. [Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)** Level: Medium Tags: [DP] + + +这个DP有点诡异. 需要斟酌。 +NOT DONE YET + + +--- + +**18. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + + +--- + +**19. [Total Occurrence of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Occurrence%20of%20Target.java)** Level: Medium Tags: [] + +想法很简单。写起来有点长。 +找total number of occurance. 首先找first occurance, 再找last occurance. + + + +--- + +**20. [Two Lists Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Lists%20Sum.java)** Level: Medium Tags: [Linked List] + + +给两个Linked list, sum up and 合成新的list + + + +--- + +**21. [Zigzag Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Zigzag%20Iterator.java)** Level: Medium Tags: [BST] + + +这个题目相对简单. 做的时候我先考虑起来k条怎么办. 那么用个map把index和每个listmark一下就好了。 +每次next(), 相应的list的头拿下来就好。 +然后就跑圈呗,每次刷一个list头。不难。只要把几个variable维护清楚就行。 + + +--- + +**22. [Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)** Level: Medium Tags: [Hash Table, Math] + + +其实想到了切入点, 是个可难可简单的题目. 这里的encode就是想办法把url存起来, 然后给个 key. +那么具体怎么做这个key, 简单就可以用一个map, 然后counting. 复杂一点就可以做random letter/number组成key. + + + +--- + +**23. [Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)** Level: Medium Tags: [Array, Sort] + + +方法1: +排序, nLog(n). 然后把直线上坡变成层叠山峰, 需要每隔几个(题目中是每隔2位)就做个swap 造成高低不平. +Note: 每隔山峰之间是相互没有关系的, 所以每次只要操心 [i], [i-1]两个位置就好了. + +方法2: +O(n) +看好奇数偶数位的规律, 然后根据题目给出的规律, 跑一遍, 每次只关注两个位置: 把不合适的[i], [i-1]调换位置就好了. + +方法3: +跟法2一样, 只是更巧妙一点罢了: +第一遍想太多. 其实做一个fall-through就能把问题解决,原因是因为: +这样的fall-through每次在乎两个element,可以一口气搞定,无关乎再之前的elements。 +特别的一点:flag来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**24. [Queue Reconstruction by Height.java](https://github.com/awangdev/LintCode/blob/master/Java/Queue%20Reconstruction%20by%20Height.java)** Level: Medium Tags: [Greedy] + + +别无他法, 只能写一遍例子, 找规律,然后greedy.  +需要写一遍发现的规律比如: 从h大的开始排列, 先放入k小的. 写comparator的时候要注意正确性. +如果要sort, 并且灵活insert:用arrayList. 自己做一个object. +最后做那个'matchCount'的地方要思路清晰, 找到最正确的spot, 然后greedy insert. + +O(n) space, O(nLog(n)) time, because of sorting. + +可能有简化的余地, 代码有点太长. +比如试一试不用额外空间? + + + +--- + +**25. [Two Sum II - Input array is sorted.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Sum%20II%20-%20Input%20array%20is%20sorted.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +升序array, 找2SUM. + +#### Two pointers +- 排序好的array. Two pointer移动start和end,核查sum. +- 注意sum用long. +- O(n) time + +#### Binary Search, 因为已经排好序了啊 +- 定住一个valueA, 然后在剩下的里面 binary serach 找 (target - valueB) +- for loop O(n), binary search O(logn) +- overall time: O(nLogN), 就不写了 + + + +--- + +**26. [2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/2%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +与 2sum II - input array is sorted类似. 都是sort array, 然后two pointer. + +LintCode的题. 注意找的是greater/bigger than target。 + +由于给定条件允许O(nLogn): + sort + two pointer + +while里面two pointer移动。每次如果num[left]+num[right] > target,那么其中所有num[left++]的加上num[right]都>target. +也就是,num[right]不动,计算加入挪动left能有多少组,那就是: right-left这么多。 全部加到count上去。 +然后right--.换个right去和前面的left部分作比较。 + + + +--- + +**27. [Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + +#### DP +- 找对方程dp[x], 积累到amount x最少用多少个coin: #coin是value, index是 [0~x]. +- 子问题的关系是: 如果用了一个coin, 那么就应该是f[x - coinValue]那个位置的#coins + 1 + +##### initialization +- 处理边界, 一开始0index的时候, 用value0. +- 中间利用Integer.MAX_VALUE来作比较, initialize dp[x] +- 注意, 一旦 Integer.MAX_VALUE + 1 就会变成负数. 这种情况会在coin=0的时候发生. + +##### Optimization +- 方法1: 直接用Integer.MAX_VALUE +- 方法2: 用-1, 稍微简洁一点, 每次比较dp[i]和 dp[i - coin] + 1, 然后save. 不必要做多次min比较. + +#### Memoization +- dp[i] 依然表示: min # of coints to make amount i +- initialize dp[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录dp[amount] 如果已经给过value, 不要重复计算, 直接return. +- 但是这道题没必要强行做memoization, 普通DP的状态和方程相对来说很好找到 + + + +--- + +**28. [Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### DP +- 求最值, 想到DP. Time/Space O (n) +- 两个特别处: +- 1. 正负数情况, 需要用两个DP array. +- 2. continuous prodct 这个条件决定了在Math.min, Math.max的时候, +- 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. +- 这也就注定了需要一个global variable 来hold result. + +#### Space optimization, rolling array +- maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins +- Time: O(n), space: O(1) + + + +--- + +**29. [3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/3%20Sum%20Closest.java)** Level: Medium Tags: [Array, Two Pointers] + + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**30. [Triangle Count.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangle%20Count.java)** Level: Medium Tags: [Array] + + +其实也就是3sum的变形, 或者而说2sum的变形. 主要用2 pointers来做. +注意, 在选index时候每次定好一个 [0 ~ i], 在这里面找点start, end, 然后i 来组成triangle. +Note巧妙点: +在此之中, 如果一种start/end/i 符合, 那么从这个[start~end]中, 大于start的都可以, 所以我们count+= end-start. +反而言之, nums[i] + + + +--- + +**31. [3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum.java)** Level: Medium Tags: [Array, Two Pointers] + + + +#### sort array, for loop + two pointer. O(n^2) +- 处理duplicate wthin triplets: +- 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. +- 如果是triplet内有重复, 用完start point, 移动到结尾. + +Previous notes: +注意: + 1. 找 value triplets, 多个结果。注意,并非找index。 + 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 + +步骤: + 1. For loop 挑个数字A. + 2. 2Sum 出一堆2个数字的结果 + 3. Cross match 步骤1里面的A. + +时间 O(n^2), 两个nested loop + +另外, 还是可以用HashMap来做2Sum。稍微短点。还是要注意handle duplicates. + + + + +--- + +**32. [Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DP, Tree] + + +Not quite clear. +根据左右分割而总结出了原理, 每次分割, 左右两边都会有一定数量的permutation, 总体上的情况数量当然是相乘. +然后每一个不同的分割点都加一遍: +f(n) = f(0)*f(n-1) + f(1)*f(n-2) + ... + f(n-2)*f(1) + f(n-1)*f(0) + +然后把数学公式转换成DP的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**33. [Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Paths%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +跟unique path的grid一样, 目标走到右下角, 但是grid里面可能有obstacle, 不能跨越. 求unique path 的count. + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- 考虑最终结尾需要的状态:如何组成,写出公式. +- 公式中注意处理能跳掉的block, marked as 1. '到不了', 即为 0 path in dp[i][j]. + + + +--- + +**34. [Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Corrdinate DP +- Space, Time: O(MN) +- dp[i][j] 就是(i, j)上最多能炸掉的enemy数量 +- dp[i][j] 需要从4个方向加起来, 也就是4个方向都要走一遍, 所以分割成 UP/Down/Left/Right 4个 int[][] +- 最后一步的时候求max +- 分方向考虑的方法很容易想到,但是四个方向移动的代码比较繁琐. +- 往上炸: 要从顶向下考虑 +- 往下炸: 要从下向上考虑 +- 熟练写2D array index 的变换. + +似乎还有一个更简洁的方法, 用col count array: http://www.cnblogs.com/grandyang/p/5599289.html + + + +--- + +**35. [3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/3Sum%20Smaller.java)** Level: Medium Tags: [Array, Two Pointers] + + +一般的O(n3)肯定不行。在此基础上优化。 +发现j,k满足条件时候,(k - j)就是所有 sum target, 又因为j不能后退,只能k--,那么问题就被锁定了. 这样可以做到O(n2) + + + +--- + +**36. [Update Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Update%20Bits.java)** Level: Medium Tags: [Bit Manipulation] + + +熟悉bits的一些trick: +- ~0 = -1 = 111111...11111111 (32-bit) +- Create mask by shifting right >>>, and shifting left +- Reverse to get 0000...11110000 format mask +- & 0000 = clean up; | ABC = assign ABC + + + +--- + +**37. [Maximum XOR of Two Numbers in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20XOR%20of%20Two%20Numbers%20in%20an%20Array.java)** Level: Medium Tags: [Bit Manipulation, Trie] + + +比较难想到. 利用到XOR性质A^B=C, then A=B^C. +1. 枚举可能的A, 2. 然后一个个猜. + +1. 枚举A: 因为求MAX肯定是找leading-1最多的数字, 那么枚举A从(1000000...000)开始, +每次多一位取1或者0 +2. 因为枚举A的时候是按照每个bit来, 那么B和C也要以同样数位出现. +这里吧B和C变成了prefix的形式, 放在了set里面. +跟2sum用hashmap的思想类似, 每次用枚举的 A^B=C, 看看结果C是否已经在set里面. +如果在, 证明枚举的A可能被B^C得出, 那么就找到了一种情况. + +还用到一些技巧: +mask = (1 << i); // i位mask +mask = mask | (1 << i); // prefix mask + + + +--- + +**38. [Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)** Level: Medium Tags: [BFS, DP, Math, Partition DP] + + +给一个数字n, 找到这个数字 最少能用多少个 平方数组成. + +平方数比如: 1, 4, 9, 16 ... etc + +#### Partition DP +- 遇到最值, 想到DP. +- 看到分割字眼, 想到分割型 DP. +- 思考, 如果 j * j = 9, 那么 j = 3 就是最少的一步; 但是如果是10呢? 就会分割成1 + 9 = 1 + j * j +- 考虑最后的数字: 要是12割个1出来, 剩下11怎么考虑? 割个4出来,剩下8怎么考虑? +- partion的方式: 在考虑dp[i - x]的时候, x 不是1, 而是 x = j*j. +- 就变成了dp = Min{dp[i - j^2] + 1} + +#### 时间复杂度 +- 乍一看是O(n*sqrt(n)). 实际也是. 但如何推导? +- 考虑上限: 把小的数字变成大的 推导上限; 考虑下限: 把数字整合归小, 找到下限. +- 考虑sqrt(1) + sqrt(2) + ....sqrt(n):找这个的upper bound and lower bound. +- 最后发现它的两边是 A*n*sqrt(n) <= actual time complexity <= B*n*sqrt(n) +- 那么就是O(n*sqrt(n))啦 + +#### BFS +- minus all possible (i*i) and calculate the remain +- if the remain is new, add to queue (use a hashset to mark calculated item) +- find shortest path / lowest level number + +#### Previous Notes +- 一开始没clue.看了一下提示 +- 1. 第一步想到了,从数学角度,可能是从最大的perfect square number开始算起。 +- 2. 然后想法到了dp, 假设最后一步用了最大的maxSqrNum, 那么就在剩下的 dp[i - maxSqrNum^2] +1 不就好了? +- 3. 做了,发现有个问题...最后一步选不选maxSqrNum? 比如12就是个例子。 +- 然后就根据提示,想到BFS。顺的。 把1~maxSqrNum 都试一试。找个最小的。 +- 看我把12拆分的那个example. 那很形象的就是BFS了。 +- 面试时候,如果拆分到这个阶段不确定,那跟面试官陶瓷一下,说不定也就提示BFS了。 + + + +--- + +**39. [Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)** Level: Medium Tags: [Backpack DP, DP] + + +给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法. + +nums 里的数字, 可以重复使用. 不同的order可以算作不同的拼法. + +#### Backpack DP +- dp[i] 表示: # of ways to fill weight i +- 1维: dp[w]: fill weigth w 有多少种方法. 前面有多少种可能性, 就sum多少个: +- dp[w] = sum{dp[w - nums[i]]}, i = 0~n + +##### 分析 +- 拼背包时, 可以有重复item, 所以考虑'最后被放入的哪个unique item' 就没有意义了. +- 背包问题, 永远和weight分不开关系. +- 这里很像coin chagne: 考虑最后被放入的东西的value/weigth, 而不考虑是哪个. + + + + + + +--- + +**40. [Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Search%20Tree%20Iterator.java)** Level: Medium Tags: [BST, Design, Stack, Tree] + + +画一下, BST in order traversal. 用stack记录最小值, 放在top. O(h) space. +每次消耗TreeNode, 都看看rightNode(其实就是下一个最小的candidate), 并且一条龙stack叠上rightNode所有的left子孙. + +Previous Notes: +用O(h)空间的做法: + +理解binary search tree inorder traversal的规律: + 先找left.left.left ....left 到底,这里是加进stack. + 然后考虑parent,然后再right. + +例如这题: + stack里面top,也就是tree最左下角的node先考虑,取名rst. + 其实这个rst拿出来以后, 它也同时是最底层left null的parent,算考虑过了最底层的parent。 + 最后就考虑最底层的parent.right, 也就是rst.right. + +注意: + next()其实有个while loop, 很可能是O(h).题目要求average O(1),所以也是okay的. + + +用O(1)空间的做法:不存stack, 时刻update current为最小值。 + +找下一个最小值,如果current有right child: + 和用stack时的iteration类似,那么再找一遍current.right的left-most child,就是最小值了。 + +如果current没有right child: + 那么就要找current node的右上parent, search in BinarySearchTree from root. + +注意: + 一定要确保找到的parent满足parent.left == current. + 反而言之,如果current是parent的 right child, 那么下一轮就会重新process parent。 + 但是有错:binary search tree里面parent是小于right child的,也就是在之前一步肯定visit过,如此便会死循环。 + + + + +--- + +**41. [Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, Stack] + + +方法1: 用queue, 把需要的item全部打出来 +方法2: 用stack, 把需要的item先存一行, 每次打开子序列时候, 全部加回stack. + + + +--- + +**42. [Best Time to Buy and Sell Stock with Cooldown.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Cooldown.java)** Level: Medium Tags: [DP] + + +Sequence DP +跟StockIII很像. 分析好HaveStock && NoStock的状态, 然后看最后一步. + + + +--- + +**43. [Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)** Level: Medium Tags: [Array, Binary Search] + + +binary search. +Goal: find peak, where both sides are descending +最左边, 最右边是Integer.MIN_VALUE时候, 也能构成中间数mid是peak的条件. + +Note: +没有必要特别check (mid-1)<0或者(mid+1)>=n. +证明: +1. 最左端: 当start=0, end = 2 => mid = 1, mid-1 = 0; +2. 最右端: 当end = n - 1, start = n - 3; mid = (start+end)/2 = n - 2; +那么mid + 1 = n - 2 + 1 = n - 1 < n 是理所当然的 + + + +--- + +**44. [Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP] + + +给两个string, A, B. 找这两个string里面的LCS: 最长公共字符长度 (不需要是continuous substring) + +#### Double Sequence DP +- 设定dp长度为(n+1), 因为dp[i]要用来表示前i个(ith)时候的状态, 所以长度需要时i+1才可以在i位置, hold住i. +- 双序列: 两个sequence之间的关系, 都是从末尾字符看起, 分析2种情况: +- 1. A最后字符不在common sequence 或者 B最后字符不在common sequence. +- 2. A/B最后字符都在common sequence. 总体count + 1. + + + +--- + +**45. [Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)** Level: Medium Tags: [Backtracking, String] + + +方法1: Iterative with BFS using queue. + +方法2: Recursively adding chars per digit + + + +--- + +**46. [Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)** Level: Medium Tags: [Binary Search, Math] + + +傻做就O(n), 要更好就考虑O(logN). +减少重复计算, 一切两半. + +注意: +- n/2的奇数偶数 +- n的正负 +- n == 0的情况, x == 0, x == 1 的情况. + + +--- + +**47. [Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS +- 和Construct from Inorder && Postorder 想法一样。 +- 写出Preorder和Inorder的字母例子,发现Preorder的开头总是这Level的root。依此写helper,注意处理index。 +- 跟Convert Sorted Array to Binary Tree类似, 找到对应的index, 然后: +- node.left = dfs(...), node.right = dfs(...) +- Divide and Conquer +- optimize on finding `mid node`: given value, find mid of inorder: +- Instead of searching linearly, just store inorder sequence in `map index>`, O(1) +- IMPORATANT: the mid from inorder sequence will become the main baseline to tell range: +- `range of subTree = (mid - inStart)` +- sapce: O(n), time: O(n) access + + + +--- + +**48. [Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. +遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + +跟Add Binary的理解方式一模一样. + +注意: +Linked List 没有天然size. +用DummyNode(-1).next来hold住结果. + + + + +--- + +**49. [Add Two Numbers II.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers%20II.java)** Level: Medium Tags: [Linked List] + + +Singly-linked list需要reverse, 用stack. +最终结果要恢复成input list 那样的sequence方向, 用stack一个个pop()刚好就可以做到. + +加法都一样: + 1. sum = carry + 2. carry = sum / 10 + 3. sum = sum % 10; + + + +--- + +**50. [Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Balanced%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个binary tree, 看是否是height-balanced + +#### DFS +- DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. +- 一旦有 <0 或者差值大于1, 就全部返回Integer.MIN_VALUE. Integer.MIN_VALUE比较极端, 确保结果的正确性。 +- 最后比较返回结果是不是<0. 是<0,那就false. +- Traverse 整个tree, O(n) + + +#### DFS, maxDepth function +- Same concept as in 1, but cost more traversal efforts. + + + +--- + +**51. [Populating Next Right Pointers in Each Node.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +给一个特殊的binary tree, treeNode里面有一个 next pointer. + +写一个function, 把所有node都更同level的node 连在一起. 最右边的node.next = NULL + +#### DFS + Divide and Conquer +- 题目要求DFS. 想清楚了如何在DFS level把几种情况都考虑了, 写起来很简单. NOT BFS, because requires O(1) space +- 对于一个root来说, 只有几个点可以顾忌到: root.left, root.right, root.next. +- 想办法把这三个方向的点, 能连起来的都连起来: +- 1. `node.left.next = node.right` +- 2. If `node.next != null`, link `node.right.next = node.next.left`; +- 然后在dfs(root.left), dfs(root.right) +- Time: visit && connect all nodes, O(n) + +#### BFS +- 不和题意,用了queue space,与Input成正比。太大。 +- BFS over Tree。 用Queue 和 queue.size(),老规矩。 +- process每层queue时, 注意把next pointer加上去就好. + + + +--- + +**52. [Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +如题, 验证是否是BST. + +#### DFS +- 查看每个parent-child关系: leftchild < root < rightChild; +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest +- imagine we know the two extreme border: Integer.MIN_VALUE, Integer.MAX_VALUE; pass node around and compare node vs. node.parent. +- 方法: 把root.val 传下来作为 max 或者 min, 然后检查children +- + +##### Note: +- min/max需要时long type. +- 如果题目真的给node.val = Integer.MAX_VALUE, 我们需要能够与之比较, long就可以. + + + +--- + +**53. [Convert Sorted List to Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Sorted%20List%20to%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List] + + +如题, 把一个sorted singly linked list 转换成一个 height balanced BST + +#### DFS +- Divide and Conquer +- 找到mid node +- 然后分割两半, 分别dfs做各自两个subtree: node.left,node.right +- 用长度来定位mid, 每次找中间点做root, 然后前半段, 后半段分别dfs with length. +- 用快慢pointer 找到mid. Better: 不用traverse entire linked list + +#### Details +- slowPointer = node; +- fastPointer = node.next; +- 然后把root = mid.next +- 然后开始sortedListToBST(mid.next.next); //后半段 +- mid.next = null;//非常重要,要把后面拍过序的断掉 +- sortedListToBST(head); //从头开始的前半段 +- 最后root.left, root.right merge一下。 + + + +--- + +**54. [Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### DFS +- 分析题意后, 按照题意: Flatten the tree, no extra space. +- 1. reserve right child: `reservedRightNode` +- 2. Connect `root.right = root.left`, DFS flatten(root.right) +- 3. 移花接木, coneect end of list -> reservedRightNode +- 4. flatten 剩下的. root.right... + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**55. [Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)** Level: Medium Tags: [Array, Binary Search, Subarray, Two Pointers] + +time: O(n) +space: O(1) + +给一串positive integer, 找最短的subarray sum, where the sum >= s + +#### Two pointer +- 全部是positive integer, 那么preSum一定是增长的. +- 那其实就用two pointer: `start=0, end=0` 不断往前移动. 策略: +- 1. end++ until a solution where sum >= s is reached +- 2. 然后移动start; 记录每个solution, Math.min(min, end - start); +- 3. 然后再移动end,往下找 +- Note: 虽然一眼看上去是nested loop.但是分析后,发现其实就是按照end pointer移动的Loop。start每次移动一格。总体上,还是O(n) + +#### Binary Search +- O(nlogn) NOT DONE. + +#### Double For loop +- O(n^2), inefficient + + + +--- + +**56. [Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)** Level: Medium Tags: [Hash Table, String, Two Pointers] + + +方法1: +Two Pointers +双指针: 从start开始遍历, 但是第一步是while loop来推进end; 直到推不动end, 然后start++ +巧妙点: 因为end是外围variable, 在start的loop上, end不会重置;[star ~ end] 中间不需要重复计算. +最终可以O(n); + +Previous verison of two pointers: +用两个pointer, head和i. +注意:head很可能被退回到很早的地方,比如abbbbbba,当遇到第二个a,head竟然变成了 head = 0+1 = 1. +当然这是不对的,所以head要确保一直增长,不回溯 + +方法2: + HashMap: + 一旦有重复, rest map. + 没有重复时候, 不断map.put(), 然后求max值 + +问题: 每次reset map之后就开始从新从一个最早的index计算, 最坏情况是O(n^2): +'abcdef....xyza' + + + + +--- + +**57. [Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +O(n), one pace, no extra space +找到窗口, 然后平移, 最后pre 和 head之间 skip一个node就好. + + + +--- + +**58. [Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Linked List, Math, Two Pointers] + + +LinkedList 里面有 cycle, 找到cycle的起始点(第一个重复出现的element). + +#### Slow, fast Pointer +- 快慢指针, O(1)space. +- 1. 确认有cycle后 2. 数学问题:找到开头. +- 当head == slow.next时候, head就是cycle starting point. +- 也就是说,当slow 移动到了那个回溯点,slow.next那个点就刚好是head的那个点... + +#### 证明 +- 1. 假设慢指针走t步, 快指针走快一倍, 也就是2t. +- 2. 我们假设cycle的长度是Y, 而进入cycle之前的长度为X. +- 3. 假设慢指针走了m圈cycle, 而快指针走了n圈cycle之后, 两个pointer相遇. +- 4. 最终在Y cycle里面的K点相遇, 也就是两个指针都在这最后一圈里面走了K 步. +- 那么: +- t = X + mY + K +- 2t = X + nY + K +- 整合公式: X + K = (n - 2m)Y +- 这里的m和n不过是整数的跑圈数, 也就是说X和K加在一起, 总归是结束cycle. X 和 K 互补 +- 结论: 当slow/fast 指针在K点相遇后, 再走X步, 就到了cycle的起点, 也就是题目要求的起点. + +#### Hash Table, O(n) space + + + + +--- + +**59. [Kth Smallest Element in a Sorted Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20Sorted%20Matrix.java)** Level: Medium Tags: [Binary Search, Heap] + +time: O(n + klogn) +space: O(n) + +给一个sorted matrix, 找kth smallest number (not distinct) + +Related: `Kth Largest Element in an Array` + +#### PriorityQueue +- 和Merge K sorted Array/ List 类似:使用PriorityQueue。 +- 因为Array的element无法直接找到next,所以用一个class Node 存value, x,y positions. +- Initial O(n) time, also find k O(k), sort O(logn) => O(n + klogn) +- 变型: Kth Largest in N Arrays + +#### Binary Search +- we know where the boundary is start/end are the min/max value. +- locate the kth smallest item (x, y) by cutt off partition in binary fasion: +- find mid-value, and count # of items < mid-value based on the ascending matrix +- O(nlogn) + + + + +--- + +**60. [Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +画图, 最小值被rotate之后, 变成array中间的最低谷. +并且, 左边山坡的最小值, 大于右边山坡的最大值. +以此来给binary search做判断. + +O(nlogn) + + + +--- + +**61. [Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)** Level: Medium Tags: [Union Find] + + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + + + +--- + +**62. [Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)** Level: Medium Tags: [Union Find] + + +Lint还不能跑, 全部按照题意和答案document的. + + + +--- + +**63. [Connecting Graph III.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20III.java)** Level: Medium Tags: [Union Find] + + +还是UnionFind的变形, 这次是算有剩下多少个union. 其实非常简单, 维持一个全局变量count: +一开始count=n, 因为全是散装elements; 每次union, count--. + + + +--- + +**64. [Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### DFS +- More or less like a graph problem: visit all nodes connected with the starting node. +- top level 有一个 double for loop, 查看每一个点. +- 每当遇到1, count+1, 然后DFS helper function 把每个跟这个当下island 相关的都Mark成 '0' +- 这样确保每个visited 过得island都被清扫干净 +- O(mn) time, visit all nodes + +#### Union Find +- 可以用union-find, 就像Number of island II 一样. +- 只不过这个不Return list, 而只是# of islands +- Union Find is independent from the problem: it models the union status of integers. +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + + + +--- + +**65. [Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2D board, 里面是 'X' 和 'O'. 把所有被X包围的area都涂成'X'. + +从四个边的edge出发, 像感染僵尸病毒一样扩散, 把靠边的node全部mark, 然后将还是'O'的改成X, 最后回复marks -> 'O' + +#### Union Find +- UnionFind里面这次用到了一个rank的概念, 需要review. rank[] 也就是在tracking每一个node所在union的size. +- 目的是: always并到大的union里面 +- note: 将2D coordinate (x,y) 转换成1D: index = x * n + y + +#### DFS: mark all invalid 'O' +- Reversed thinking: find surrounded nodes, how about filter out border nodes && their connections? +- Need to traverse all the border nodes, consider dfs, visit all. +- loop over border: find any 'O', and dfs to find all connected nodes, mark them as 'M' +- time: O(mn) loop over all nodes to replace remaining 'O' with 'X' + +#### DFS: mark all valid 'O' +- More like a graph problem: traverse all 'O' spots, and mark as visited int[][] with area count [1 -> some number] +- Run dfs as top->bottom: mark area count and dsf into next level +- End condition: if any 'O' reaches border, mark the global map +- keep dfs untill all connected nodes are visited. +- At the end, O(mn) loop over the matrix and mark 'X' for all the true area from map. +- Practice: write code to verify + +### BFS +- TODO + + + +--- + +**66. [Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- HashMap构建Trie. Trie三个Method: +- 1. Inset: 加 word +- 2. Search: 找word +- 3. StartWith: 找prefix + +##### 特点 +- 只有两条children的是binary tree. 那么多个children就是Trie +- 那么没有left/right pointer怎么找孩子? +- 用HashMap,以child的label为Key,value就是child node。 HashMap走位 + +##### 其他 +- node里的char在这是optional. 只要在每个TrieNode里面用map存储向下分布的children就好了. +- 另外有种题目,比如是跟其他种类的search相关,在结尾要return whole string,就可以在node里存一个up-to-this-point的String。 + +##### Previous Note +- 如果是遇到一个一个字查询的题,可以考虑一下。 +- 构建TrieNode的时候要注意:如何找孩子?如果是个map的话,其实就挺好走位的。 +- 而且,每个node里面的 char 或者string有时候用处不大, +- 可以为空。但是有些题目,比如在结尾要return一些什么String,就可以在end string那边存一个真的String。 + + + + + +--- + +**67. [Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +Trie结构, prefix tree的变形: '.'可以代替任何字符,那么就要iterate这个node所有的children. + +节点里面有char, isEnd, HashMap +Build trie = Insert word:没node就加,有node就移动。 +Search word:没有node就报错. 到结尾return true + +这题因为'.'可以代替任何possible的字符,没一种都是一个新的path,所以recursive做比较好些。 +(iterative就要queue了,麻烦点) + + + +--- + +**68. [Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)** Level: Medium Tags: [Array, Backtracking, DFS] + + +#### DFS, Backtracking: +- 找到开头的字母, 然后recursively DFS 去把word串到底: +- 每到一个字母, 朝四个方向走, 之中一个true就可以. +- Note:每次到一个字母,mark一下'#'. 4个path recurse回来后,mark it back. + +#### Note: other ways of marking visited: +- 用一个boolean visited[][] +- Use hash map, key = x@y + + + + +--- + +**69. [Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)** Level: Medium Tags: [DFS, Divide and Conquer, Stack] + + +给一个expression string. 里面包括数字, 字母, 括号. 其中数字代表括号里面的内容重复几次. + +括号里面可以是String, 也可能是expression. + +目的: 把expression展开成一个正常的String. + + +#### Stack, Iteratively +- Process inner item first: last come, first serve, use stack. +- Record number globally and only use it when '[' is met. +- Stack存 [ ] 里面的内容, detect 括号开头结尾: 结尾时process inner string +- 有很多需要注意的细节才能做对: +- Stack 也可以用, 每个地方要注意 cast. 存进去的需要是Object: String, Integer +- 几个 type check: instanceof String, Character.isDigit(x), Integer.valueOf(int num) +- 出结果时候: `sb.insert(0, stack.pop())` + + +#### DFS +- Bottom->up: find deepest inner string first and expand from inside of `[ ]` +- 与Stack时需要考虑的一些function类似. 特别之处: **检查`[ ]`的结尾** +- 因为DFS时候, 括号里的substring会被保留着进入下一个level, 所以我们在base level要keep track of substring. +- 用int paren 来track 括号的开合, 当paren再次==0的时候 找到closure ']' +- 其他时候, 都要继续 append to substring + + + + +--- + +**70. [Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)** Level: Medium Tags: [Stack, Tree] + + +给一串数字, 做一个 maximum binary tree: 最顶上的root最大; 左child也是一个max tree, 右child也必须是max tree. + +#### Monotonous Stack +- 用到bottom->top递减的stack: 最底下的root维持成最大的element. +- 过程当中, 一旦遇到currNode.val > stack.peek(), 就意味着需要把这个currNode放在 stack的底层位置. +- 也就是说, 遇到这个条件, process, pop()所有 currNode.val > stack.peek(), 最后把currNode加进去. + +- maxTree题目本身的要求是: 大的在最中间, 左右两边的subTree也要是maxTree: +- Monotonous Stack在这里帮助 keep/track of max value, 但是left/right tree的logic是MaxTree独有的. +- left/right node的assignment是根据题目要求: 中间最大值分开后, 左边的是左边subTree, 右边的作为右边subTree. + +#### Previous notes +- Should memorize MaxTree. 依次类推,会做Min-Tree, Expression Tree +- Stack里,最大的值在下面。利用此性质,有这样几个step: + +##### Step1 +- 把所有小于curr node的,全Pop出来, while loop, keep it going. +- 最后pop出的这个小于Curr的node:它同时也是stack里面pop出来小于curr的最大的一个,最接近curr大小。(因为这个stack最大值靠下面) +- 把这个最大的小于curr的node放在curr.left. + +##### Step2 +- 那么,接下去stack里面的一定是大于curr: +- 那就变成curr的left parent. set stack.peek().right = curr. + +##### Step3 +- 结尾:stack底部一定是最大的那个,也就是max tree的头。 + + + + + +--- + +**71. [Swap Nodes in Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Nodes%20in%20Pairs.java)** Level: Medium Tags: [Linked List] + + +#### enumurate +基本原理, 写出来, 然后连线: +pre -> A -> B -> C -> ... +需要一个虚拟 preNode做起始node, 不然无法把后面的node换到开头. + +#### 注意 +用dummy = pre作为head前一格. +用 `pre.next == null && pre.next.next` 来check是否为NULL. +pre.next.next 保证了至少有一次swap. + + + +--- + +**72. [Wood Cut.java](https://github.com/awangdev/LintCode/blob/master/Java/Wood%20Cut.java)** Level: Medium Tags: [Binary Search] + + +二分的思想: 判断的是一个 validate() function, 而不是简单的'==' + +不需要sort! 为什么呢? 因为我们不是在given array上面二分, 我们是根据最大值在[0, max]上二分. + +Overall time: O(nLogM), where M = largest wood length + + + +--- + +**73. [Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Two Pointers] + + +- 注意不要思维定式: 以为mid是index +- 这里mid其实是binary search on value [1, n] 的一个value. +- 再次用到validate() function + +Time: O(nLogN) + + + +--- + +**74. [Game of Life.java](https://github.com/awangdev/LintCode/blob/master/Java/Game%20of%20Life.java)** Level: Medium Tags: [Array] + + +#### basic +- 简单的implementation, 把count function写清楚就好. +- time: O(mn), extra space: O(mn) +- 注意结尾要一个个board[i][j]copy + +#### follow up +unlimited border? 可能需要分割board. 用大框分割, 每次换行的时候, 重复计算2行就好了. see details below. + +#### improvement: do it in place! +- time: O(mn), extra space: O(1) +- bit manipulation: 用第二个bit来存previous value. +- 因为我们只考虑 0 和 1 而已, 所以可以这样取巧. 但是并不scalable. +- 如果需要存multiple state, 可能需要移动更多位, 或者用一个 state map +- 注意 bit manipulation 的细节: <<1, >>1, 还有 mast的用法: |, & + + + + +--- + +**75. [Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)** Level: Medium Tags: [Array, Interval, PriorityQueue, Sort, Sweep Line] + + +#### Sweep Line +- 把Interval拆分成数轴上的Point +- 起飞mark 1 +- 降落mark -1 +- 用PriorityQueue排序, loop through queue, 计算(起飞+降落)值可能有的max。 + +#### 注意 +- 同时起飞和降落,就是 1 - 1 = 0. 所以在while loop里面有第二个while loop, +- 当坐标x重合时,在这里做完所有x点的加减,然后再比较 max。 +- 这避免了错误多count,或者少count + + + +--- + +**76. [Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### PriorityQueue +- PriorityQueue + 一个Class来解决.O(nlogn) +- 跟 Number of Airpline in the sky是同一道题 + +#### 方法2: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**77. [Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +##### DP +- 计数DP.注意方程式前两位置加在一起: 前两种情况没有overlap, 也不会缺情况. +- 注意initialization, 归1. +- 需要initialize的原因是,也是一个reminder: 在方程中会出现-1index +- Of course, row i = 0, or col j = 0, there is only 1 way to access +- time O(mn), space O(mn) + +##### 滚动数组 +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + + + + +--- + +**78. [Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Square.java)** Level: Medium Tags: [Coordinate DP, DP] + + +只能往右边,下面走, 找面积最大的 square. 也就是找到变最长的 square. + +#### DP +- 正方形, 需要每条边都是一样长度. +- 以右下角为考虑点, 必须满足条件: left/up/diagonal的点都是1 +- 并且, 如果三个点分别都衍生向三个方向, 那么最长的 square 边就是他们之中的最短边 (受最短边限制) +- dp[i][j]: max square length when reached at (i, j), from the 3 possible directions +- dp[i][j] = Math.min(Math.min(dp[i - 1][j], dp[i][j - 1]), dp[i - 1][j - 1]) + 1; +- Space, time O(mn) + +##### init +每个点都可能是边长1, 如果 matrix[i][j] == '1' + +##### 滚动数组 +[i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + +--- + +**79. [Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)** Level: Medium Tags: [DP, Game Theory, Greedy] + + +拿棋子游戏, 每个人可以拿1个或者2个, 拿走最后一个子儿的输. 问: 根据给的棋子输, 是否能确定先手的输赢? + +Game Theory: 如果我要赢, 后手得到的局面一定要'有输的可能'. + +#### DP, Game Theory +- 要赢, 必须保证对手拿到棋盘时, 在所有他可走的情况中, '有可能败', 那就足够. +- 设计dp[i]:表示我面对i个coins的局面时是否能赢, 取决于我拿掉1个,或者2个时, 对手是不是会可能输? +- dp[i] = !dp[i - 1] || !dp[i-2] +- 时间: O(n), 空间O(n) +- 博弈问题, 常从'我的第一步'角度分析, 因为此时局面最简单. + +#### Rolling Array +空间优化O(1). Rolling array, %2 + + + +--- + +**80. [Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)** Level: Medium Tags: [Array, DP, Game Theory, Memoization, MiniMax] + + +给一串coins, 用values[]表示; 每个coin有自己的value. 先手/后手博弈, +每次只能 按照从左到右的顺序, 拿1个或者2个棋子, 最后看谁拿的总值最大. + +MiniMax的思考方法很神奇, 最后写出来的表达式很简单 + +#### DP, Game Theory 自考过程比较长 +- 跟Coins in a line I 不一样: 每个coin的value不同. +- 用到MiniMax的思想, 这里其实是MaxiMin. Reference: http://www.cnblogs.com/grandyang/p/5864323.html +- Goal: 使得player拿到的coins value 最大化. +- 设定dp[i]: 从index i 到 index n的最大值. 所以dp[0]就是我们先手在[0 ~ n]的最大取值 +- 于此同时, 你的对手playerB也想最大化, 而你的选择又不得不被对手的选择所牵制. +- 用MaxiMin的思想, 我们假设一个当下的状态, 假想对手playerB会做什么反应(从对手角度, 如何让我输) +- 在劣势中(对手让我输的目标下)找到最大的coins value sum + + +##### 推算表达式 +- Reference里面详细介绍了表达式如何推到出来, 简而言之: +- 如果我选了i, 那么对手就只能选(i+1), (i+2) 两个位置, 而我在对方掌控时的局面就是min(dp[i+2], dp[i+3]) +- 如果我选了i和(i+1), 那么对手就只能选(i+2), (i+3) 两个位置, 而我在对方掌控时的局面就是min(dp[i+3], dp[i+4]) +- 大家都是可选1个或者2个coins +- 目标是maximize上面两个最坏情况中的最好结果 + +##### 简化表达式 +- 更加简化一点: 如果我是先手, dp[i]代表我的最大值. +- 取决于我拿了[i], 还是[i] + [i+1], 对手可能是dp[i + 1], 或者是dp[i+2] +- 其实dp[i] = Math.max(sum - dp[i + 1], sum - dp[i + 2]); +- 这里的sum[i] = [i ~ n] 的sum, 减去dp[i+1], 剩下就是dp[i]的值没错了 + +##### Initialization +- 这个做法是从最后往前推的, 注意initialize dp末尾的值. +- dp = new int[n + 1]; dp[n] = 0; // [n ~ n]啥也不选的时候, 为0. +- sum = new int[n + 1]; sum[n] = 0; // 啥也不选的时候, 自然等于0 +- 然后记得initialize (n-1), (n-2) + +##### 时间/空间 +Time O(n) +Space O(n): dp[], sum[] + + + +--- + +**81. [Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Recursive +trivial, 先加left recursively, 再加right recursively, 然后组成头部. + +#### Stack +- 双stack的思想, 需要在图纸上画一画 +- 原本需要的顺序是: 先leftChild, rightChild, currNode. +- 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild +- 这样出来的结果是reverse的, 那么翻转一下就可以了. +- v1做的时候用了stack1, stack2, 因为根据这个双stack的思想而来 +- v2简化, 可以放在一个stack里面, 每次record result 的时候: rst.add(0, item); + +##### 利用stack的特点 +- 每次加element进stack的时候, 想要在 bottom/后process的, 先加 +- 想要下一轮立刻process的, 最后push进stack. + +##### 注意 +这些binary tree traversal的题目.常常有多个做法:recursive or iterative + + + +--- + +**82. [Compare Version Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Version%20Numbers.java)** Level: Medium Tags: [String] + + +给两串version number, 由数字和'.' 组成. 比较先后顺序. + +If version1 > version2 return 1, if version1 < version2 return -1, otherwise return 0. + +#### divide and conquer +- 用 str.split("\\.") 分割string +- Convert成integer, 逐个击破 + +#### 注意 +- '1.0' 和 '0' 是相等的 +- 如果可以假设version integer都是valid, 直接Integer.parseInt()就可以了 +- 不然的话, 可以compare string + + + +--- + +**83. [Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样, 如果一样, 直接 2 ^ h - 1就好 +- 不一样的话, 再DFS + +##### Trick +- 直接DFS会timeout, O(n), 其实可以optimize +- to pass the test with O(h^2), 位运算: Math.pow(2, h) = 2 << (h - 1). 神奇! +- 2 << 1就是把所有bits往左移动一位, 也就是 * 2 + +#### Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + + + +--- + +**84. [Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] + +#### Topological Sort +- 给一个graph of nodes +- 至关重要: 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 目标是根据edge 的 direction, 把这个graph 里面的 node sort 一个list +- 如果有cycle, 这个item就不会被放在最后的list 里面. +- 比如: 如果两个课互相是dependency, 就变成了cyclic dependency, 这样不好. + + +#### BFS +- Kahn algorithem: +- 先build一个graph map: ; or `List[] edges; edges[i] = new ArrayList<>();` +- count in-degree: inDegree就是每个node上面, **有多少个走进来的edge**? +- **IMPORTANT**: always initialize inDegree map/array with 0 +- 那些没有 in-coming-edge的, indegree 其实就 等于 0, 那么他们就应该在final result list里面 +- 对这些 indegree == 0 的 nodes BFS, add to queue. +- visit queue 上每个 node: count++, also add this curr node to sorted list +- Check all neighbors/edges of curr node: 如果visit过了, 这个node上的 indegree-- +- 如果 indegree == 0, add this node to queue. + +##### Indegree 原理 +- Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. +- Remember: **indegree是周围的node到我这里的次数count** +- 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### DFS +- 这道题没有要求作出final list, 相对简单, 只要visit每个nodes, 最后确认没有cycle就好了 +- 用 visited int[] 来确认是否有cycle. 1 代表 paretNode visited, -1 代表在DFS上一行的标记 +- 如果遇到-1, 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. +- 走完一个node的所有neighbor, 都没有fail, 那么backtracking, set visited[i] = 1 +- 真的topo sort会在DFS的底端, 把record放进一个stack, 最后reverse, 就是真的sort order. + +#### Notes: +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map +- List[]的list, 其实是default List + +#### Previous notes +有点绕,但是做过一次就明白一点。 +是topological sort的题目。一般都是给有dependency的东西排序。 + +最终都会到一个sink node, 再不会有向后的dependency, 在那个点截止。 +我就已这样子的点为map的key, 然后value是以这个node为prerequisite的 list of courses. + +画个图的话,prerequisite都是指向那个sink node, 然后我们在组成map的时候,都是从sink node 发散回来到dependent nodes. + +在DFS里面,我们是反向的, 然后,最先完全visited的那个node, 肯定是最左边的node了,它被mark的seq也是最高的。 + +而我们的sink node,当它所有的支线都visit完了,seq肯定都已经减到最小了,也就是0,它就是第一个被visit的。 + + +最终结果: +每个有pre-requisit的node都trace上去(自底向上),并且都没有发现cycle.也就说明schedule可以用了。 + + + +--- + +**85. [Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] +- 做法跟Course Schedule I 非常像, 可以参考. + +#### Topological Sort, Indegree, BFS +- 用`List[] edges; edges[i] = new ArrayList<>();` 来表示graph: 就是每个node, to all its neighbors +- 每个没有 inDegree==0 node, 都是可以加进 final list里面的. 比如一开始找到的那些 inDegree = 0的 node +- 注意, 如果 prerequisites = [], 那么就是说这些课都independent, 开个int[0 ~ n-1]的数组并赋值就好. +- 如果有cycle, 严格意义上就做不了topological sort, 也无法涵盖所有nodes, 那么return [ ] + +#### DFS +- 根据 Course Schedule 里面的DFS 修改 +- 维持visited int[]全局变量 +- 维持sortedList int[] 全局变量, 注意加进去的时候是 add(0, node) 加在开头这样 +- 每次到一个node的children全部DFS走完之后, 就可以把他加进final list里面 +- 如果有cycle, 也就是dfs return false的时候, 这个题目判定排课失败, return new int[] { } + + + +--- + +**86. [Contains Duplicate III.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20III.java)** Level: Medium Tags: [BST] + + +给一个unsorted array, 问, 是否有两个element, value相差最大为t, 而两个element的index 相差最大为k. + +Note: 虽然题目名字是Contains Duplicate, 但其实要找的两个element不是duplicate, 而是Math.abs(value1 - value2) <= t. + +#### TreeSet +- TreeSet还是一个set, 我们用来装已经visit过得item +- 如果window大小超过K, 那么把nums[i - k - 1] 去掉, 并且加上新的element +- 这里有个公式推算: (Math.abs(A-B) <= t) =>>>>> (-t <= A - B <= t) =>>>>>> A >= B - t, A <= B + t +- 也就是说, 如果对于 B = nums[i], 来说, 能找到一个target A, 满足上面的公式, 那么就可以 return true. +- Time O(nLogk), treeSet的大小不会超过k, 而 treeSet.ceiling(), treeSet.add(), treeSet.remove() 都是 O(logK) +- Space O(k) + +#### Note +- 与Contains Duplicate II 类似概念. TreeSet有BST 因此可以直接用, 而不用自己构建BST +- 简化题目里面的重要条件 Math.abs(A-B) <= t 而推断出 A >= B - t, A <= B + t +- 并且需要需要用 TreeSet.ceiling(x): return number greater or equal to x. 这个用法要记住吧, 没别的捷径. + + + +--- + +**87. [Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game.java)** Level: Medium Tags: [Array, DP, Greedy] + + +给出步数,看能不能jump to end. + +#### Greedy - start from index = 0 +- Keep track of farest can go +- 一旦 farest >= nums.length - 1, 也就是到了头, 就可以停止, return true. +- 一旦 farest <= i, 也就是说, 在i点上, 已经走过了步数, 不能再往前跳, 于是 return false +- This can be done using DP. However, greedy algorithm is fast in this particular problem. + +#### Greedy - start from index = n - 1 +- greedy: start from end, and mark last index +- loop from i = [n - 2 -> 0], where i + nums[i] should always >= last index +- check if last == 0 when returning. It means: can we jump from index=0 to the end? +- Time: O(n), beat 100% + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (A[j] >= i - j), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- Time: O(n^2) + + + + +--- + +**88. [Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/Coin%20Change%202.java)** Level: Medium Tags: [Backpack DP, DP] + + +给串数字, target amount, 求总共多少种方式可以reach the amount. + +#### DP +- O(MN): M, total target amount; N: size of coins +- 类似于: 网格dp, unique path 里面的2种走法: 从上到下, 从左到右 +- 状态: dp[i]: sum of ways that coins can add up to i. +- Function: dp[j] += dp[j - coins[i]]; +- Init: dp[0] = 1 for ease of calculation; other dp[i] = 0 by default +- note: 避免重复count, 所以 j = coins[i] as start +- 注意 coins 需要放在for loop 外面, 主导换coin的流程, 每个coin可以用无数次, 所以在每一个sum value上都尝试用一次每个coin + +#### knapsack problem: backpack problem + + + +--- + +**89. [Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Partition DP +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- 确定末尾的2种状态: single letter or combos. 然后计算出单个letter的情况, 和双数的情况 +- 定义`dp[i] = 前i个digits最多有多少种decode的方法`. new dp[n + 1]. +- 加法原理: 把不同的情况, single-digit, double-digit 的情况加起来 +- dp[i] += dp[i - x], where x = 1, 2 +- note: calculate number from characters, need to - '0' to get the correct integer mapping. +- 注意: check value != '0', 因为'0' 不在条件之中(A-Z) +- Space, Time O(n) + +#### 引申 +- 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + + + +--- + +**90. [Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Path%20Sum.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +#### DP +- Time, Space O(MN) +- 往右下角走, 计算最短的 path sum. 典型的坐标型. +- 注意: init 第一行的时候, 要accumulate dp[0][j - 1] + grid[i][j], 而不是单纯assign grid[i][j] + +#### Rolling Array +- Time O(MN), Space O(1) +- 需要在同一个for loop里面完成initialization, 和使用dp[i][j] +- 原因: dp[i % 2][j] 在被计算出来的时候, 是几乎马上在下一轮是要被用的; 被覆盖前不备用,就白算 +- 如果按照第一种方法, 在开始initialize dp, 看起来固然简单, 但是不方便空间优化 + + + +--- + +**91. [Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)** Level: Medium Tags: [Bit Manipulation, Bitwise DP, DP] + + +给一个数组, 算里面有多少bit 1. + +#### Bitwise DP +- 对于每一个数字, 其实很简单就能算出来: 每次 >>1, 然后 & 1 就可以count 1s. Time: 一个数字可以 >>1 O(logN) 次 +- 现在要对[0 ~ num] 都计算, 也就是N个数字, 时间复杂度: O(nLogN). +- 用DP来优化, 查找过的number的1s count, 存下来在 dp[number]里面. +- 计算你顺序从 0 -> num, count过的数字就可以重复利用. +- Bit题目 用num的数值本身表示DP的状态. +- 这里, dp[i] 并不是和 dp[i-1]有逻辑关系; 而是dp[i] 和dp[i>>1], 从binary representation看出有直接关系. + + + +--- + +**92. [Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### DP +- O(n^2) +- 需要记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. +- dp[i] = dp[i - 1] + nums[i]; +- 最后移动, 作比较 + +#### 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- 验证 + + + +--- + +**93. [House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)** Level: Medium Tags: [DP, Sequence DP, Status DP] + + +和House Robber I 类似, 搜刮房子, 相邻不能动. 特点是: 现在nums排成了圈, 首尾相连. + +#### Sequence DP +- dp[i][status]: 在 status=[0,1] 情况下, 前i个 房子拿到的 max rob gain. status=0, 1st house robbed; status=1, 1st house skipped +- 根据dp[i-1]是否被rob来讨论dp[i]: dp[i] = Math.max(dp[i-1], dp[i - 2] + nums[i - 1]); +- 特别的是,末尾的last house 和 first house相连. 这里就需要分别讨论两种情况: 第一个房子被搜刮, 或者第一个房子没被搜刮 +- be careful with edge case nums = [0], only with 1 element. +- Time,space: O(n) + +#### 两个状态 +- 是否搜刮了第一个房子, 分出两个branch, 可以看做两种状态. +- 可以考虑用两个DP array; 也可以加一dp维度, 补充这个状态. +- 连个维度表示的是2种状态(1st house being robbed or not); 这两种状态是平行世界的两种状态, 互不相关. + +#### Rolling array +- 与House Robber I一样, 可以用%2 来操作rolling array, space reduced to O(1) + + + +--- + +**94. [House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)** Level: Medium Tags: [DFS, DP, Status DP, Tree] + + +Houses被arrange成了binary tree, 规则还是一样, 连续相连的房子不能同时抄. + +求Binary Tree neighbor max 能抄多少. + +#### DFS +- 判断当下的node是否被采用,用一个boolean来表示. +- 如果curr node被采用,那么下面的child一定不能被采用. +- 如果curr node不被采用,那么下面的children有可能被采用,但也可能略过,所以这里用Math.max() 比较一下两种可能有的dfs结果。 +- dfs重复计算:每个root都有4种dive in的可能性, 假设level高度是h, 那么时间O(4^(h)), where h = logN, 也就是O(n^2) + +#### DP, DFS +- 并不是单纯的DP, 是在发现DFS很费劲后, 想能不能代替一些重复计算? +- 基本思想是dfs解法一致: 取root找最大值, 或者不取root找最大值 +- 在root上DFS, 不在dfs进入前分叉; 每一个level按照状态来存相应的值: dp[0] root not picked, dp[1] root picked. +- Optimization: DP里面, 一口气找leftDP[]会dfs到最底层, 然后自下向上做计算 +- 这个过程里面, 因为没有在外面给dfs()分叉, 计算就不会重叠, 再也不用回去visit most-left-leaf了, 算过一遍就完事. +- 然而, 普通没有dp的dfs, 在算完visited的情况下的dfs, 还要重新dfs一遍!visited的情况. +- Space O(h), time O(n), 或者说是O(2^h), where h = log(n) + +#### DP 特点 +- 不为状态而分叉dfs +- 把不同状态model成dp +- 每一个dfs都return一个based on status的 dp array. +- 等于一次性dfs计算到底, 然后back track, 计算顶部的每一层. +- DP 并不一定要是以n为base的. 也可以是局部的去memorize状态->value. + + + +--- + +**95. [Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20in%20String.java)** Level: Medium Tags: [Two Pointers] + + +#### Two Pointer +- 如果做s1的permudation, 时间复杂度是O(n!) 肯定不可以 +- 这里用HashTable的做法 (因为26字母, 所以用int[26]简化) 来记录window内的 character count +- 如果window内的character count 相等, 那么就是permudation +- 更进一步优化: 找两个map相互对应, 不如用一个 int[26]: s1对遇到的character做加法, s2对遇到的character做减法 +- two pointer 运用在 n1, n2 的把控; 以及 s2.charAt(i - n1) 这一步 + + + +--- + +**96. [Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations%20II.java)** Level: Medium Tags: [Backtracking] + + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +#### Backtracking +- 排序, +- Mark visited. 通过permutation规律查看是否排出了重复结果 +- 并且要检查上一层recursive时有没有略过重复element +- time O(n!) + +##### 背景1 +- 在recursive call里面有for loop, 每次从i=0开始, 试着在当下list上加上nums里面的每一个。 +- 从i=0开始,所以会依次recursive每一个nums: +- 因此,例如i=2,肯定比i=3先被访问。也就是:取i=2的那个list permutation肯定先排出来。 + +##### 背景2 +- 重复的例子:给出Input[x, y1, y2], 假设y的值是一样的。那么,{x,y1,y2}和{x,y2,y1}是相同结果。 + +##### Note +- 综上,y1肯定比y2先被访问,{x,y1,y2}先出。 紧随其后,在另一个recursive循环里,{x,y2...}y2被先访问,跳过了y1。 +- 重点:规律在此,如果跳过y1,也就是visited[y1] == false, 而num[y2] == num[y1],那么这就是一个重复的结果,没必要做,越过。 +- 结果:那么,我们需要input像{x,y1,y2}这样数值放一起,那么必须排序。 + +#### Non-recursive, manuall swap +- Idea from: https://www.sigmainfy.com/blog/leetcode-permutations-i-and-ii.html +- 用到 sublist sort +- 用 swap function, 在原数组上调节出来新的permutation +- 注意: 每次拿到新的candidate, 都要把没有permutate的数位sort, 然后再开始swap. +- 这是为了确保, [j]和[j-1]在重复时候, 不用重新记录. + +#### Queue +- 给一个visited queue +- 和queue在所有的地方一同populate. +- 然后visited里面存得时visited indexes。 (Not efficient code. check again) + + + +--- + +**97. [Shuffle an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Shuffle%20an%20Array.java)** Level: Medium Tags: [Permutation] + + +像shuffle music 一样, 做一套shuffle array的functions: + +shuffle() 给出random的permutation + +reset() 给出最初的nums + +#### Permutation +- Permutation 实际上就是在list/array/... 上面给元素换位置 +- 硬换位置, 每次换的位置不同, 用random来找到要换的index +- 维持同一个random seed +- O(n) + +##### Note +- compute all permutations 太慢, 不可行. + + + +--- + +**98. [Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Anagrams.java)** Level: Medium Tags: [Hash Table, String] + + +给一串string, return list of list, 把anagram 放在一起. + +#### Hash Table, key 是 character frequency +- 存anagram +- 用 character frequency 来做unique key +- 用固定长度的char[26] arr 存每个字母的frequency; 然后再 new string(arr). +- 因为每个位子上的frequency的变化,就能构建一个unique的string +- O(nk), k = max word length + + +#### Hash Table, key 是 sorted string +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**99. [Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 背包有自己的大小M, 看最多能放多少重量的书? + +#### Backpack DP 1 +- 简单直白的思考 dp[i][m]: 前i本书, 背包大小为M的时候, 最多能装多种的书? +- **注意**: 背包问题, 重量weight一定要是一维. +- dp[i][j] = Math.max(dp[i][j], dp[i - 1][j - A[i - 1]] + A[i - 1]); +- 每一步都track 最大值 +- 最后return dp[n][m] +- 时间空间 O(mn) +- Rolling array, 空间O(m) + +#### Backpack DP 2 +- true/false求解, 稍微曲线救国: 重点是, 最后, 按照weight从大到小遍历, 第一个遇到true的, index就是最大值. +- 考虑: 用i个item (可跳过地取), 是否能装到weight w? +- 需要从'可能性'的角度考虑, 不要搞成单一的最大值问题. +- 1. 背包可装的物品大小和总承重有关. +- 2. 不要去找dp[i]前i个物品的最大总重, 找的不是这个. + dp[i]及时找到可放的最大sum, 但是i+1可能有更好的值, 把dp[i+1]变得更大更合适. + +##### 做法 +- boolean[][] dp[i][j]表示: 有前i个item, 用他们可否组成size为j的背包? true/false. +- (反过来考虑了,不是想是否超过size j, 而是考虑是否能拼出exact size == j) +- **注意**: 虽然dp里面一直存在i的位置, 实际上考虑的是在i位置的时候, 看前i-1个item. + +##### 多项式规律 +- 1. picked A[i-1]: 就是A[i-1]被用过, weight j 应该减去A[i-1]. 那么dp[i][j]就取决于dp[i-1][j-A[i-1]]的结果. +- 2. did not pick A[i-1]: 那就是说, 没用过A[i-1], 那么dp[i][j]就取决于上一行d[i-1][j] +- dp[i][j] = dp[i - 1][j] || dp[i - 1][j - A[i - 1]] + +##### 结尾 +- 跑一遍dp 最下面一个row. 从末尾开始找, 最末尾的一个j (能让dp[i][j] == true)的, 就是最多能装的大小 :) +- 时间,空间都是:O(mn) + + + + +--- + +**100. [Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)** Level: Medium Tags: [Backpack DP, DP] + + +给i本书, 每本书有自己的重量 int[] A, 每本书有value int[] V + +背包有自己的大小M, 看最多能放多少value的书? + +#### Backpack DP +- 做了Backpack I, 这个就如出一辙, 只不过: dp存的不是max weight, 而是 value的最大值. +- 想法还是,选了A[i - 1] 或者没选A[i - 1]时候不同的value值. +- 时间空间O(mn) +- Rolling Array, 空间O(m) + +#### Previous DP Solution +- 如果无法达到的w, 应该mark as impossible. 一种简单做法是mark as -1 in dp. +- 如果有负数value, 就不能这样, 而是要开一个can[i][w]数组, 也就是backpack I 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**101. [Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)** Level: Medium Tags: [Backpack DP, DP] + + +#### Backpack DP +- 与背包1不同: 这里不是check可能性(OR)或者最多能装的size是多少; 而是计算有多少种正好fill的可能性. +- dp[i][w]: 用前i本书, 正好fill到 w weight的可能性. +- 对于末尾, 还是两种情况: +- 1. i-1位置没有加bag +- 2. i-1位置加了bag +- 两种情况可以fill满w的情况加起来, 就是我们要的结果. +- 如常: dp[n + 1][w + 1] +- 重点: dp[0][0] 表示0本书装满weight=0的包, 这里我们必须 dp[0][0] = 1, 给后面的 dp function 做base +- Space, time: O(MN) +- Rolling array, 空间优化, 滚动数组. Space: O(M) + +#### 降维打击, 终极优化 +- 分析row(i-1)的规律, 发现所有row(i)的值, 都跟row(i-1)的左边element相关, 而右边element是没用的. +- 所以可以被override. +- Space: O(M), 真*一维啊! +- Time: O(MN) + + + +--- + +**102. [Evaluate Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Reverse%20Polish%20Notation.java)** Level: Medium Tags: [Stack] + + +给一个 RPN string list, 根据这个list, 计算结果. + +#### Stack +- stack 里面 存数字 +- 每次遇到operator, 都拿前2个数字计算 +- 计算结果存回到stack里面, 方便下一轮使用. +- Time,Space O(n) + + + + +--- + +**103. [Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)** Level: Medium Tags: [Linked List, Sort] + + +input一串数字, 需要出sorted output. 每次insert一个数字时, 都要放到正确的sorted的位置 + +每次insertion的时候, 都从input里面减掉这个数字 + +#### Linked List +- 把list里面每个元素都拿出来,scan and insert一遍 +- Time O(n^2), worst case, 每次放入n个数字里面的element, 刚好都是最大的 +- 所以每次要traverse n nodes, 然后走n次 + +##### 思考方法 +- 如果已经有个sorted list, insert一个element进去。怎么做? +- while 里面每个元素都小于 curr, keep going +- 一旦curr在某个点小了,加进去当下这个空隙。 + + + +--- + +**104. [Interleaving Positive and Negative Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20Positive%20and%20Negative%20Numbers.java)** Level: Medium Tags: [Two Pointers] + + +给一串数组 有正负数. 重新排列, 让数组里面 正数 和 负数 相隔开. 原来的order无所谓 + +#### Two pointer +- 需要知道正负的位置, 所以排序 O(nlogN) +- 考虑: 正数多还是负数多的问题, 举栗子就看出来端倪了 +- 然后Two Pointer, swap +- Time O(nlogn), space O(n) + +#### extra space +- 用extra O(n) space, 把正负分成两个list +- 然后分别按照index填回去 +- time O(n). space O(n) +- 但是就么有用到Two pointer了 + + + +--- + +**105. [Largest Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number.java)** Level: Medium Tags: [Sort] + + +给一串数字, 非负数, 把所有数字串联起来, 组成最大数字. + +因为结果很大, 所以用string表示 + +#### Sort, Comparator +- 考虑 more significant spot 应该拿到更大的值 +- 如果sort number, comparator 会比较难写: 每个digit的weight不同, 要分别讨论个位数和多位数. +- goal: 让较大的组合数排在前面, 让较小的组合数排在后面 +- 不如: 组合两种情况, 用String比较一下大小 (也可以用 integer来比较组合数, 但是为保险不超Integer.MAX_VALUE, 这里比较String) +- String.compareTo() 是按照 lexicographically, 字典顺序排列的 +- 利用compareTo, 来倒序排列 string, 刚好就得到我们要的结果. +- O(nlogn), 排序 + + + +--- + +**106. [Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)** Level: Medium Tags: [DP, Double Sequence DP, Sequence DP, String] + + +#### Double Sequence DP +- 两个string, 找最值: longest common string length +- 序列型, 并且是双序列, 找两个序列 (两维的某种性质) +- dp[i][j]: 对于 A 的前i个字母, 对于 B 的前j个字母, 找最长公共substring的长度 +- dp = new int[m + 1][n + 1] +- dp[i][j] = dp[i - 1][j - 1] + 1; only if A.charAt(i - 1) == B.charAt(j - 1) +- 注意track max, 最后return +- space O(n^2), time(n^2) + +##### Rolling array +- 空间优化, [i] 只有和 [i - 1] 相关, 空间优化成 O(n) + +#### String +- 找所有A的substring, 然后B.contains() +- track max substring length +- O(n^2) time + + + +--- + +**107. [Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)** Level: Medium Tags: [Array, Coordinate DP, DP, Memoization] + + +#### Coordinate DP +- due to access permission, not test +- dp[i][j]: longest continuous subsequence length at coordinate (i, j) +- dp[i][j] should come from (i-1,j) and (i, j-1). +- dp[0][0] = 1 +- condition: from up/left, must be increasing +- return dp[m-1][n-1] + +#### Memoization +- O(mn) space for dp and flag. +- O(mn) runtime because each spot will be marked once visited. +- 这个题目的简单版本一个array的例子:从简单题目开始想DP会简单一点。每个位置,都是从其他位置(上下左右)来的dpValue + 1. 如果啥也没有的时候,init state 其实都是1, 就一个数字,不增不减嘛。 + + + + +--- + +**108. [Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)** Level: Medium Tags: [Array, DP, Greedy, PreSum, Sequence DP, Subarray] + + +给一串数组, 找数组中间 两个不交互的 subarray 数字之和的最大值 + +#### DP +- 考虑两个方向的dp[i]: 包括i在内的subarray max sum. +- dp[i] 的特点是: 如果上一个 dp[i - 1] + nums[i - 1] 小于 nums[i-1], 那么就舍弃之前, 从头再来: +- dp[i] = Math.max(dp[i - 1] + nums.get(i - 1), nums.get(i - 1)); +- 缺点: 无法track全局max, 需要记录max. +- 因为我们现在要考虑从左边/右边来的所有max, 所以要记录maxLeft[] 和 maxRight[] +- maxLeft[i]: 前i个元素的最大sum是多少 (不断递增); maxRight反之, 从右边向左边 +- 最后比较maxLeft[i] + maxRight[i] 最大值 +- Space, Time O(n) +- Rolling array, reduce some space, but can not reduce maxLeft/maxRight + +#### preSum, minPreSum +- preSum是[0, i] 每个数字一次加起来的值 +- 如果维持一个minPreSum, 就是记录[0, i]sum的最小值(因为有可能有负数) +- preSum - minPreSum 就是在 [0, i]里, subarray的最大sum值 +- 把这个最大subarray sum 记录在array, left[] 里面 +- right[] 是一样的道理 +- enumerate一下元素的排列顺位, 最后 max = Math.max(max, left[i] + right[i + 1]) + + + +--- + +**109. [Reverse Linked List II .java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List%20II%20.java)** Level: Medium Tags: [Linked List] + + +reverse 一个 linked list 中 [m ~ n] 的一部分. + +#### Reverse linked list +- 在基本的reverse linked list 上面 多了一层: 找到front node, 接下来的 [m ~ n] node 需要被reverse +- 只需要reverse中间的部分. +- Reverse的时候: 用一个dummyNode, 这道题里面, 其实就用 nodeFront, 那么 dummy.next 就是整个reversed list. + +##### 注意 +- 一定要Mark开头的那个mth node, 最后用它接上 剩下node tail. 不然后面的node会断掉 + +#### Previous notes +- 遍历到M前, +- 存一下那个点, +- 从M开始, for loop, reverse [m~n]。 然后把三段链接在一起。 + + + + +--- + +**110. [Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +给一个Binary Tree root, 以及两个node p, q. 找 p 和 q 的 lowest common ancestor + +#### DFS +- 因为是 binary tree, 所以直接盲目搜索搜索path不efficient, use extra space and waste time +- 巧用DFS来找每一个node的common ancestor. +- Need the assumption: 1. unique nodes across tree; 2. must have a solution +- 当root == null或者 p q 任何一个在findLCA底部被找到了(root== A || root == B),那么就return 这个root. +- 三种情况: +- 1. A,B都找到,那么这个level的node就是其中一层的ancestor: 其实,最先recursively return到的那个,就是最底的LCA parent. +- 2. A 或者 B 找到,那就还没有公共parent, return 非null得那个。 +- 3. A B 都null, 那就找错了没有呗, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time/space O(n) + + + +--- + +**111. [Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的list +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Brutly寻找p和q的common ancestor, 然后recursively drive left/right +- 非常巧妙, 但是也比较局限; 稍微变条件, 就很难recursive. +- 几种情况: +- 1. one of p, q 在leaf, 那么此时的root其实就是lowest common ancestor +- 2. 如果p, q 在root的左右两边, 这就是分叉口, 那么root就是lowest common ancestor +- 3. 如果p,q 在root的同一边 (左,右), 那么继续dfs +- O(1) extra space, O(logn) time + + + +--- + +**112. [Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Two Pointers +- sorted array, 重复元素都在一起 +- 跟 `Remove Duplicates from Sorted Array` 几乎一模一样, 只不过unique index现在可以 validate 2 位 +- 其余一模一样, use index to track unique item; skip if duplicated for more than 2 times +- O(n) time, O(1) space +- 这里也可以真的用2个pointers 写while loop, 但是没有必要, 只是单纯地走一个for loop其实就足够. + + + +--- + +**113. [Remove Duplicates from Sorted List II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List%20II.java)** Level: Medium Tags: [Linked List] + + +从Linked list 里面摘掉重复元素: 只要重复过, 全部都删掉; 重复出现过得元素一个不留. + +#### Linked List +- sorted list, 重复元素都在一起 +- 运用 dummyHead: 如果要去掉所有重复元素, 就要有个dummyHead作为局外人在开头牵线 +- 只要发现一个 node.val == node.next.val, 就记下这个duplicated val, move forward, 过掉所有重复过的元素 +- 思想: +- 用第二个 inner while loop, 把所有的重复元素都处理干净, 然后再move forward +- 优点: outter while loop 不需要考虑太多case, 在inner loop 都把主要的business logic 解决了. + +##### 注意DummyHead 的使用 +- 当我们有了DummyHead 作为Linked List 的局外线头, 其实可以选择每次遇到duplicate, 就把更加后面的元素 强行assign 给 dummyHead.next +- 下面还尝试过一种做法: 但是需要考虑的edge case 太多了: 不断移动node, 知道不重复, assign prev.next = node. +- 这样的做法比较直白, 但是需要考虑很多edge case, 而且并没有很好利用到 dummy head, 注意规避. + +##### Previous Note +- 斩草除根。 +- 多个node,check node.next ?= node.next.next + + + + +--- + +**114. [QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)** Level: Medium Tags: [Quick Sort, Sort] + + +implement quick sort. + +#### Quick Sort +- 首先partition. 返还一个partition的那个中间点的位置: 这个时候, 所有小于nums[partitionIndex] 都应该在 partitionIndex左边 +- 然后劈开两半 +- 前后各自 quick sort, recursively +- 注意:在partition里面, 比较的时候nums[start] < pivot, nums[end]>pivot, 如果写成了 <= 会 stack overflow. +- Time O(nlogn), Space: O(1) + + + +--- + +**115. [MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/MergeSort.java)** Level: Medium Tags: [Merge Sort, Sort] + + +#### Merge Sort +- Divide and conquer, recursively +- 先从中间分段, merge sort 左边 (dfs), merge sort 右边 +- 最后merge起来 +- merge的时候因为是做int[], 所以没办法必须要O(n) space +- Time O(nlogn), Space O(n) + + + +--- + +**116. [Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**117. [Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)** Level: Medium Tags: [BFS, Tree] + + +如题, 但是output要倒序. + +#### BFS +- 跟Binary Tree Level Order Traversal一样,只不过存result一直存在存在0位. + + +#### DFS +- 根据level来append每个list +- rst里面add(0,...)每次都add在list开头 + + + +--- + +**118. [Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)** Level: Medium Tags: [DFS, Divide and Conquer, Double Recursive, Tree] + + +找到binary tree 里的最长 consecutive sequence. Sequence可以递增递减, Sequence顺序可以回溯parent. + +#### DFS, Divide and Conquer +- Similar to Binary Tree Longest Consecutive Sequence I +- 只不过可以递增递减, 还有连接上parent的方向. +- 对于任何一个节点, 都可能: +- 1. 自己跟两个child链接, 成为一个sequence +- 2. 左边孩子, 右边孩子各自是一个consecutive sequence, 但是不跟root相连 +- main function 一开始就divide成这三份, 然后dfs +- dfs take diff == 1, diff == -1, 来做递增递减的校对. +- dfs rules: +- 1. if node == null, leaf depth = 0 +- 2. if not consecutive, reset the depth = 0 (same for both left child, and right child) +- 3. compare the leftDepth && rightDepth to find the maximum +- 4. diff is the same in the same dfs loop to maintain consistant increase/decrease + +##### 注意 +- dfs的结果很可能是0, 如果没有任何结果, 那么上一层的caller depth = dfs() + 1 = 1 +- 那么回归到root, dfs的结果很可能就是1. +- 可能会问: 那么在tree里面的partial sequence (不连接到root)的被忽略了? +- 这里 `longestConsecutive(root.left)` 就很重要了 +- 这一步特地忽略掉了root, 然后走下去一层: 因为是recursive, 所以还会继续divde && conquer +- 最后, 任何一层的孩子都会被照顾到. + +##### Double Recursive functions +- 用两种recursive的方式handle skip root node的情况 +- Recursive using dfs(), basically build child + parent +- Recursive using main function, but with value of child node: skipping root + + + +--- + +**119. [Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)** Level: Medium Tags: [Backtracking, Combination, DFS] + + +Given two integers n and k, return all possible combinations of k numbers out of 1 ... n. + +#### DFS, Backtracking +- for loop, recursive (dfs). +- 每个item用一次, 下一个level dfs(index + 1) +- Combination DFS. 画个图想想. 每次从1~n里面pick一个数字i +- 因为下一层不能重新回去 [0~i]选,所以下一层recursive要从i+1开始选。 + + + +--- + +**120. [Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)** Level: Medium Tags: [Array, Backpack DP, DP] + + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + +#### Backpack DP +- 计数问题, 可以想到DP. 其实就是Backpack VI. +- 从x个数字里面找candidate(可以重复用同一个数字), 来sum up to target. 找: # of ways to form the sequence. +- Backpack VI: 给一个数组nums, 全正数, 无重复数字; 找: # of 拼出m的方法 +- dp[i]: # of ways to build up to target i +- consider last step: 如果上一步取的是 candidate A, 那么就该加到dp[i]: +- dp[i] += dp[i - A] +- 要找overall dp[i], 就做一个for loop: dp[i] = sum{dp[i - num]}, where for (num: nums) +- Time: O(mn). m = size of nums, n = target +- If we optimize dp for loop, 需要Sort nums. O(mlogm). will efficient 如果m是constant或者relatively small. Overall: O(n) + +#### DFS, backtracking +- 尽管思考方式是对的, 但是 times out +- 可以重复使用数字的时候, 比如用1 来拼出 999, 这里用1就可以走999 dfs level, 不efficient + + + +--- + +**121. [Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Right%20Side%20View.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右:即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list + +#### DFS +- Use Map 来存每一个level的结果 +- dfs function 里, 如果 input depth 不存在, 就add to map. +- dfs function 里面先: dfs(node.right), 然后 dfs(node.left) +- 由于always depth search on right side, 所以map会被right branch populate; 然后才是 leftChild.right + + + + +--- + +**122. [Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)** Level: Medium Tags: [DFS, Tree] + + +找到从max path sum from root. 条件: 至少有一个node. + +#### DFS +- 比Binary Tree Maximum Path Sum I 简单许多. 因为条件给的更多:at least 1 node + have to start from root +- root一定用到 +- 3种情况: curr node, curr+left, curr+right +- 因为一定包括root, 说以从 `dfs(root, sum=0)` 开始, 每个level先加root, sum += root.val + + + +--- + +**123. [Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +给一个single linked list, 右移k steps. k non-negative. + +#### Linked List basics +- 记得用dummy.next来存head. +- 特殊: 这里k可能大于list总长. 写一写linked node 移动的步数, 然后 k = k % n. +- 找到newTail, newHead, 然后利用dummy, 换位子 + + + +--- + +**124. [Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +找到binary tree 里的最长 consecutive sequence. + +#### DFS +- Divide and Conquer. dfs +- 分开 看左边/右边 +- 如果左边满足连续递增的规则, dfs (depth + 1), 如果不满足规则, dfs(depth = 1) +- 右边也是一样 +- 对结果跟max作比较, return + + + +--- + +**125. [Number of Connected Components in an Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Connected%20Components%20in%20an%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +count这个graph里面有多少个独立的component. + +#### Union Find +- 跟Graph Valid Tree 几乎一模一样 +- 建造简单的parent[] union find +- 每个edge都union. +- **注意** union 的时候, 只需要union if rootA != rootB + +#### DFS +- build graph as adjacent list: Map> +- dfs for all nodes of the graph, and mark visited node +- count every dfs trip and that will be the total unions + + + +--- + +**126. [Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)** Level: Medium Tags: [Basic Implementation, Enumeration, String] + + +给一个时间string"12:09", 用里面的4个integer组合成其他时间string, 目标找最小的next time. + +如果组合出的time string 在input time之前, 默认 + 24 hours. + +#### String +- enumerate all candidates and filter to keep the correct ones +- String.compareTo(string) -> gives lexicographical comparision + + + +--- + +**127. [Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)** Level: Medium Tags: [Array, Quick Sort, Sort, Two Pointers] + + +给一串数字, 和 int k. 根据k的值partition array, 找到第一个i, nums[i] >= k. + +#### Two Pointer +- Quick sort的基础. +- Partition Array根据pivot把array分成两半。 +- 从array两边开始缩进。while loop到遍历完。非常直白的implement。 +- 注意low/high,或者叫start/end不要越边界 +- O(n) +- 注意: 这里第二个inner while `while(low <= high && nums[high] >= pivot) {..}` 采用了 `nums[high] >= pivot` +- 原因是题目要找第一个nums[i] >= k, 也就是说, 即便是nums[i]==k也应该swap到前面去 +- 这个跟quick sort 原题有一点点不一样. + + + + +--- + +**128. [Word Ladder.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder.java)** Level: Medium Tags: [BFS] + + +给一串string[], 需要找shortest distance to change from wordA -> wordB. (限制条件细节见原题) + +#### BFS +- 通常, 给一个graph(这道题可以把beginWord看成一个graph的起始node), 找shortest path用BFS +- 在start string基础上,string的每个字母都遍历所有26个字母 +- visited 过的 从wordList里去掉 +- time: word length m, there can be n candidates => O(mn) +- 但是总是exceed time limit on LeetCode. However, it passes LintCode: +- 原因是 LeetCode给的是list, list.contains(), list.remove() 都是 O(logn) time!!! +- convert to set first. + +#### Trie +- timeout, overkill + + + +--- + +**129. [Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)** Level: Medium Tags: [Design, Hash Table] + + + +给一个string[] dict, 和一个word. + +每个word都可以缩写成固定的abbreviation ``(详细看原题) + +检查input word是否满足unique + +#### HashMap +- 简单算出abbreviatioin +- 检查abbr是否存在; 如果存在, 是不是input word本身. + + + +--- + +**130. [Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)** Level: Medium Tags: [BST, DP, Divide and Conquer, Tree] + + +给一个数字n, 找到以(1...n)为node的所有unique BST. + +#### BST +- 根据BST规则, divide and conquer +- 取一个value, 然后分两半(start, value - 1), (value + 1, end) 分别dfs +- 然后左右两边的结果cross match + +#### DP? Memoization? + + + +--- + +**131. [Ugly Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number.java)** Level: Medium Tags: [Math] + + +LeetCode: 判断数字是否是ugly number. (definition: factor only have 2, 3, 5) + +#### Math +- 看是否可以整除. +- 看整除最终结果是否== 1 + +LintCode: 找kth ugly number, 应该与 Ugly Number II是一样的 + +- 方法1: PriorityQueue排序。用ArrayList check 新的ugly Number是否出现过。 +- 方法1-1:(解释不通,不可取)用PriorityQueue排序。神奇的3,5,7走位:按照题目答案的出发,定了3,5,7以什么规律出现。但是题目并没有特殊表明。 +- 方法2: DP . Not Done yet. + + + + +--- + +**132. [Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + +time: O(nlogk) +space: O(n) + +给一串String. 找到top k frequent words. + +#### PriorityQueue - Min Heap +- O(n) space of map, O(nlogk) to build queue. +- limit minHeap queue size to k: add to queue if found suitable item; always reduce queue if size > k + +#### PriorityQueue - Max Heap +- 用HashMap存frequency, 用ArrayList存lists of words +- create一个Node class, 然后用PriorityQueue. +- PriorityQueue里面用到了 String.compareTo(another String).巧妙。 +- time: PQ uses O(nlogn), overall O(nlogn) +- slower, because the maxHeap needs to add all candidates + +#### Trie && MinHeap屌炸天 +- 可以做一下 +- http://www.geeksforgeeks.org/find-the-k-most-frequent-words-from-a-file/ + +#### HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**133. [Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree definition +- Recursively build the binary tree +- 左孩子:(A.left, (A.left+A.rigth)/2) +- 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**134. [Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Build%20II.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + + +给一个array, 建造segment tree structure, + +每个treeNode 里面存这个range里的 max value, return root node. + +#### Segemnt Tree +- 给的是Array. 注意找区间内的max, assign给区间. 其余和普通的segment tree build一样 +- 注意, segment tree是根据array index range 排位: 根据index in [0, array.length - 1]割开区间, break到底 +- 最终start==end做结尾 +- 这道题要trackmax, 那么在leaf node assign max=A[start] or A[end] +- 往上,parent一层的max:就是比较左右孩子,其实都是在两个sub-tree里面比较sub-tree的max。 + +- Devide and Conquer +- 先分,找到left/right,比较max,在create current node,再append到当前node上面。 +- 实际上是depth-first, 自底向上建立起的。 + + + +--- + +**135. [Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给了segment Tree, node里面有Max value, 找[start,end]里面的max + +#### Segment Tree, Divide and Conquer +- 根据[start,end]跟 mid of (root.start, root.end) 做比较: +- 简单的2个case: [start,end]全在mid左, 或者[start, end]全在mid右 +- 稍微复杂的3rd case: [start, end]包含了mid, 那么就break into 2 queries +- [start, node.left.end], [node.right.start, end] + + + +--- + +**136. [Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +给一个segmentTree, node里面存max. 写一个modify function: modify(node, index, value). + +#### Segment Tree, Divide and Conquer +- Recursively 在segment tree里面找index, update it with value. +- 每个iteration,很可能(要么左手,要么右手)max就变了。所以每次都left.max and right.max compare一下 +- 最后轮回到头顶,头顶一下包括头顶,就全部都是max了 + + + +--- + +**137. [Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)** Level: Medium Tags: [Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree] + + +#### Segment Tree +- 和 Segment Tree Query I 以及其他Segment Tree类似: 这个SegmentTreeNode return count of elements in range +- 这个题目考了validate input source:input 的start,end可能超出root[start,end]。 +- 那么第一步就要先clear一下: 1. 完全不在range就return 0. 2. 有range重合就规整到root的range. + + + + +--- + +**138. [ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)** Level: Medium Tags: [Design, Hash Table] + + +#### basic implementation +- 用HashMap, 理解题目规律,因为重复的计算可以被覆盖,所以是个优化题。没有什么太大的悬念和意义. +- 消灭重合点: +- 如果process当下col, 其实要减去过去所有加过的row的交接点。。。 +- 再分析,就是每次碰到row 取一个单点, sumRow += xxx。 +- 然后process当下col时候, sum += colValue * N - sumRow. 就等于把交叉所有row(曾经Process过的row)的点减去了。很方便。 +- 最后read in 是O(P), process也是O(P). + + + + +--- + +**139. [Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Two Pointers +- 木桶理论。盛水的最高取决于最低的那面墙。 +- 左右两墙,往中间跑动。 +- 另:若一面墙已经小于另外一面,就要移动,换掉矮墙(可能下一面更高,或更低) +- 但决不能换掉当下的高墙,因为低墙已经limit的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**140. [Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + +time: O(n) +space: O(1) + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List +- Basic Implementation of copy linked list: +- use node and dummy to hold new list, 遍历head.next .... null. +- Map 在这里用来: 1. avoid creating same node; 2. return the item if existing +- map 的 key全部是old object, 新的key全部是 newly created object +- 每一步都check map里面有没有head. 没有? 加上 +- 每一步都check map里面有没有head.random. 没有? 加上 + + + +--- + +**141. [Encode and Decode Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20Strings.java)** Level: Medium Tags: [String] + + +如题. + +#### String +- 'word.length()#word' 这样encode, 可以避免遇到# +- 基于我们自己定的规律, 在decode的里面不需要过多地去check error input, assume所有input都是规范的. +- decode就是找"#",然后用"#"前的数字截取后面的string. + + + + +--- + +**142. [Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)** Level: Medium Tags: [DFS, Divide and Conquer] + + +如题: Calculate the a^n % b where a, b and n are all 32bit integers. + +#### Divide and Conquer +- a^n可以被拆解成(a*a*a*a....*a), 是乘机形式,而%是可以把每一项都mod一下的。所以就拆开来take mod. +- 这里用个二分的方法,recursively二分下去,直到n/2为0或者1,然后分别对待. +- 注意1: 二分后要conquer,乘积可能大于Integer.MAX_VALUE, 所以用个long. +- 注意2: 要处理n%2==1的情况,二分时候自动省掉了一份 a,要乘一下。 + + + + +--- + +**143. [Find the Connected Component in the Undirected Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Connected%20Component%20in%20the%20Undirected%20Graph.java)** Level: Medium Tags: [BFS, DFS] + + +给一个undirected graph, return 所有的component. (这道题找不到了) + +#### BFS +- BFS遍历,把每个node的neighbor都加进来. +- 一定注意要把visit过的node Mark一下。因为curr node也会是别人的neighbor,会无限循环。 +- Component的定义:所有Component内的node必须被串联起来via path (反正这里是undirected, 只要链接上就好) +- 这道题:其实component在input里面都已经给好了,所有能一口气visit到的,全部加进queue里面,他们就是一个component里面的了。 +- 而我们这里不需要判断他们是不是Component + +#### DFS +- DFS 应该也可以 visit all nodes, mark visited. + + + +--- + +**144. [HashWithCustomizedClass(LinkedList).java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithCustomizedClass(LinkedList).java)** Level: Medium Tags: [Hash Table] + + +练习HashMap with customized class. functions: get(), put(), getRandom() + +#### Hash Table +- store map as array: `Entry[] table;` +- store entry as linked list: `public Entry(K key, V value, Entry next)` +- compute hashKey: `Math.abs(key.hashCode()) % this.capacity` +- Handle collision: +- 1. Check if duplicate (matching key), if so, replace and return +- 2. Check through the linked list, find find duplicate (matching key), replace and return. +- 3. If no duplicate, add the entry to the tail +- Find item: compute hashKey -> find linked list -> iterate over list to find a matching key. + + + +--- + +**145. [Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)** Level: Medium Tags: [Binary Search, Divide and Conquer, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的最小值. + +#### Segment Tree +- SegtmentTree, methods: Build, Query. 这题是在SegmentTreeNode里面存min. +- 类似的有存:max, sum, min + + + +--- + +**146. [Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字 int[], 然后一个query Interval[], 每个interval是 [start, end], 找query 区间里的sum. + +#### Segment Tree + Binary Search +- 其实是segment tree 每个node上面加个sum +- 记得Segment Tree methods: Build, Query +- Note: 存在SegmentTreeNode里面的是sum. 其他题目可能是min,max,count ... or something else. + + + +--- + +**147. [Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)** Level: Medium Tags: [BST, DFS, Stack, Tree] + + +#### Iterative + stack: inorder traversal +- 很容想到Inorder-binary-search-tree Traversal +- Iterative 稍微难想点:先把最左边的add, pop() stack, 加上右边(如果存在); 下一个轮回,如果又左孩子,又是一顿加。 + +#### Recursive + DFS +- 然后稍微优化一下,确保rst.size() == k 时候,就可以return了 +- check leaf => dfs left => add root => dfs right + + + +--- + +**148. [Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Element%20II.java)** Level: Medium Tags: [Array] + + +#### Sort + count +- O(nlogN) + +#### Two counters +- O(n), count and track valueA, valueB +- count overall apperance at the end for the two items +- save to result +- 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +--- + +**149. [Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Linked List +- linked list 不能像partitioin array一样从两边遍历 +- 把小于value的加在前半段, 把 >= value的加在后半段 +- 做法很普通: 建造两个list, midTail pointer, post pointer +- 把满足条件(=x)的数字分别放到两个list里面 +- 记得用dummyNode track head. +- 最终midTail.next = post链接起来。 + + + +--- + +**150. [Peeking Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Peeking%20Iterator.java)** Level: Medium Tags: [Design] + + +#### Use concept pre cache +- 找一个cache来存next()的值, 也就是: next value的值提前存在cache里面 +- 因此peek()的时候, 就可以直接return cache, 而不用做 itt.next() +- 然后每次真的next()的时候, 里取下一个itt.next()维护这个cache + +#### Previous notes +- 再一次理解错题意. peek() 就是头顶,但是不一定是最大值啊。 +- 总是把PEEK想成了最大值,然后用2 STACK做了最大值的cache,练的一手好双stack,可惜错了。 + + + + +--- + +**151. [Rehashing.java](https://github.com/awangdev/LintCode/blob/master/Java/Rehashing.java)** Level: Medium Tags: [Hash Table] + + +给一个Hash Table, 是用 linked list 做的. 问题是: capacity太小, collision太多的情况下, 需要double capacity 然后rehash. + +#### Hash Table +- 明白hashCode() function的意义: 拿到hashKey的时候, 用hashKey%capacity 来做hash code +- hashcode就是hash map里面的index +- 明白collision handling 的方式, 和如何double capacity而rehashing +- 都是基本操作, 概念实现 + + + +--- + +**152. [Reorder List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reorder%20List.java)** Level: Medium Tags: [Linked List] + + +给一个Linked list, reorder: 从head/tail 两个方向 向中间进发, re-order like: one node at a time, + +#### Linked List 功能大集合 +- reverse list, find mid of list, merge two list +- 先find mid, 然后把 mid.next reverse了, 最后merge 两段. +- 注意, 用完mid.next之后, 一定要 mid.next = null, 不然merge会出问题 + + + +--- + +**153. [Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)** Level: Medium Tags: [Backtracking, DFS, String] + + +给一串数字, 检查是否是valid IP, 如果合理, 给出所有valid 的IP组合方式. + +#### Backtracking +- 递归的终点:list.zie() == 3, 解决最后一段 +- 递归在一个index上面, pass s.toCharArray() +- validate string要注意leading '0' +- 注意: 递归的时候可以用一个start/level/index来跑路 +- 但是尽量不要去改变Input source, 会变得非常confusing. +- note: code有点messy, 因为要考虑IP的valid情况 +- 那个'remainValid', 其实是一个对于remain substring的判断优化, 不成立的就不dfs了 + + + +--- + +**154. [Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String.java)** Level: Medium Tags: [String] + + +#### Break by space, then flip +- 结尾不能有空格 +- trim() output +- 如果Input是 ""的话,split以后就啥也没有了 +- 另个题目Reverse Words in String (char[]) 可以in-place, 条件是char[]里面是没有首尾空格. +- Time, Space: O(n) + +#### Other methods +- flip entire string, then flip each individual string (代码有点多, 这道题犯不着) + + + +--- + +**155. [Reverse Words in a String II.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20II.java)** Level: Medium Tags: [String] + + +#### In-place reverse +- reverse用两回. 全局reverse。局部:遇到空格reverse +- 注意ending index: `i == str.length - 1`, 结尾点即使没有' '也要给reverse一下最后一个词 + + + + +--- + +**156. [Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)** Level: Medium Tags: [Array, Binary Search] + + +给2D matrix, 每行sorted, 每行的首位都大于上一行的末尾. goal: find target from matrix + +#### 2D matrix 转1D array +- 一行一行是从小到大, sorted, 连续的, 可以看做1D sorted array +- Binary Search + + + +--- + +**157. [Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)** Level: Medium Tags: [Binary Search, Divide and Conquer] + + +给matrix, 每一行sorted, 每一列从上往下sorted, 找target是否存在 + +#### Binary Search +- 根据给定的性质, 其实点选的极端一点: x = 最下面的row, y = 当下一行里面最小的left position. +- (x,y)在左下角 +- 在此情况下, 只能往一个方向运行: 如果小于target, y++; 如果大于target, 那么只能x-- +- 每次操作, 都是删掉一行, 或者一列, 再也不需要回头看 +- `while (x >= 0 && y < col) {}` 确保不会跑脱 +- 同样的方式: 可以从右上角(0, col - 1) 开始, 代码稍微改一改 + +#### Divide and Conquer? +- TODO + + + +--- + +**158. [Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)** Level: Medium Tags: [Array, Binary Search] + + +给sorted array, 有重复数字, 找跟target重合所在的range. + +#### Binary Search +- 2个while loop +- 找first/last occurance +- TODO: Can the code be simplified? + + + + +--- + +**159. [Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)** Level: Medium Tags: [BST, Binary Tree] + + +给一个BST, integer range (k1, k2), 找range 里面所有的integer. + +#### BST +- 等于dfs遍历了所有k1<= x <= k2的x node。 +- dfs left, process root, then dfs right +- 这里, 把 left/right/match的情况全部cover了,然后把k1,k2的边框限制好,中间就全部遍历了。 + + + +--- + +**160. [Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)** Level: Medium Tags: [Divide and Conquer, Linked List, Merge Sort, Sort] + + +#### Merge sort +- 1. find middle. 快慢指针 +- 2. Sort: 切开两半,先sort前半, 如果先sort了mid.next~end, sort后,中间点mid.next == null,再sort前半段 +- 3. Merge: 假设given list A, B 已经是sorted, 然后按照大小,混合。 +- 要recursively call sortList() on partial list. + +#### Quick sort +- 想做可以看讲义:http://www.jiuzhang.com/solutions/sort-list/ +- 但是quick sort不建议用在list上面。 +- 排列list, merge sort可能更可行和合理。原因分析在下面, 以及: http://www.geeksforgeeks.org/why-quick-sort-preferred-for-arrays-and-merge-sort-for-linked-lists/ + + + +--- + +**161. [Summary Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Summary%20Ranges.java)** Level: Medium Tags: [Array] + + +给一串sorted list, 中间有缺数字, return 所有数字的range string (example 看题目) + +#### Basic implementation +- 用一个list as the buffer to store candidates +- when: 1. end of nums; 2. not continuous integer => convert list to result + + + +--- + +**162. [Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Topological Sort] + + +#### Topological Sort BFS +- indegree tracking: Track all neighbors/childrens. 把所有的children都存在 inDegree里面 +- Process with a queue: 先把所有的root加一遍(indegree == 0),可能多个root。并且全部加到queue里面。 +- BFS with Queue: +- Only when map.get(label) == 0, add into queue && rst. (indegree剪完了, 就是root啦) +- inDegree在这里就 count down indegree, 确保在后面出现的node, 一定最后process. + + +#### Basics about graph +- 几个graph的condition: +- 1. 可能有多个root +- 2. directed node, 可以direct backwards. + +TODO: +- build`Map inDegree = new HashMap<>();` and include the root itself +- that is more traditional indegree building + + + +--- + +**163. [Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)** Level: Medium Tags: [Array, Enumeration] + + +从(0,0)坐标, 走完spiral matrix, 把结果存在list里. + +#### DX, DY +- Basic implementation, array, enumeration +- 写一下position前进的方向: RIGHT->DOWN->LEFT->UP +- 用一个direction status 确定方向 +- 写一个compute direction function 改变方向 `(direction + 1) % 4` +- `boolean[][] visited` 来track走过的地方 + + + +--- + +**164. [Construct Binary Tree from Inorder and Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Construct%20Binary%20Tree%20from%20Inorder%20and%20Postorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Tree] + + +#### DFS, Divide and Conquer +- 写个Inorder和Postorder的例子。利用他们分left/right subtree的规律解题。 +- Postorder array 的末尾, 就是当下层的root. +- 在Inorder array 里面找到这个root,就刚好把左右两边分割成left/right tree。 +- 这题比较tricky地用了一个helper做recursive。 特别要注意处理index的变化, precisely考虑开头结尾 +- runtime: O(n), visit && build all nodes + +#### Improvement +- `findMid(arr)` can be replaced with a map, no need execute O(n) search at runtime + + + +--- + +**165. [Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Generate%20Parentheses.java)** Level: Medium Tags: [Backtracking, DFS, Sequence DFS, String] + + +#### DFS +- start with empty string, need to go top->bottom +- 取或者不取`(`, `)` +- rule: open parentheses >= close parentheses +- Note: 在DFS时 pass a reference (StringBuffer) and maintain, instead of passing object (String) and re-create every time +- time: O(2^n), pick/not pick, the decision repat for all nodes at every level +- T(n) = 2 * T(n - 1) + O(1) + +#### bottom->up DFS +- figure out n=1, n=2 => build n=3, and n=4 +- dfs(n-1) return a list of candidates +- add a pair of `()` to the candidates: either in front, at end, or contain the candidates + + + +--- + +**166. [Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)** Level: Medium Tags: [DFS, Enumeration, Math, Sequence DFS] + + +TODO: +1. use list, iterative? keep candidates and populating +2. clean up the dfs code, a bit messy +3. edge case of "0001000" is invalid, right? + +#### DFS +- A bit like BFS solution: find inner list, and then combine with outter left/right sides. +- find all solutions, DFS will be easier to write than iterative/BFS +- when n = 1, there can be list of candidates at bottom of the tree, so bottom->up is better +- bottom->up, dfs till leaf level, and return candidates. +- each level, pair with all the candidates +- 其实就是剥皮,一层一层,是一个central-depth-first的,钻到底时候,return n=1,或者n=2的case,然后开始backtracking。 +- 难的case先不handle.到底之后来一次overall scan. +- every level have 5 choices of digital pairs to add on sides. Need to do for n-2 times. +- Time complexity: O(5^n) + + + +--- + +**167. [Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)** Level: Medium Tags: [Backtracking, DFS, DP] + + +String 只包含 + , - 两个符号. 两个人轮流把consecutive连续的`++`, 翻转成 `--`. + +如果其中一个人再无法翻转了, 另一个人就赢. 求: 给出string, 先手是否能赢. + +#### Backtracking +- curr player 每走一步, 就生成一种新的局面, dfs on this +- 等到dfs结束, 不论成功与否, 都要backtracking +- curr level: 把"++" 改成 "--"; backtrack的时候, 改回 '--' +- 换成boolean[] 比 string/stringBuilder要快很多, 因为不需要重新生成string. +- ++ 可以走 (n - 1)个位置: +- T(N) = (N - 2) * T(N - 2) = (N - 4) * (N - 2) * T(N - 4) ... = O(N!) + +##### iterate based on "++" +- 做一个String s的 replica: string or stringBuilder +- 每次dfs后, 然后更替里面的字符 "+" => "-" +- 目的只是Mark已经用过的index +- 真正的dfs 还是在 original input string s 身上展开 +- 每次都重新生成substring, 并不是很efficient + +##### Game theory +- 保证p1能胜利,就必须保持所有p2的move都不能赢 +- 或者说, 在知道棋的所有情况时, 只要p2有一种路子会输, p1就一定能走对路能赢. +- 同时,p1只要在可走的Move里面,有一个move可以赢就足够了。 +- p1: player1, p2: player2 + +#### O(N^2) 的 DP +- 需要Game Theory的功底, Nim game. https://www.jiuzhang.com/qa/941/ +- http://www.1point3acres.com/bbs/thread-137953-1-1.html +- TODO: https://leetcode.com/problems/flip-game-ii/discuss/73954/Theory-matters-from-Backtracking(128ms)-to-DP-(0ms) + + + +--- + +**168. [Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning.java)** Level: Medium Tags: [Backtracking, DFS] + + +给个string s, partition(分段)后, 要确保每个partition都是palindrome. + +求所有partition palindrome组合. `list>` + +#### DFS +- 可以top->bottom: 遍历str, validate substring(start, i); if valid, add as candidate, and dfs; backtrack by remove candidate. +- 也可以bottom->up: 遍历str, validate substring(start, i); if valid, dfs(remaining str), return list of suffix; cross match with curr candidate. + +#### DFS Top->Bottom +- 在遍历str的时候,考虑从每个curr spot 到 str 结尾,是能有多少种palindorme? +- 那就从curr spot当个字符开始算,开始backtracking. +- 如果所选不是palindrome, 那move on. +- 若所选的确是palindrome, 加到path里面,DFS去下个level,等遍历到了结尾,这就产生了一种分割成palindrome的串。 +- 每次DFS结尾,要把这一层加的所选palindrome删掉,backtracking嘛 + +#### Optimization +- 可以再每一个dfs level 算 isPalindrome(S), 但是可以先把 boolean[][] isPalin 算出来, 每次O(1) 来用 +- 注意: isPalin[i][j] 是 inclusive的, 所以用的时候要认准坐标 +- Calculate isPalin[i][j]: pick mid point [0 ~ n] +- expand and validate palindrome at these indexes: `[mid, mid+1]` or `[mid-1][mid+1]` + +#### Complexity +- Overall Space O(n^2): 存 isPlain[][] +- Time O(2^n), 每一层都在做 pick/not pick index i 的选择, 所以worst case 2^n. +- 因为我们的isPalin[][]优化了palindrome的判断O(1), 所以overall Time: O(2^n) + + + +--- + +**169. [Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)** Level: Medium Tags: [Array, Hash Table, PreSum] + + +给一个int[][] matrix, 找一个sub matrix, where the sum == 0. + +#### PreSum的思想 +- 算出一个右下角点(i,j)到(0,0)的大小: 上一块 + 左一块 + curr node - overlap area +- preSum[i][j]: sum from (0,0) to (i-1,j-1) +- same approach as `subarray sum`: use hashmap to store diff->index; if diff re-appears, that means sum of 0 has occurred +- sequence of calculation: 1. iterate over start row. 2. iterate over end row. 3. iterate over col number (this is where hashmap is stored based on) +- the iteration over col is like a screening: find previous sum and determine result +- Note: 其实并没有真的去找 `== 0` 的解答,而是根据特性来判断 `剩下的/后来加上的一定是0` + + + +--- + +**170. [Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Substring.java)** Level: Medium Tags: [DP, String] + + +给一个string, 找到最长的palindrome substring. + +Related: Longest Palindromic Subsequence, Palindrome Partioning II + +O(n^2) is not too hard to think of. How about O(n)? + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + +#### DP: isPalin[][] +- 穷举double for loop. O(n^2) +- boolean isPalin[i][j], 每次确认有palindrome就记录下来true / false +- 穷举的for loop计算顺序: end point j, and stat point i = [0, j] +- 在计算 isPalin[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- double for loop: O(n^2). slower, because it guarantees O(n^2) due to the for loop + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**171. [Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Palindromic%20Subsequence.java)** Level: Medium Tags: [DFS, DP, Interval DP, Memoization] + + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- 用[i][j]表示区间的首尾 +- 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) +- Iteration一定是以i ~ j 之间的len来看的. +- len = j - i + 1; 那么反推, 如果len已知, j = len + i -1; +- 注意考虑len == 1, len == 2是的特殊情况. +- time/space: O(n^2) + +#### Memoization +- 同样的方式model dp[i][j]: range [i, j] 之间的 max palindromic length +- 三种情况: +- 1. 首尾match 继而 dfs[i+1, j-1] +- 2. 首尾不match,dfs[i+1,j] +- 3. 首尾不match,dfs[i,j-1] +- 注意: init dp[i][j]=-1, dfs的时候查dp[i][j] 是否算过 +- more about dfs: bottom-up, first dive deep into dfs(i+1,j-1) till the base cases. +- time/space: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + +--- + +**172. [Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/Gas%20Station.java)** Level: Medium Tags: [Greedy] + + +给一串gas station array, 每个index里面有一定数量gas. + +给一串cost array, 每个index有一个值, 是reach下一个gas station的油耗. + +array的结尾地方, 再下一个点是开头, 形成一个circle route. + +找一个index, 作为starting point: 让车子从这个点, 拿上油, 开出去, 还能开回到这个starting point + +#### Greedy +- 不论从哪一个点开始, 都可以记录总油耗, `total = {gas[i] - cost[i]}`. 最后如果total < 0, 无论从哪开始, 必然都不能走回来 +- 可以记录每一步的油耗积累, `remain += gas[i] - cost[i]` +- 一旦 remain < 0, 说明之前的starting point 不合适, 也就是说, 初始点肯定在后面的index. 重设: start = i + 1 +- single for loop. Time: O(n) + +#### NOT DP +- 看似有点像 House Robber II, 但是问题要求的是: 一个起始点的index +- 而不是求: 最后点可否走完/最值/计数 + + + +--- + +**173. [Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)** Level: Medium Tags: [Array, Coordinate DP, DFS, DP, Memoization] + + +给一个list> triangle, 细节原题. 找 min path sum from root. + +#### DFS + Memoization +- 其实跟给一个2D matrix没有什么区别, 可以做dfs, memoization. +- initialize memo: pathSum[i][j] = MAX_VALUE; 计算过的path省略 +- Bottom-top: 先dfs到最深的path, 然后逐步网上返回 +- `OR 原理: min(pathA, pathB) + currNode` +- 浪费一点空间, pathSum[n][n]. space: O(n^2), where n = triangle height +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP +- 跟dfs的原理很像, `OR 原理: min(pathA, pathB) + currNode` +- init dp[n-1][j] = node values +- build from bottom -> top: dp[i][j] = Math.min(dp[i + 1][j], dp[i + 1][j + 1]) + triangle.get(i).get(j); +- 跟传统的coordinate dp有所不同, inner for loop 是需要计算 j <= i, 原因是triangle的性质. +- 空间: dp[n][n]. space: O(n^2) +- 时间:O(n^2). Visit all nodes once: 1 + 2 + 3 + .... n = n^2 + +#### DP + O(n) space +- Based on the DP solution: the calculation always depend on `next row` for col at `j` and `j + 1` +- 既然只depend on next row, 可以用rolling array来处理: reduce to O(n) space. +- Further: 可以降维, 把第一维彻底去掉, 变成 dp[n] +- 同样是double for loop, 但是只在乎column changes: `dp[j] = Math.min(dp[j], dp[j + 1]) + triangle.get(i).get(j);` + + + +--- + +**174. [Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space +- 扫描线+Count无敌手。注意start end把interval给合起来。 +- count==0的时候,就是每次start end双数抵消的时候,就应该是一个interval的开头/结尾。写个例子就知道了。 +- 记得怎么写comparator. New way: new PriorityQueue<>(Comparator.comparing(p -> p.val)); +- 在 LeetCode里面,Sweep Line比方法2要快很多. + +#### Sort Interval +- Sort by interval.start之后,试着跑一遍,按照merge的需求,把需要merge的地方续好,然后减掉多余的interval就好。 +- sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` +- Related example: Insert Interval +- 用两个相连的Interval: curr, next +- 如果 curr.end覆盖了 next.start: 需要merge. 那么比较一下 curr.end vs. next.end +- 一旦merge, 需要remove被覆盖的 next interval: `list.remove(i+1)` +- 若没有重合,就继续iteration +- time O(nlogn), space O(1) + +#### Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list +- 找到结尾 interval, 满足条件就可以save +- 如果不到return的条件, 就继续延伸 interval.end + + + +--- + +**175. [H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index.java)** Level: Medium Tags: [Bucket Sort, Hash Table, Sort] + + +找到h-index, 给的citation int[] 并不是sorted. h-index 的definition 具体看题目. + +#### Sort, find h from end +- 例子写出来,发现可以sort以后按照定义搜索一遍。 nlogn. +- 搜索一遍时候可以优化,用binary search. 但是没意义,因为array.sort已经用了nlogn +- 题目给的规则, 从小到大排序后: 剩下的 paper `n-h`, 全部要 <= h 个 citation. +- time O(nlogn), search O(n) + +##### 正向思考 +- 从i = 0 开始找第一个 `citations[i] >= h`, 就是第一个符合 h-index 规则的paper, return h + +##### 反向思考 +- 如果从 h = n, 每次h--; 那么 `x = n - h` 就是从 `[0 ~ n)` 开始找第一个 `dictations[x] >= h`, 就是结果 +- 同时, `dictations[x-1]` 就是最后一个(dictation最多的)其余的paper. + +#### Bucket count / Bucket Sort +- O(n) +- Bucket sort的思想(更像是counting sort?): 过一遍 input, 把dictation value 作为 index, 分布在bucket[index]上++ +- bucket[x] 是 count when # of citation == x. +- 如果 x 大于 n的时候, 就超出了index范围, 但是刚好这个问题可以包容, 把这样的情况记位在bucket[n]就可以 +- 巧妙: `sum += bucket[h]` where `h = [n ~ 0]` 利用了h-index的definition: +- #of papers (sum of bucket[n]...bucket[0]) has more than h cidations +- 这里运用到了bucket sort的思想, 但是并不是sorting, 而h-index的定义运用的很巧妙. +- Read more about actual bucket sort: https://en.wikipedia.org/wiki/Bucket_sort + + + +--- + +**176. [H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/H-Index%20II.java)** Level: Medium Tags: [Binary Search] + + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + + + +--- + +**177. [Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)** Level: Medium Tags: [Array, Partition, Quick Sort, Sort, Two Pointers] + + +给一串数字 nums, 数字代表颜色[0,1,2]; 要求 sort nums, 数字最终按照大小排列. + +虽然叫sort color, 其实就是sort 这些 numbers, 只不过抽象了一下. + +#### partition array, the base of quick sort +- partition the array by pivot k = {0, 1, 2} +- 每一次partition都return starting point of the current partition +- 然后根据下一个 color, 去还没有sort 干净的那个部分, 再sort一下就好 +- time O(kn), where k = 0 => O(n) +- 这里只是partion, 并不需要recursively quick sort, 所以结果是简单的O(n) + +#### One pass +- have two pointers, left/right +- start tracks red, end tracks blue. Swap red/blue to right position, and left++ or right--. +- leave white as is and it will be sorted automatically +- be very careful with index i: when swapping with index right, we do not know what is nums[right], so need to re-calculate index i . +- O(n) +- Note: this one pass solution does not work if there are more than 3 colors. Need to use the regular quick sorty. + +#### Counting sort +- TODO: count occurance and reassign array + + + +--- + +**178. [Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)** Level: Medium Tags: [Partition, Quick Sort, Sort, Two Pointers] + + +Sort Color的普通版, sort all k colors in colors array. + +Details 参见: https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Color.java + +#### Quick Sort +- O(nk) + + + +--- + +**179. [Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)** Level: Medium Tags: [Partition, Sort, String, Two Pointers] + + +给一串字符(ASCII 大写, 小写字母), 要求sort 小写字母, 在大写字母前面. + +字母间的前后顺序无所谓, 也不需要preserve original order . + +跟sort color分成相似. + +#### Partition + Two pointers +- 其实就是quick sort里面的partition function的简化版 +- Two pointers, 找一个 pivot 'a' 来区分大写小写字母 +- ASCII code 里面 大写字母在小写字母前面, 数字更小 +- 然后 while, move start++, end--, +- 每一轮都swap + +#### Two pointers +- 直接用两个 pointer left/right 标记开头结尾 +- 每次遇到 `>= 'a'` 就是小写字母, swap(chars, i, left); +- 每次遇到 `< 'a'` 就是大写字母, swap(chars, i, right); +- 注意: 每次处理完left swap, 任由for loop i++, 因为确定 [0 left] 都是准确的 +- 每次处理完 right swap, 我们不确定从 right index 换过来的是不是正确的, 所以 i--, 跟for loop 的 i++抵消. +- 写 while loop 的 solution看起来更容易理解. + + + +--- + +**180. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + +time: O(nlogn) +space: O(n) + +给一串数字, 找subarray的首尾index, 条件: subarray最接近0. + +#### PreSum + index in class +- Can be a 2D array, or a `class Point` to store preSum + index +- Sort preSum: smaller (有可能负数) 靠前, 大数字靠后 +- 比较preSum种相连接的两个节点, 找差值min; 因为最接近的两个preSum节点的差值肯定是最小 +- min所在的两个节点的index, 就是result candidate: 这两个index可能再原nums里面相差很远 +- time O(nlogn), sort +- space: O(n) + +#### 为何没有用 map ? +- 因为map虽然能存 preSum + index, 但是无法有效排序 +- 所以用一个class来存这两个信息, 然后合理排序 + + + +--- + +**181. [Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### Array, count frequency, enumerate +- Enumerate to understand: 1. we can module the tasks in module/section; 2. Only need sum the intervals/slots, not return actual layout +- Perfect condition, all letters appear identical # times: just line them up separate in order. +- Real case: task appears different times +- 1. Place maxCount task as header followed with n slots: define (maxCount-1) sections +- 2. For tasks with less # than maxCount# can fill the (maxCount-1) sections; what about the tail section? +- 3. Any task with same maxTask#, of if prior sections all filled, will fill the tail section +- To count overall slots/intervals, come up with this equation: +- 1. Fixed sections: `(maxCount - 1) * (n + 1)` +- 2. Plus all repeating maxCount tasks: calculate by couting identical maxCount of them +- 3. Exception: if the first (max - 1) sections are all filled completely, and we still have extra task (ex: when n is not large enough), then just return tasks.length +- time O(1), space O(1) + +#### PriorityQueue +- 正面去做: +- summerize 每个task出现的次数, 然后qp sort Task object, count 大的靠前 +- 起始每个section: k slots = n + 1 +- 目标是穷尽 k, 或者 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) +- 如果qp 真的穷尽, break, return count +- 不然, count + remain of k +- extra space O(x), time O(n) + constant time O(xlogx), where x = 26 + + + +--- + +**182. [Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort] + + +#### PriorityQueue +- Use priority queue to sort by customized class interval{int dist; int x, y;}. +- Sort by larger distance and then sort by start index +- seat(): pq.poll() to find interval of largest distance. Split and add new intervals back to queue. +- leave(x): one seat will be in 2 intervals: remove both from pq, and merge to a new interval. +- 主方程写出来其实很好写, 就是 split + add interval, 然后 find + delete interval 而已. 最难的是构建data structure +- seat(): O(logn), leave(): O(n) + +##### Trick: 构建虚拟 boundary +- 如果是开头的seat, 或者是结尾的seat, 比较难handle: 一开始坐在seat=0的时候, 没有interval啊! +- Trick就是, 我们自己定义个虚拟的座位 `seat=-1`, `seat=N` +- 一开始有一个 interval[-1, N] 然后就建立了boundary. +- 从此以后, 每次split成小interval的时候: +- 遇到 `interval[-1, y]`, distance就是 `(y - 0)` +- 遇到 `interval[x, N]`, distance就是 `(N - 1 - x)` +- 当然正常的interval dist 就是 `(y - x) / 2` + +##### distance 中间点 +- Interval.dist 我们其实做的是 distance的中间点 `(y - x) / 2` +- 这里的dist是 `距离两边的距离` 而不是 x, y 之间的距离. 这里要特别注意. + +#### TreeSet +- https://leetcode.com/problems/exam-room/discuss/139885/Java-Solution-based-on-treeset/153588 + +#### Map +- how? +- TODO, not sure. + + + +--- + +**183. [Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Anagrams.java)** Level: Medium Tags: [Array, Hash Table] + + +把anagram找到并output + +#### HashMap +- 存在int[26], Arrays.toString(arr) 就是 string key: character frequency map +- anagram都有一样的key, 存进hashmap +- output anagrams + +#### HashMap + Sort +- HashMap 的做法. sort每个string, 存进HashMap, 重复的就是anagrams,最后输出。 +- toCharArray +- Arrays.sort +- Stirng.valueOf(char[]) +- 时间n*L*O(logL),L是最长string的长度。 + +#### Previous Notes +- Arrays.toString(arr)的做法。arr是int[26], assuming only have 26 lowercase letters. +- Count occurrance, 然后convert to String,作为map的key. +- Time complexity: nO(L) +- 另一种做法:http://www.jiuzhang.com/solutions/anagrams/ +- 1. take each string, count the occurrance of the 26 letters. save in int[]count. +- 2. hash the int[] count and output a unique hash value; hash = hash * a + num; a = a * b. +- 3. save to hashmap in the same way as we do. +- 这一步把for s: strs 里面的时间复杂度降到了O(L). L = s.length(). +- Need to work on the getHash() function. +- 时间变成n*O(L). Better. + + + + +--- + +**184. [Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)** Level: Medium Tags: [DFS, Hash Table, Tree] + + +给一串3-digit 的数组. 每个数字的表达一个TreeNode, 3 digit分别代表: depth.position.value + +这串数字已经从小到大排列. 求: 所有可能的 root->leaf path 的所有可能的 path sum 总和. + +#### DFS, Hash Table +- 因为`前两个digit可以uniquely identify`一个node, 所以可以把前两个digit作为key, 定位node. +- 特点: 比如考虑root, 有 n 个leaf, 就会加 n 遍root, 因为有 n 个 unique path嘛. +- 实现: 每个node, 上来先把curr value加进sum; 只要有child, 到这个node位置的以上path sum 就要被重加一次. +- format: depth.position.value. (on same level, position may not be continuous) +- approach: map each number into: , and dfs. +- Start from dfs(map, rootKey, sum): +- 1. add node value to sum +- 2. compute potential child. +- 3. check child existence, if exist, add sum to result (for both left/right child). Check existence using the map. +- 4. also, if child exist, dfs into next level +- Space, time O(n) + + + +--- + +**185. [Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)** Level: Medium Tags: [DP, Math] + + +具体看题目: count # of valid rectangles (four corner are 1) in a grid[][]. + +#### basic thinking + Math +- Fix two rows and count matching columns +- Calculate number rectangles with `combination` concept: +- total number of combinations of pick 2 points randomly: count * (count - 1) / 2 + +#### DP +- TODO. HOW? + +#### Brutle +- O(m^2 * n^2), times out + + + +--- + +**186. [Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)** Level: Medium Tags: [DP, String] + + +根据题意, count # of palindromic substring. (不同index截取出来的substring算不同的情况) + +#### isPalin[][] +- build boolean[][] to check isPalin[i][j] with DP concept +- check all candidates isPalin[][] +- O(n^2) + +#### odd/even split check +https://leetcode.com/problems/palindromic-substrings/discuss/105689/Java-solution-8-lines-extendPalindrome + + + +--- + +**187. [Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Multiply%20Strings.java)** Level: Medium Tags: [Math, String] + + +给两个integer String, 求乘积 + +#### String calculation, basic implementation +- let num1 = multipier, num2 = base. +- mutiply and save into int[m + n], without carry. Loop over num1, each row num1[x] * num2 +- move carry to the correct index and direclty save result +- calculate carry on rst[]: sb.insert(0, c) such that no need to reverse() later +- remove leading '0', but do not delete string "0" +- time,space O(mn) + +#### Previous notes. +- Bad solution: reversing makes it complicated, no need to reverse. +- 1. 数字‘123’, 在数组里面, index == 0 是 ‘1’。 但是我们平时习惯从最小位数开始乘积,就是末尾的'3'开始。 +- 所以!翻转两个数字先!我去。这个是个大坑。 +- 2. 乘积product,和移动Carrier都很普通。 +- 3. !!最后不能忘了再翻转。 +- 4. 最后一个看坑。要是乘积是0,就返回‘0’。 但是这个其实可以在开头catch到没必要做到结尾catch。 +- 用到几个StringBuffer的好东西: reverse(), sb.deleteCharAt(i) +- 找数字,或者26个字母,都可以: s.charAt(i) - '0'; s.charAt(i) - 'a'; + + + +--- + +**188. [Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + +time: O(2^n) +space: O(2^n) + +给一串unique integers, 找到所有可能的subset. result里面不能有重复. + +#### DFS +- dfs的两种路子: 1. pick&&skip dfs, 2. for loop dfs +- 1. pick&&skip dfs: 取或者不取 + backtracking. 当level/index到底,return 一个list. Bottom-up, reach底部, 才生产第一个solution. +- 2. for loop dfs: for loop + backtracking. 记得:做subset的时候, 每个dfs recursive call是一种独特可能,先加进rst. top-bottom: 有一个solution, 就先加上. +- Time&&space: subset means independent choice of either pick&¬ pick. You pick n times: `O(2^n)`, 3ms + +#### Bit Manipulation +- n = nums.length, 那么在每一个index, 都是 pick / not pick: 0/1 +- 考虑subset index 0/1的bit map: range 的就是 [0000...00 ~ 2^n-1] +- 每一个bitmap就能展现出一个subset的内容: all the 1 represents picked indexes +- 做法: +- 1. 找出Range +- 2. 遍历每一个bitmap candidate +- 3. 对每一个integer 的 bit representation 遍历, 如果是1, add to list +- time: O(2^n * 2^n) = O(4^n), still 3ms, fast. + +#### Iterative, BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- should be same O(2^n), but actual run time 7ms, slower + + + + + +--- + +**189. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + +time: O(2^n) +sapce: O(2^n) + +给一串integers(may have duplicates), 找到所有可能的subset. result里面不能有重复. + +#### DFS +- DFS, 找准需要pass along的几个数据结构. 先`sort input`, 然后DFS +- Using for loop approach: 每个dfs call是一种可能性,直接add into result. +- 为了除去duplicated result, skip used item at current level: `if (i > depth && nums[i] == nums[i - 1]) continue;` +- sort O(nlogn), subset: O(2^n) +- space O(2^n), save results + +#### BFS +- Regular BFS, 注意考虑如果让one level to generate next level +- skip duplicate: `if (i > endIndex && nums[i] == nums[i - 1]) continue;` +- 1. 用queue来存每一次的candidate indexes +- 2. 每一次打开一层candiates, add them all to result +- 3. 并且用每一轮的candidates, populate next level, back into queue. +- srot O(nlogn), subset: O(2^n) +- should be same O(2^n). slower than dfs + +#### Previous notes: +- 在DFS种skip duplicate candidates, 基于sorted array的技巧: +- 一旦for loop里面的i!=index,并且nums[i] == nums[i-1], +- 说明x=nums[i-1]已经在curr level 用过,不需要再用一次: [a,x1,x2],x1==x2 +- i == index -> [a,x1] +- i == index + 1 -> [a,x2]. 我们要skip这一种 +- 如果需要[a,x1,x2]怎么办? 其实这一种在index变化时,会在不同的两个dfs call 里面涉及到。 + +#### 注意 +- 不能去用result.contains(), 这本身非常costly O(nlogn) +- 几遍是用 list.toString() 其实也是O(n) iteration, 其实也是增加了check的时间, 不建议 + + + + +--- + +**190. [Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + +time: O(n!) +space: O(n!) + +给一串数字candidates (no duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 可以用任意多次. + + +#### DFS, Backtracking +- 考虑input: 没有duplicate, 不需要sort +- 考虑重复使用的规则: 可以重复使用, 那么for loop里面dfs的时候, 使用curr index i +- the result is trivial, save success list into result. + +##### Time complexity for Combination (reuse-candidate) +- at each level dfs, we have the index as starting point: +- if we are at `index=0, we can have n child dfs() options via for loop`; +- if at `index=1, we will have (n-1) dfs options via for loop`. +- Consider it as the `pick/not-pick` problem, where the difference is you can pick `x` times at each index rather than only 2 times. +- Overall, we will multiply the # of possibilities: n * (n - 1) * (n - 2) ... * 1 = n! => `O(n!)` + +##### Combination DFS 思想 +- 在每个index上面都要面临: `pick/not pick的选择`, 用for loop over index + backtracking 实现 picks. +- 每次pick以后, 就生成一条新的routine, from this index +- 下一个level的dfs从这个index开始, 对后面(或者当下/if allow index reuse) 进行同样的 pick/not pick 的选择 +- 注意1: 每个level dfs 里面, for loop 里会有 end condition: 就不必要dfs下去了. +- 注意2: Backtracking在success case && dfs case 后都要做, 因为backtrack 是为了之前上一层dfs. + + + + +--- + +**191. [Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20II.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字candidates (can have duplicates), 和一个target. + +找到所有unique的 组合(combination) int[], 要求每个combination的和 = target. + +注意: 同一个candidate integer, 只可以用一次. + +#### DFS, Backtracking +- when the input has duplicates, and want to skip redundant items? +- 1. sort. 2. in for loop, skip same neighbor. +- 考虑input: 有duplicate, 必须sort +- 考虑重复使用的规则: 不可以重复使用 +- 1. for loop里面dfs的时候, 使用curr index + 1 +- 2. for loop里面, 同一个level, 同一个数字, 不能重复使用: `(i > index && candidates[i] == candidates[i - 1]) continue` +- 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip (这个就记住吧) +- the result is trivial, save success list into result. + +##### Time complexity +- Which one? +- Time: every level has 1 less element to choose, worst case is: cannot find any solution over all combinations: O(m!) +- Time: Same as `subsetII`, pick/not=pick an item as we go, no reuse of item. Worst case: all unique items in the set. O(2^n) + + + + +--- + +**192. [Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一个integer k, 和一个target n. + +从positive数字[1 ~ 9], 找到所有unique的 组合(combination) int[], size = k, 要求每个combination的和 = n. + +(隐藏条件, 需要clarify): 同一个candidate integer [1 ~ 9], 只可以用一次. + +#### DFS, Backtracking +- 跟Combination Sum I, II 没什么太大区别, 只不过, 一定要用k个数字, 也就是一个for loop里面的特别条件 +- 考虑input: 没有重复数字 [1 ~ 9] +- 考虑candidate重复利用: 不可以重复利用, next level dfs 时候, curr index + 1 +- the result is trivial, save success list into result. + +##### Time Complexity +- Which one? +- worst case: tried all numbers and cannot find: O(m!), m = 9, all possible integers in [1~9] +- C(n,k), n choose k problem : `n! / (k! * (n-k)!)` => ends up being `O(min(n^k, n^(n-k)))` + + + +--- + +**193. [Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + +time: O(n) +space: O(1) + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 看第二个解答, 进一步简化了代码 +- PreProduct, 跟preSum的感觉有点像, 就是差一位. + + + +--- + +**194. [Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)** Level: Medium Tags: [Bit Manipulation] + +time: O(n) +space: O(1), 32-bit array + +给出Hamming Distance定义(bit format时候有多少binary diff), 求一串数字的hamming distance总和. + +#### Bit Manipulation +- Bit题: 考验 bit >>, mask & 1, 还有对题目的理解能力 +- Put integers in binary, and compare each column: +- for each `1`, ask: how many are different from me? all the `0` +- `# of diffs at each bit-column = #ofZero * #ofOne ` +- 1. countZero[], countOne[]; 2. loop over nums and populate the two array + +##### 注意雷点 +- 问清楚: 10^9 < 2^31, we are okay with 32 bits +- `最终的hamming distance 要从 [1 ~ 32] 哪个bit开始算起`? 取决于 `最长`的那个binary format: 但不用先去找bit length +- 在做countZero, countOne时候, 都做32-bit; 最终做乘积的时候, 如果 `1` 或者 `0` 个数为零, 乘积自然为0. + + + + +--- + +**195. [Smallest Subtree with all the Deepest Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/Smallest%20Subtree%20with%20all%20the%20Deepest%20Nodes.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + +time: O(n) +space: O(n) + +给一个tree, 按照题意找最一个node满足: +1. 这个node的subtree涵盖最深level的所有leaves. +2. 这个node必须是能找到的最deep那个 + +条件2的需求是因为: root本身就是满足条件1的node, 还有很多Higher-level node也是如此, 所以要找那个deepest. + + +#### DFS on tree +- 分析题目, 思想是: 看到tree里面所有的leaves, 找到他们最deep的 common ancestor +- Maintain a map +- Recursively dfs: return deepest node that has all leaves by these comparisons: +- 1. If left,right child same depth, return root: they need common ancestor +- 2. If not same depth, return the one with larger depth +- 被传送去上一个level的, 永远都是subtree里面符合题意的: the node containing all leaf nodes +- Visit all nodes once O(n), space O(n) + +#### BFS +- Find all leaves at deepest level +- Use map to track each node-parent +- Backtrack all nodes to find common ancestor + + + +--- + +**196. [Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Hash Table + PreSum +- Hash Table two sum 思想, but `save frequency of current preSum` +- map.get(priorSum) = the # of possible ways to reach k +- Keep counting +- O(n) time, O(n) space + +##### Detailed explanation +- From the orignal presum solution: `target = preSum[j] - preSum[i - 1]`. Here: `k = sum - priorSum`, and reversely, `priorSum = sum - k` +- priorSum is just previously calcualted sum; track its frequency using `map` +- map.get(priorSum): # ways to sum up to priorSum. +- Also, to get `priorSum + k = sum`: each unique way of building priorSum will append later elements to reach sum (the later elemnts will sum up to k) +- Therefore # ways to build `k = map.get(priorSum)` + + +#### PreSum, O(n^2) +- move from starting point i = [0 ~ n -1] and define range = [i ~ j] +- use presum to verify k: `preSum[j] - preSum[i - 1]` +- O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**197. [Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + +time: O(n) +space: O(n) + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path 的情况, 不懂得要问: +- 1. `.` 代表current directory, 可以忽略. +- 2. `../` 表示previous level. +- 3. double slash 可以忽略. +- 4. empty string 要output `/` +- 最终就是用stack (`上一个加进去的item, 用来备选pop() out`), 遇到 `../` pop()掉上一个加上去的item, 其余加进stack +- 最终用 '/' 把所有item连接起来. + + + +--- + +**198. [Convert Binary Search Tree to Sorted Doubly Linked List (extra space).java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List%20(extra%20space).java)** Level: Medium Tags: [Linked List, Stack, Tree] + +time: O(n) +space: O(n) + +给一个BST, convert成 sorted doubly DoublyListNode. + +#### Inorder Traversal, Linked List +- 会iterative traverse Binary Search Tree(Stack && handle left-dig-down) +- create Doubly-ListNode, 注意用一个dNode作为tail node of the list + +##### Iterative inorder traversal +- 在check right node的事后, +- 不论right == null or != null, 每次都要强行move to right. +- 如果不node = node.right, +- 很可能发生窘境: +- node always = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 + + + +--- + +**199. [Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + +time: O(n) +space: O(n) + +#### Queue +- 简单的level traversal.根据level奇数偶数而add到不同位子. +- Option1: based on level % 2, insert to front/end of list +- Option2: based on level, insert right/left of node into queue + + + +--- + +**200. [Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- true/false problem, think about dp +- 子问题: 前i个字母, 是否可以有valid break +- 检查dp[j] && `if substring(j, i) valid`, for all j = [0 ~ i] +- dp = new boolean[n + 1]; dp[0] = true; +- goal: if there is a j, `dp[j] == true && word[j, n] in dict`. Need iterate over i = [0 ~ n], also j = [0, i] +- 注意, 用set代替list, 因为要用 contains(). + +#### Previous notes +##### 方法2(attempt4 code) +- 与Word BreakII用同样的DP。 +- valid[i]: 记录从i到valid array末尾是否valid. + +##### 方法1:(attempt3 code) +- state,rst[i]: 从[0~i] inclusive的string是否可以在dict中break开来找到? +- function: rst[i] = true if (rst[i - j] && set.contains(s.substring(i - j, i))); j in[0~i] +- 1. rst[i - j] 记录的是[0, i-j]这一段是否可以break后在dict找到。 +- 2. 若true,再加上剩下所有[i-j, i]都能在dict找到,那么rst[i] = rst[0, i - j] && rst[i-j, i] == true +- 优化:找dict里面最长string, 限制j的增大。 + + + + +--- + +**201. [Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Binary Search, Coordinate DP, DP, Memoization] + +time: O(n^2) dp, O(nLogN) binary search +space: O(n) + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做O(n^2), 然后可否O(nLogN)? + +#### DP, double for loop, O(n^2) +- 找subsequence: 不需要continous, 可以skip candidate +- 考虑nums[i]结尾的时候, 在[0, i), dp[i - 1] 里count有多少小于nums[i] +- dp[i]: 到i为止 (对于所有 j in [0, i], 记录max length of increasing subsequence +- max需要在全局维护: nums是无序的, nums[i]也可能是一个很小的值, 所以末尾dp[i]并不是全局的max, 而只是对于nums[i]的max. +- 正因此, 每个nums[i]都要和每个nums[j] 作比较, j < i. +- dp[i] = Maht.max(dp[i], dp[j] + 1); j = [0 , i - 1] +- 时间复杂度 O(n^2) + +#### O(nLogN) +- 维持一个list of increasing sequence +- 这个list其实是一个base-line, 记录着最低的increasing sequence. +- 当我们go through all nums的时候, 如果刚好都是上升, 直接append +- 如果不上升, 应该去list里面, 找到最小的那个刚好大于new num的数字, 把它换成num +- 这样就完成了baseline. 举个例子, 比如替换的刚好是在list最后一个element, 等于就是把peak下降了, 那么后面其他的数字就可能继续上升. +- '维护baseline就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**202. [Best Time to Buy and Sell Stock with Transaction Fee.java](https://github.com/awangdev/LintCode/blob/master/Java/Best%20Time%20to%20Buy%20and%20Sell%20Stock%20with%20Transaction%20Fee.java)** Level: Medium Tags: [Array, DP, Greedy, Sequence DP, Status DP] + +time: O(n) +space: O(n), O(1) rolling array + +跟Stock II 一样, 买卖无限, 需先买在卖. 附加条件: 每个sell transaction要加一笔fee. + +#### Sequence DP +- 与StockII一样, dp[i]: represents 前i天的最大profit. +- sell 的时候, 才完成了一次transaction, 需要扣fee; 而买入不扣fee. +- model sell on dp[i] day (which depends on dp[i-1]) and each day can be sell/buy => add status to dp[i][status] +- status[0] buy on this day, status[1] sell on this day +- dp[i][0] = Math.max(dp[i-1][0], dp[i - 1][0] - prices[i]); +- dp[i][1] = Math.max(dp[i-1][1], dp[i - 1][1] + prices[i] - fee); +- init: dp[0][0,1] = 0; dp[1][1] = 0; dp[1][0] = - prices; +- return dp[n][1] + + + +--- + +**203. [Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Random%20Pick%20Index.java)** Level: Medium Tags: [Reservior Sampling] + +time: O(n) +space: O(n) for input int[], O(1) extra space used + +#### Reservior sampling +- Random choose: think about reservoir sampling. https://www.youtube.com/watch?v=A1iwzSew5QY +- Use random generator rd.nextInt(x) pick integer between [0, x) +- try all numbers, when target is met, we want to model reservoir sampling: +- item was chosen out of i samples, and all other samples are failed. +- where we can use 'count' to represent the denominator/base to choose. +- **HAVE TO finish all samples** to make sure equal opportunity +- we can pick that last matched item as result +- `rd.nextInt(count++) == 0` make sure we are always picking num == 0 to meet definition of reservoir sampling. + +#### Knowledge +- If multiply these probablities together to get the probability of one item being chosen with reservior sampling: +- probability = 1/i * (1 - 1/i+1) * (1 - 1/i+2) ....(1 - 1/n) = 1/n + + + + +--- + +**204. [Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Celebrity.java)** Level: Medium Tags: [Array, Greedy] + +time: O(n) +space: O(1) + +有n个人, 其中有个人是celebrity, 满足条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +#### Understand the property +- If brutly find celeb by comparing all possible pair: take complete O(n^2) handshakes. +- Instead, we can perform pruning, or like survival mode: +- 1. Assume a celeb = 0, and compare with all i = [1~ n-1] +- 2. If `celeb candidate know i, set celeb = i` as the next candidate (ex: prev canddiate invalid when he knows i) +- 3. For last standing celeb candidate: compare with all for validation +- Why performing the last run of validation? There could be someone dropped out before we execute `know(celeb, i)`. + +##### 思考逻辑 +- 先写出来[0 ~ n - 1], 最简单的方式 O(n^2) 检查, 记录每个人的状态. +- 逐渐发现, 因为 celeb 谁都不会认识, 那么当任何candidate knows anyone, 他自身就不是celeb. +- 我们可以greedy地, 一旦fail一个, 就立刻假设下一个是celeb candidate +- 最终还是要检查一遍, 避免错漏. +- 想一下happy case: 如果 celeb=0, 那么 know(celeb, i) 永远都是false, 然后 celeb一直保持0, 坚持到verify所有人. + + + +--- + +**205. [Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/Sparse%20Matrix%20Multiplication.java)** Level: Medium Tags: [Hash Table] + +time: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` +space: O(1) extra + +给两个matrics, 做乘积. 注意, 是sparse matrix (特点: 很多0). + +#### Hash Table +- Recall matric multiplication rules: result[i][j] = sum(A-row[i] * B-col[j]) +- `sparse matric: lots positions are zero` +- 平白地写matric multiplication 没有意义, 重点就是optimization: +- `optimization`: for A-zero-row, and B-zero-col, there is no need to calculate, just return 0. +- 1. Find A-zero-rows and store in setA, same for setB +- 2. during multiplication, reduce time complexity. +- Base: O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row` + +#### Matrices +- 乘法规则: result[i][j] = sum(A-row[i] * B-col[j]) +- A column size == B row size. 并且: 计算顺序是iterate over A column size + + + +--- + +**206. [Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)** Level: Medium Tags: [Hash Table] + +time: O(mn) +space: O(X), X = max wall width + +给一面墙, 每一行是一行bricks. 用一条vertical line 扫描, 会vertically割开brink. 找到割开最少brick的那条线的x index. + +#### Hash Table +- Find the vertical line (x-coordinate of the grid), where most gaps are found. +- Each gap has (x,y) coordinate +- Create `map`, and maintain a max occurance. +- 计算: x-coordinate: `x = 0; x += brick[i] width` +- Eventually: min-crossed bricks = wall.lenght - maxOccurrance + +##### 思想 +- 分析题意, 找到题目的目标 +- 虽然Map自己不能有sort的规律, 但是可以maintain global variable + + + +--- + +**207. [Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/Exclusive%20Time%20of%20Functions.java)** Level: Medium Tags: [Stack] + + +#### Stack +- 1. later function always appears after prior fn: 1 is called by 0 +- 2. `Not mentione in the question`: a function can be started multiple times +- 3. `Not mentione in the question`: a fn cannot start if children fn starts +- 4. Use stack to keep id +- TODO: what leads to the choice of stack? stacking fn id + + + +--- + +**208. [Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)** Level: Medium Tags: [Array, Math] + + +#### Array, Math +- 这个问题更在于问题本身的分析 (而且还有多余条件); 最终的for loop 也比较不standard. +- People younger than 15 cannot make requests due to the first rule. +- From the age of 15, people can make requests to the same age: a[i] * (a[i] - 1) requests. +- People can make requests to younger people older than 0.5 * i + 7: a[j] * a[i] requests. +- The third rule is redundant as the condition is already covered by the second rule. +- TODO: the approach. + + + +--- + +**209. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + + +// 如何想到从中间initialize + + + +--- + +**210. [Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)** Level: Medium Tags: [Hash Table, PreSum, Subarray] + +time: O(n) +space: O(n) + +#### Map +- use `Map` to store inline preSum and its index. +- 1. Build presum incline +- 2. Use map to cache current preSum value and its index: `Map` +- 3. Each iteration: calculate possible preSum candidate that prior target sequence. ex: `(preSum - k)` +- 4. Use the calculated preSum candidate to find index +- 5. Use found index to calculate for result. ex: calculate range. + + + +--- + +**211. [Contiguous Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Contiguous%20Array.java)** Level: Medium Tags: [Hash Table] + + +TODO: how aout without chaning the input nums? + + + +--- + +**212. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + +time: O(n) +space: O(n) + +给一串点, 找是否有一个所有点中间的, 跟y-axis平行的中线. + +#### Hash Table +- 1. store in `Map>`, 2. iterate over map, check head,tail against the mid point +- 很好的细节题目: +- 1. 除以2, 需要存double +- 2. (问面试官)可以有重复的点! 所以track `set` +- 3. 处理 left==right时候, 就当做两个点来处理. +- 4. 存进set里面没有sort, 但是最后做check的时候, 需要sort list +- 时间: visit all nodes 两遍, O(n) + + + +--- + +**213. [Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + +time: O(1) avg +space: O(n) + +#### Hash Table +- 用`map 来track value->index`, 用`list track index->value` +- map查看value是否存在 +- list maintain 用来 insert/remove/random operations. +- 特点: 一旦remove, 换到list结尾然后 `list.remove(list.size() - 1)`, 这样remove的cost更低. +- list.remove(object) 应该是要O(logn) 做一个search的. + + + +--- + +**214. [Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)** Level: Medium Tags: [Coordinate DP, DP] + +time: O(n^2) +time: O(n) + +给一串 unsorted sequence, 找到长 increasing subsequence 的个数! + +#### Coordinate DP +- 需要能够判断综合题, 分清楚情况和套路: combination of `longest subsequence` and `ways to do`, as well as global variable. +- len[i] (我们平时的dp[i]): 在前i个元素中, 最长的 increasing subsequence length; +- count[i]: 在前i个元素中, 并且以 len[i]这个长度为准的 subsequence的 count. 或者: 在前i个元素中, ways to reach longest increasing subsequence. +- `len[i] == len[j] + 1`: same length, but different sequence, so add all `count[i] += count[j]` +- `len[i] < len[j] + 1`: 这就是更长的情况找到了, 那么有多少次 count[j] 有多少, count[i] 就有多少. 仔细想sequence: 长度增长了, 但是ways to reach i 没有增长. +- 同样的判断需要用在 maxLen 和 maxFreq上: +- 如果没有增长 maxLen 不变, maxFreq上面需要 +=count[i] (同一种长度, 多了更多的做法) +- 如果maxLen 变长, maxFreq 也就是采用了 count[i] = count[j] +- TODO: Is rolling array possible? + +#### 相关 +- 都是 Coordiate DP, DP的鼻祖家族: +- Longest Increasing Subsequence (跟这道题的一部分一模一样) +- Longest Continuous Increasing Subsequence (连续, 只check dp[i - 1]) +- Longest Increasing Continuous Subsequence I, II (Lintcode, II 是matrix) + + + +--- + +**215. [Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)** Level: Medium Tags: [Coordinate DP, DP, Status DP] + + + +#### DP +- 特点: 上一步可能是swaped也可能是fixed +- 考虑A,B之间的现状: `A[i] > A[i - 1] && B[i] > B[i - 1]` 或者 `A[i] > B[i - 1] && B[i] > A[i - 1]` +- 问题: 如何把这个状态变成合理的strick-increasing状态? +- `A[i] > A[i - 1] && B[i] > B[i - 1]`: 1. 已经合理, 也不动. 2. [i], [i-1] 全部都swap +- `A[i] > B[i - 1] && B[i] > A[i - 1]`, 交错开来, 所以调换[i], 或者[i-1]: 1. 换[i-1]. 2. 换[i] +- 注意因为求min, 所以init value应该是 Integer.MAX_VALUE; + + + +--- + +**216. [Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, 在higher level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 应该比较好想: naturally level-traverse all nodes, add node to appropriate col list +- Use min/max to track map keys, since the keys are continous +- Map does not provide random access; unless map key is marked with sequence i = [min, max] + +#### DFS +- 一开始很容易想到: enumerate一下, 先放curr node.val, 然后node.left.val, node.right.val. 非常简单 +- 但是最简单的方法有错: assume所有left subtree都 排在right subtree. 但是: right subtree可能先有一个lower-left-branch, appear in a column first. +- 所以还要preserve column list的order. +- 这里我们用了 `Map` 来track col, Node 里面用了 `node.level`来track level (其实再一个map也可以) +- 这样在结尾要sort,就会非常慢: Visit all nodes O(n) + O(logK) + O(KlogM), K = # of cols, M = # of items in col +- 应该也是可以optimize map keys的, 反正都是continuous key + + + + + +--- + +**217. [Populating Next Right Pointers in Each Node II.java](https://github.com/awangdev/LintCode/blob/master/Java/Populating%20Next%20Right%20Pointers%20in%20Each%20Node%20II.java)** Level: Medium Tags: [DFS, Tree] + +time: O(n) +space: O(1) + +给一个binary tree, 用constant space link 所有所有node.next to same level next node. + +#### DFS +- 用constant space 也就是不可以BFS, 但是mention了用dfs stack space没问题 (提示啊!) +- 1. link leftChild -> rightChild +- 2. resolve root.rightMost child -> first possible root.next.left/right child +- 3. dfs connect(rightChild), connect(leftChild) +- Each level should be fully linked from left side, so every reach to parent will have valid path or end. + +#### Trick +- 1. 处理 nextNode -> next -> next ...的case: 找到第一个有child的next node才可以罢休. 这个case很容易miss +- 2. 我们的假设是, 上一个level的所有node都应该是linked, 那么在dfs时候, 就应该先connect(root.right). 右孩子的全处理完毕, 那么trick1才可以施行. + + + +--- + +**218. [Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + +time: log(n) +space: O(1) + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 +- 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` +- `start mid`: start = mid; +- 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- `mid < target < end`: start = mid; +- `target < mid`: end = mid; + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**219. [Find the Weak Connected Component in the Directed Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20the%20Weak%20Connected%20Component%20in%20the%20Directed%20Graph.java)** Level: Medium Tags: [Union Find] + + +遍历 weak connected graph, 将结果存在 List>种. + +#### Union Find +- 跟传统的UnionFind有两点不同: +- 1. 用 Map 代替 int[], 因为没有给出 graph node label的 boundary. +- 2. find(x)时候, 没有去update `parent[x]/map.put(x, ..)`. 因为我们最终需要找到这个path. +- 无法用传统dfs: directed node 无法point到上一个点; 必须用`存parent的方式把所有node遍历掉` + +#### Identify这是个union-find问题 +- 看到了weak component的形式: 一个点指向所有,那么所有的点都有一个公共的parent,然后就是要找出这些点。 +- 为何不能从一个点出发,比如A,直接print它所有的neighbors呢: +- 如果轮到了B点,那因为是directed,它也不知道A的情况,也不知道改如何继续加,或者下手。 +- 所以,要把所有跟A有关系的点,或者接下去和A的neighbor有关系的点,都放进union-find里面,让这些点有Common parents. +- 最后output的想法: +- 做一个 map 。 +- 之前我们不是给每个num都存好了parent了嘛。 +- 每个num都有个parent, 然后不同的parent就创造一个不同的list。 +- 最后,把Map里面所有的list拿出来就好了。 + + + +--- + +**220. [Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Hash Table, Union Find] + + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Union Find +- 构建 `Map`, 然后再反向整合: parent -> list of email +- init with for all emails +- 因为不同account可能串email, 那么把所有email union的时候, 不同account 的email也会被串起来 +- 最终: 所有的email都被union起来, 指向一个各自union的 parent email +- UnionFind 的 parent map 可以反向输出所有 child under parent. +- 同时要维护一个 account name> 的map, 最终用来输出. + +#### Hash Table solution, passed but very slow +- Definitely need iterate over accounts: merge them by email. +- Account object {name, list of email} +- map +- 1. iterate over accounts +- 2. find if 'account' exist; if does, add emails +- 3. if not, add account to list and to map. map all emails to accounts. +- output -> all accounts, and sort emails +- space O(mn): m row, n = emails +- time O(mn) + + + +--- + +**221. [Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)** Level: Medium Tags: [Binary Search, Lint, Segment Tree] + + +给一串数字, array size = n. 给一串query: 每个query是一个数, 目的找 count# items smaller than query element. + +#### Segment Tree +- 和平时的segment tree问题不同。 [0 ~ n] 代表实际数字: based on real value的segment tree. +- Modify时,把array里面的value带进去,找到特定的位子, 然后count + 1. +- 最终在SegmentTree leaf上面全是array里面实际的数字。 +- node.count: 在node range里面的有多少个数字 + +##### right use of modify() +- build() 只是 empty segment tree, 没有property +- modify() 需要: 1. 找到left, update count+=1; 2. aggregate all parent when after returning +- 所以每一个modify 都是在整个path上所有的node上 + count + +##### query trick +- 在query前,给进去的start和end是: 0 ~ value-1. +- `value-1`: 找比自己所在range小1的range(那么自然而然地就不包括自己了),这样就找到了smaller number. + +##### About other basic segment tree setup +- [那么其他做过的SegmentTree是怎么样呢?] +- 那些构成好的SegmentTree(找min,max,sum)也有一个Array。但是构成Tree时候,随Array的index而构架。 +- 也就是说,假如有Array[x,y,....]:在leaf,会有[0,0] with value = x. [1,1] with value = y. +- [但是这题] +- 构成时,是用actual value.也就是比如Array[x,y,....]会产生leaf:[x,x]with value = ..; [y,y]with value =... +- 其实很容易看穿: +- 若给出一个固定的array构成 SegmentTree,那估计很简单:按照index从0~array.lengh,leaf上就是[0,0] with value = x. +- 若题目让构造一个空心SegmentTree, `based on value 0 ~ n-1 (n <= 10000)`, 然后把一个Array的value modify 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**222. [My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)** Level: Medium Tags: [Array, TreeMap] + + +Given a list of interval as calendar items. Check if newly added calendar item is overlapping. + +Understand it is only checking time, but not requiring to insert into right spot. No need to overthink. + +#### Simply O(n) check on array +- number of test cases is small, like 1000, so less concern about the time complexity +- simply loop over the list of intervals, and check if any overlapping. +- where to insert does not really matter: every time we are just checking for overlaopping, not merging any range +- **IMPORTANT**: if interval over lapping, they will have this property `Math.max(s1, s2) < Math.min(e1, e2)`. This will help detect the overlapping very easily. +- O(n^2) runtime, with simple code. But somehow this approach is faster than the TreeMap solution: maybe the test cause causes avg O(n)? + +#### TreeMap +- One constraint from the simply array solution: it always cost O(n) to find the potential overlapping interval +- We can manually sort and always manually try to find the correct element via binary search, or we could store the interval in a treeMap, where the intervals are sorted by `start`. +- As result, all we need to do for book(start, end) is to find the next element ceiling(start), last element floor(start), and check for overlapping +- This approach also saves the custom data structure +- Overall cost O(nlogn) + +##### About TreeMap +- always with key sorted ascendingly +- more costly than regular HashMap because of the sorting. Building treemap of n items: O(nlogn) + +#### Sweep line +- use `Point{int start, end; boolean start}` to mark start/end of class. Add to pq. +- Adding new item to pq, sort, and check if overlapping occurs by counting started classes +- If started classes > 1, that means we overlapped. +- Every time it could consume all classes to find the overlap, O(n^2). +- Not quite need to sort or insert at correct point, and this solution requires longer code. Not quite worthy it for a simple problem. + + + + +--- + +**223. [Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Pairs.java)** Level: Medium Tags: [Binary Indexed Tree, Binary Search Tree, Divide and Conquer, Merge Sort, Segment Tree] + + +给一串数字, count total reverse pair `nums[i] > 2*nums[j]`, i < j + +This problem can be solved with Merge sort concept, BST, Segment Tree and Binary Indexed Tree. Good for learning/review. + +#### Merge Sort +- Using merge sort concept, not exaclty merge sort implementation. +- One very simply concept: if we want to know how many elements between [i, j] are meeting requirements of `nums[i] > 2*nums[j]`, it would be really helpful, if the entire range is sorted: +- then we just need to keep one i index, and keep j++ for all elements meeting requirement `j<=e && nums[i]/2.0 > nums[j]` +- Then it comes to the sorting part: we cannot just directly sort entire array, because the restriction is `all elements on right side of curr element`. BUT, it is okay to sort `right side range` and compare with left side elements : ) +- 灵感: use merge sort concept, divide and conquer: +- divide the elements from mid, compare each subarray +- sort once sub-array is completed (so that it can be used recursively at parent level) +- use classic while loop `while(j<=e && nums[i]/2.0 > nums[j])` to count pairs + + +#### Segment tree +- TODO +- split the array into index-based segment tree, where each element is at leaf +- store min of range: use max to determine if certain range is needed for further query +- query for each element right side range (i + 1, end), where it recursively query&aggregate sub-range if meeting requirement `nums[i] > 2*nums[j]` +- only when target > subRange.min * 2: there are possible candidates, query further +- worst case O(n^2) when all tailing elements are meeting requirement. + +#### BST +- TODO +- Build the BST based on node value. It will be not applicable if we search after entire tree is built (our goal is right range), so we need to build right elements, and search/count right after the elements is added +- Worst case is still O(n^2), if all added nodes are meeting requirement +- search(tree, curr / 2.0) + + + +#### O(n^2) +- check each one of them + + + + +--- + +**224. [Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Need to maintain k large elements, where the smallest will be compared and dropped if applicable: +- Maintain k elements with min value: consider using minHeap +- add k base elements first +- Maintain MinHeap: only allow larger elements (which will squzze out the min value) +- Remove peek() of queue if over size +- O(nlogk) + + +#### Quick Sort +- 用Quick Sort 里面partion的一部分 +- sort结束后是ascending的, 那么 n - k 就是第k大. +- partion的结果是那个low, 去找 low==nums.size() - k, 也就是倒数第K个。 +- 没找到继续partion recursively. +- sort的过程是排一个从小到大的list. (同样的代码还可以好xth smallest,mid变成x就好) +- Steps: +- 每个iteration, 找一个pivot,然后从low,和high都和pivot作比较。 +- 找到一个low>pivot, high() +- 2. Given list 里面也可能有null node, 不要忘记查. + +#### Divide and Conquer +- always merge 2 list at a time +- 3 branches: +- 1. start == end +- 2. start + 1 == end +- 3. or start + 1 < end (recursive and keep merging) +- T(k) = 2T(k/2) + O(mk), where m = longest list length +- time complexity: O(nklogk) +- TODO: write the recursive code. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**226. [Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20k%20Sorted%20Arrays.java)** Level: Medium Tags: [Heap, MinHeap, PriorityQueue] + + +Same as merge k sorted list, use priorityQueue + +#### Priority Queue +- 由Merge k sorted list启发。用PriorityQueue,存那k个首发element +- PriorityQueue需要存储单位: 自己建一个Class Node 存val, x, y index. +- 因为array里没有 'next' pointer,只能存x,y来推next element +- Not sure why `new PriorityQueue<>(Comparator.comparing(a -> a.val));` is slower + + + +--- + +**227. [Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/Heapify.java)** Level: Medium Tags: [Heap, MinHeap] + + +Turn unsorted array into a min-heap array, where for each A[i], + +A[i * 2 + 1] is the left child of A[i] and A[i * 2 + 2] is the right child of A[i]. + +#### Heap +- Heap用的不多. 得用一下, 才好理解. 通常default 的PriorityQueue就是给了一个现成的min-heap: +- 所有后面的对应element都比curr element 小。 +- Heapify里面的**siftdown**的部分: +- 只能从for(i = n/2-1 ~ 0), 而不能从for(i = 0 ~ n/2 -1): 必须中间开花,向上跑的时候才能确保脚下是符合heap规则的 + +#### Heapify/SiftDown做了什么? +- 确保在heap datastructure里面curr node下面的两个孩子,以及下面所有的node都遵循一个规律 +- 比如在这里,若是min-heap,就是后面的两孩子都要比自己大。若不是,就要swap。 + +#### min-heap的判断规律: +- for each element A[i], we will get A[i * 2 + 1] >= A[i] and A[i * 2 + 2] >= A[i]. +- siftdown时:在curr node和两个child里面小的比较。如果的确curr < child, 搞定,break while. +- 但若curr 并不比child小,那么就要换位子,而且继续从child的位子往下面盘查。 + + + +--- + +**228. [Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### HashMap + bucket List[] +- Use HashMap to store +- Reverse mapping in a `bucket = new List[n]`. +- Size of the data structure will be m <= n +- The bucket[count] preserves order from end of the array. +- Simply loop over the reversed map, we can find the top k items. +- Solid O(n) + +#### PriorityQueue, MinHeap +- Use regualr priorityQueue to sort by frequency ascendingly +- the queue.peek() record has lowest frequency, which is replacable +- Always only maintain k elements in the queue, so sorting is O(logk) +- IMPORTANT: remember to `rst.add(0, x)` for desired ordering +- time faster than maxHeap: O(nlogk) + +#### PriorityQueue, MaxHeap +- 题目有提醒: 必须beetter than O(nLog(n)), 也就是说明要O(n) +- 首先想到就是PriorityQueue, 并且不能queue.offer on the fly +- 那么就先count, O(n), using HashMap +- 再priorityQueue, (mLog(m)), m是unique 数字的总量 +- 最终find top k, O(k) +- Overall time: O(n) + O(mLogm) + O(k) => O(n), if m is small enough + + + + +--- + +**229. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + +time: O(n) +space: O(n) + +#### DP +- curr index is based on previous calculation: the min of all 3 previous factors +- O(n) + +#### PriorityQueue, DP +- 非常brutle的。 +- 每次把dp[i-1]拿出来,不管三七二十一,分别乘以2,3,5. 出来的结果放进priority queue做比较。 +- 最后时间是n*log(n*3) +- 注意:use long, use HashSet确保没有重复 +- O(nlogn) + + + + +--- + +**230. [Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)** Level: Medium Tags: [BST, Tree] + + +找 Inorder traversal规则里的下一个. + +主要想法是考虑: + 1. 如果 node.right == null, 找上一个unprocessed node alone the inorder traversal path + 2. 如果 node.right != null, successor 一定在这个node.right那个subtree里面 +最后竟然可以简化成几行, 非常全面的BST问题: 有search, 有对inorder traversal的理解, 还有坑. + +#### Short Recursive and Iterative without Stack +- Previous solution, we use stack to hold previous cached/unprocessed items: but do we need use catch to hold them? +- If moving left: `p.val < root.val`, then root (parent of left child) is a successor candidate, so save `rst = root`. +- If moving right or equal: `p.val >= root.val`, the successor has nothing to do with curr node, so just directly dive into root.right. +- Both iterative and recursive solution can be simplified as such. + + +#### Previous Iterative + stack +- Iteratively search +- Still need stack to store previously unprocessed items along the path + +#### Previous Recursive + Stack +- 画inorder图,发现规律.每个node的后继node(successor)有几种情况: +- 1. node.right 是个leaf到底了。那么就return. +- 2. set rightNode = node.right, 但发现rightNode has a lot left children to leaf. +- 3. 比如, node.right == null, 也就是node自己是leaf,要回头看山顶找Inorder traversal规则里的下一个。 +- 发现:其实就是每层都把路过的curr node放在stack里,最上面的,就是当下改return的那个successor:) Done. + + + +--- + +**231. [Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)** Level: Medium Tags: [BFS, DFS] + + +给一个room 2D grid. 里面有墙-1, 门0, 还有empty space INF(Math.MAX_VALUE). + +对每个empty space而言, fill it with dist to nearest gate. + +#### DFS +- Form empty room: it can reach different gate, but each shortest length will be determined by the 4 directions. +- Option1(NOT applicable). DFS on INF, mark visited, summerize results of 4 directions. +- hard to resue: we do not know the direction in cached result dist[i][j] +- Option2. DFS on gate, and each step taken to each direction will +1 on the spot: distance from one '0'; +- Through dfs from all zeros, update each spot with shorter dist +- Worst time: O(mn), where entre rooms[][] are gates. It takes O(mn) to complete the iteration. Other gates be skipped by `if (rooms[x][y] <= dist) return;` + +#### BFS +- Exact same concept. Init with `Queue queue = new LinkedList()` + + + +--- + +**232. [Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + +time: O(n) +space: O(1) + +题目描述起来有点复杂, 简而言之: 把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal +- 平时做过convert BST to sored list: 画一下就理解, 其实就是in-order traversal +- 只不过做的时候要小心地 doubly link them +- 理解之后就简单了, traverse all nodes, DFS 好做: `left, curr, right` + +##### 题目特殊特点 +- 自始至终用了同一个 `Node {val, left, right}`, 而并不是开一个新的doubley linked list class +- extra space 的问题, 是因为它需要create new DoublyLinkedNode class: different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` +- 要求in-place: 不能重新create new node + + + +--- + +**233. [String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- check sign, leading-0, overall size > 11, check max/min in Long format +- if passed all tests, parseInt() + +#### regular expression +- if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**234. [Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Clone%20Graph.java)** Level: Medium Tags: [BFS, DFS, Graph] + + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes. +- 先能复制多少Node复制多少. 然后把neighbor 加上 +- Use `map` to mark visited + +#### DFS +- Given graph node obj `{val, list of neighbor}`: copy the node and all neighbors +- Mark visited using map +- for loop on the each one of the neighbors: map copy, record in map, and further dfs +- once dfs completes, add newNeighbor as neighbor of the new node (get to it via map) +- 主要思想是: 一旦复制过了, 不必要重新复制 + +#### BFS +- Copy the root node, then copy all the neighbors. +- Mark copied node in map. +- Use queue to contain the newly added neighbors. Need to work on them in the future. + + + +--- + +**235. [Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutations.java)** Level: Medium Tags: [Backtracking, DFS, Permutation] + + +#### Recursive: Backtracking +- Given a remaining list: 取, 或者不取 +- always iterate over full `nums[]`, use list.contains() to check if item has been added. +- Improvement: maintain list (add/remove elements) instead of 'list.contains' +- time O(n!): visit all possible outcome +- T(n) = n * T(n-1) + O(1) + +#### Iterative: Insertion +- 插入法: +- 1. 一个一个element加进去 +- 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element +- 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- 但是比dfs要快, 因该是因为 # of checks 少: 不需要check list.size(), 不需要maintain remaining list. + +#### Previous Notes +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- A bit slower, possibly because of the polling and saving the entire list every time + + + + +--- + +**236. [One Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/One%20Edit%20Distance.java)** Level: Medium Tags: [String] + + +如果S, T只用一个operation就能变成相等, return true. + +#### Edit: 删除,增加,和替换 +- 换完之后,理论上换成的String 就应该全等 +- for loop, 一旦找到不一样的char, 就判断那三种可能性: insert/delete/replace +- insert/delete 对于2个string来说, 效果是类似的 +- O(n) + + + +--- + +**237. [4Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/4Sum.java)** Level: Medium Tags: [Hash Table] + + +#### Based on 2sum +- 1. 利用2Sum的原理,把4Sum分为连个2Sum。左一个pair,右一个pair,每个pair里面放2个数字。 +- 2. 以一个点,i,作为分界口,也要列举出所有i之前的pair,作为基础。 +- 3. 再尝试从所有i+1后面,找合适的2nd pair。 +- Time: O(n^2 * x), where x = # of candidates, still slow +- 可以用HashSet, 可以直接比较list里面每一个元素, 保证set不重复. +- Previous Notes: 在造class Pair时候,要做@override的function: hashCode(), equals(Object d). 平时不太想得起来用。 +- 参见 http://lifexplorer.me/leetcode-3sum-4sum-and-k-sum/ + +#### Based on 3Sum +- 3Sum外面再加一层. 参考3Sum. 时间O(n^3)。 但此方法在k-sum时候,无疑过于费时间. O(n^k) + + + +--- + +**238. [Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)** Level: Medium Tags: [BFS, DFS, Graph, Tree, Union Find] + + +#### unionFind +- keyword: tree has no `cycle`. +- 一旦两个node在edge中出现, 并且parent相同, 说明这两个node不union, 也在同一个tree里面, 所以可以break them. + +#### Graph, DFS +- Add graph using adjacent list, and verify cycle alone the way +- IMPORTANT: use `pre` node in dfs to prevent backward dfs +- similar to `Graph Valid Tree` where it validates cycle and also needs to validate if all nodes are connected + +#### BFS +- same concept as DFS, find first redundant edge that alreay exists in graph map. + + + +--- + +**239. [Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Union Find +- 复习Union-Find的另外一个种形式, track union size: tree does not have cycle, so eventually union size should == 1 +- 1. 查找2个元素是不是在一个union里面。如果不在,false. 如果在,那就合并成一个set,共享parent. +- 2. 验证cycle: `find(x) == find(y) => cycle`: new index has been visited before +- 存储的关键都是:元素相对的index上存着他的root parent. +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ + +#### DFS +- Very similar to `Redundant Connection` +- Create adjacent list graph: Map> +- 检查: +- 1. 是否有cycle using dfs, check boolean[] visited +- 2. 是否所有的node全部链接起来: validate if all edge connected: # of visited node should match graph size +- IMPORTANT: use `pre` node to avoid linking backward/infinite loop such as (1)->(2), and (2)->(1) + +#### BFS +- (还没做, 可以写一写) +- 也是检查: 1. 是否有cycle, 2. 是否所有的node全部链接起来 + + + +--- + +**240. [The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)** Level: Medium Tags: [BFS, DFS] + + +#### BFS +- BFS on coordinates +- always attempt to move to end of border +- use boolean[][] visited to alingn with BFS solution in Maze II, III, where it uses Node[][] to store state on each item. + + + +--- + +**241. [The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)** Level: Medium Tags: [BFS, DFS, PriorityQueue] + + +#### BFS +- if already found a good/shorter route, skip +- `if (distMap[node.x][node.y] <= node.dist) continue;` +- This always terminates the possibility to go return to original route, because the dist will be double/higher + + + +--- + +**242. [Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)** Level: Medium Tags: [DP, MiniMax] + + +Detailed in `Coins in a Line III` + + + +--- + +**243. [Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)** Level: Medium Tags: [Hash Table, String] + + + +#### Convert to orginal string +- shit by offset. `int offset = s.charAt(0) - 'a';` +- increase if less than 'a': `if (newChar < 'a') newChar += 26;` + +#### Previous notes +- 相同shift规则的string, 能被推算到同一个零起始点,就是共同减去一个char,最后就相等。以此作为key,用HashMap。一目了然。 +- 记得根据题目意思,一开始要String[] sort一下。 + + + +--- + +**244. [Delete Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Delete%20Digits.java)** Level: Medium Tags: [Greedy, Priority Queue] + + +#### Priority Queue +- TODO: parse into node(index, digitValue) +- find the top k, and remove from char array +- O(nlogn) time + +#### Greedy +- 数位靠前的,权值更大. 所以硬来把靠前的相对更大的(跟following digit相比)去掉。 + + + +--- + +**245. [Flatten 2D Vector.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%202D%20Vector.java)** Level: Medium Tags: [Design] + + +Implement an iterator to flatten a 2d vector. + +Just move pointers carefully with next(), hashNext() + +#### Basic Implementation using x, y corrdinate +- 就是把2D list里面的element全部遍历一遍。 +- 跟一个nxn的matrix遍历,是没区别的拉; 所有来个x,y,把2d list跑一变。 + +#### Always return item at index 0, and remove from list? +- list 方便remove, 考虑吧reduce input vector (就像给的是linked list 一样) + + + +--- + +**246. [The Spiral Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Spiral%20Matrix%20II.java)** Level: Medium Tags: [Array] + + +#### Move forward till end +- Similar concept as `The Maze`: keep walking until hit wall, turn back +- fix direction `dx[direction % 4]` + + + +--- + diff --git a/review/level/NA.md b/review/level/NA.md new file mode 100644 index 0000000..a5c3183 --- /dev/null +++ b/review/level/NA.md @@ -0,0 +1,4 @@ + + + +## NA (19) diff --git a/review/level/Review.md b/review/level/Review.md new file mode 100644 index 0000000..64762ef --- /dev/null +++ b/review/level/Review.md @@ -0,0 +1,101 @@ + + + +## Review (5) +**0. [Maximum Subarray III.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20III.java)** Level: Review Tags: [] + + + +--- + +**1. [Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/Valid%20Perfect%20Square.java)** Level: Review Tags: [Binary Search, Math] + + +Binary找sqrt. 基本 mid+1, mid-1 template. +注意: define index as long. + + + +--- + +**2. [Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)** Level: Review Tags: [Array, Binary Search, PreSum] + + +给int[] nums 和 window min size k. window size可以大于K. 找最大的连续数列average value. + +- Binary Search的思想, 用在所要找的这个 average sum 上面. 大小是在[min, max]之中 +- 找k的时候, 是>=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + + + +--- + +**3. [The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Skyline%20Problem.java)** Level: Review Tags: [Binary Indexed Tree, Divide and Conquer, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +又叫做skyline. 用Sweep Line做的O(nLogN), 但是貌似还有很多做法: segement tree, hashheap, treeSet? + +#### Sweep Line, Time O(nLogN), Space O(n) +- original reference http://codechen.blogspot.com/2015/06/leetcode-skyline-problem.html?_sm_au_=isVmHvFmFs40TWRt +- 画图分析: 需要找到 non-overlaping height point at current index; also height needs to be different than prev height peek to be visible. +- 把所有点分出来, 每个点有index x, 再加上一个height. +- 在这个list上排序,根据index和height. 注意用负数标记building start point height, 这样保证start在end 之前 +- 用负数的height标记start: 在priority queue里面同一个x-pos比较 startPoint.height, endPoint.height 的时候, 因为end height是整数, 所以compare时会自动把start point放在end point前面 +- 当然了, 如果两个 start point比较, 第二个point的负数超大的话(也就是height很高), 就会顺理compare return正数, 成章形成倒位 +- 在processs时候用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height . 遇到peek,就是一个合理的解 +- heightQueue里面加一个0, 用来在结尾的时候做closure + +#### Segment Tree +- 看了一些做法, segment tree写法很复杂, 估计在面试中难以用segment tree来写: https://www.cnblogs.com/tiezhibieek/p/5021202.html + +#### HashHeap +- HashHeap template 可以考虑: https://www.jiuzhang.com/solution/building-outline/#tag-highlight-lang-java + +Binary Indexed Tree? + + + + + +--- + +**4. [Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Invalid%20Parentheses.java)** Level: Review Tags: [BFS, DFS, DP] + + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- in dfs: remove the incorrect parentheses one at a time +- detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` +- once detected, remove the char from middle of s, and dfs on the rest of the s that has not been tested yet. + +##### Core concept: reverse test +- `if a parenthese string is valid, the reverse of it should also be valid` +- Test s with open='(', close=')' first; **reverse s**, and test it with open=')', close='(' + +##### Minor details +- only procceed to remove invalid parenthese when `count<0`, and also break && return dfs after the recursive calls. +- The above 2 facts eliminates all the redundant results. +- Reverse string before alternating open and close parentheses, so when returning final result, it will return the correct order. +- Open questions: how does it guarantee minimum removals? + +##### Backtracking +- 如果用stringbuffer, 那么久不会每次create new string, 但是需要maintain这个string buffer, 就会backtracking + +##### Complexity +- Seems to be O(n), but need to derive + +#### BFS +TODO + +#### DP + + + +--- +