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$1.class b/GenerateCodeTable$1.class new file mode 100644 index 0000000..3f24036 Binary files /dev/null and b/GenerateCodeTable$1.class differ 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..91a6db3 100644 --- a/GenerateCodeTable.java +++ b/GenerateCodeTable.java @@ -1,113 +1,510 @@ -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.*; +import java.util.stream.*; +import java.text.SimpleDateFormat; + +/* +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 String GIT_HUB_LINK = "https://github.com/awangdev/LintCode/blob/master/Java/"; + private static String NOT_AVAILABLE = "N/A"; + private static String LINTCODE_TOKEN = "[lint]"; + private static String TOOL_TOKEN = "[tool]"; + private static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyy-MM-dd"); + /* + 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 timeComplexity; + private String spaceComplexity; + private String note; + private List tags; + + public TableRow( + long date, + String fileName, + String level, + String tutorialLink, + String timeComplexity, + String spaceComplexity, + String note, + List tags) { + this.date = date; + this.fileName = fileName; + this.level = level; + this.timeComplexity = timeComplexity; + this.spaceComplexity = spaceComplexity; + this.tutorialLink = tutorialLink; + this.note = note; + this.tags = tags; + } + + public String getLeetcodeNum() { + String[] fileNameArr = fileName.split("\\."); + if (LINTCODE_TOKEN.equals(fileNameArr[0].toLowerCase())) { + return LINTCODE_TOKEN; + } + if (TOOL_TOKEN.equals(fileNameArr[0].toLowerCase())) { + return TOOL_TOKEN; + } + try { + Integer.parseInt(fileNameArr[0]); + return fileNameArr[0]; + } catch(Exception e) { + return NOT_AVAILABLE; + } + } + + 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(int i) { + Date date = new Date(this.date); + return "|" + this.getLeetcodeNum() + "|[" + this.fileName + "](" + GIT_HUB_LINK + fileName.replace(" ", "%20") + + ")|" + this.level + "|" + this.tags + "|" + this.timeComplexity + "|" + this.spaceComplexity + "|Java|" + i + "|\n"; + } + } + + public static String TUTORIAL_KEY_WORD = "tutorial:"; + public static String TAGS_KEY_WORD = "tags:"; + public static String TIME_KEY_WORD = "time:"; + public static String SPACE_KEY_WORD = "space:"; + 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++; + } + } + + 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" + + "| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence |\n" + + "|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:|\n"; + List tableRows = getTableRows(listOfFiles); + for (int i = 0; i < tableRows.size(); i++) { + outputContent += tableRows.get(i).getTableComposedLine(i); + } + return outputContent; + } + + /* Generate the tags Table*/ + public String generateTagREADME(File[] listOfFiles) { + String outputContent = generateTableOfContent("TagREADME.md") + "\n\n"; + String header = "| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence |\n" + + "|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:|\n"; + List tableRows = getTableRows(listOfFiles); + Map> 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(entryTableRows.get(i).getTableComposedLine(i)); + } + outputContent += sb.toString() + "\n\n\n"; + } + return outputContent; + } + + // Generate review files by tags + public String generateTagReviewPage(File[] listOfFiles) { + List tableRows = getTableRows(listOfFiles); + Map> 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) { + List tableRows = getTableRows(listOfFiles); + Map> 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"; + + 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("](" + GIT_HUB_LINK); + 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) { + List tableRows = new ArrayList<>(); + for (File file : listOfFiles) { + if (file.getName().contains(".java")) { + tableRows.add(getTableRow(file.getName())); + } + } + List nonProcessedRows = tableRows.stream() + .filter(tableRow -> tableRow.getLeetcodeNum().equals(NOT_AVAILABLE)) + .collect(Collectors.toList()); + List processedLeetCodeRows = tableRows.stream() + .filter(tableRow -> !tableRow.getLeetcodeNum().equals(NOT_AVAILABLE) + && !tableRow.getLeetcodeNum().equals(LINTCODE_TOKEN) && !tableRow.getLeetcodeNum().equals(TOOL_TOKEN) + ) + .collect(Collectors.toList()); + List processedLintCodeRows = tableRows.stream() + .filter(tableRow -> tableRow.getLeetcodeNum().equals(LINTCODE_TOKEN)) + .collect(Collectors.toList()); + List processedToolCodeRows = tableRows.stream() + .filter(tableRow -> tableRow.getLeetcodeNum().equals(TOOL_TOKEN)) + .collect(Collectors.toList()); + Collections.sort(processedLeetCodeRows, Comparator.comparing(TableRow::getDate)); + Collections.sort(processedLintCodeRows, Comparator.comparing(TableRow::getDate)); + System.out.println("Non Reviewed Problems: " + nonProcessedRows.size() + + ", Reviewed LeetCode Problems: " + processedLeetCodeRows.size() + + ", Reviewed LintCode Problems: " + processedLintCodeRows.size()); + List output = new ArrayList<>(); + output.addAll(nonProcessedRows); + output.addAll(processedLintCodeRows); + output.addAll(processedToolCodeRows); + output.addAll(processedLeetCodeRows); + return output; + } + + private TableRow getTableRow(String fileName) { + TableRow tableRow = null; + String tutorialLink = ""; + String calculatedLevel = NOT_AVAILABLE; + String timeComplexity = ""; + String spaceComplexity = ""; + long timestamp = 0; + List tags = new ArrayList<>(); + + // get timestamp of file + File file = new File("Java/" + fileName); + timestamp = file.lastModified(); + + try { + 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(); + } + + // TODO: remove the legacy timestamp. + // Below are existing logic to parse timestamp. Remove when all timestamp is cleaned up. + if (line.length() != 0) { + try{ + Long.parseLong(line); + line = reader.readLine().trim(); + } catch(Exception e){ } + } + + // 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); + line = reader.readLine().trim(); + } + + // Get Time + if (line.contains(TIME_KEY_WORD)) { + timeComplexity = line.substring(6).trim(); // "time:" + line = reader.readLine().trim(); + } + + // Get Space + if (line.contains(SPACE_KEY_WORD)) { + spaceComplexity = line.substring(7).trim(); // "space:" + } + + // 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, timeComplexity, spaceComplexity, note, tags); + } catch (Exception e) { + System.err.format("IOException: %s%n. Filename: %s", e, fileName); + } + return tableRow; + } + + private String calculateLevel(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 (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. Two Sum.java b/Java/1. Two Sum.java new file mode 100755 index 0000000..a40872f --- /dev/null +++ b/Java/1. Two Sum.java @@ -0,0 +1,141 @@ +E +tags: Array, Hash Table +time: O(n) +space: O(n) + +#### 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/10. Regular Expression Matching.java b/Java/10. Regular Expression Matching.java new file mode 100755 index 0000000..40080e5 --- /dev/null +++ b/Java/10. Regular Expression Matching.java @@ -0,0 +1,187 @@ +H +tags: String, DP, Sequence DP, Double Sequence DP, Backtracking + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + +``` +/* +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 +*/ + +/* +Method1: +DFS on remaining of s and p. Analyze the different cases. +End case: both i,j reached end true; or one of them reached end. + +The two different cases: given any index j on p, check if the j+1=='*' +- YES: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p +- NO: check i,j, and move forward with s[i+1], p[j+1] +*/ + +class Solution { + int n, m; + public boolean isMatch(String s, String p) { + n = s.length(); + m = p.length(); + + return dfs(s, 0, p, 0); + } + + private boolean dfs(String s, int i, String p, int j) { + if (j >= m) return i >= n; + + char pc = p.charAt(j); + if (j+1 < m && p.charAt(j + 1) == '*') { + if (dfs(s, i, p, j+2)) return true; // when * reuslts in 0, move j + if (i < n && (pc == '.' || s.charAt(i) == pc)) return dfs(s, i+1, p, j); // when * reuslts not as 0, move i + return false; + } + if (i < n && (pc == '.' || s.charAt(i) == pc)) { // next pc not equal to '*' + return dfs(s, i+1, p, j+1); + } + return false; + } +} + +// DFS + Memoization +class Solution { + int n, m; + Boolean[][] memo; + public boolean isMatch(String s, String p) { + n = s.length(); + m = p.length(); + memo = new Boolean[n+2][m+2]; + return dfs(s, 0, p, 0); + } + + private boolean dfs(String s, int i, String p, int j) { + if (j >= m) return i >= n; + if (memo[i][j] != null) return memo[i][j]; + + char pc = p.charAt(j); + if (j+1 < m && p.charAt(j + 1) == '*') { + // when * reuslts in 0 [elm], move j + memo[i][j+2] = dfs(s, i, p, j+2); + if (memo[i][j+2]) return true; + + // when * reuslts not into 1 or more [elm], move i + if (i < n && (pc == '.' || s.charAt(i) == pc)) { + memo[i+1][j] = dfs(s, i+1, p, j); + return memo[i+1][j]; + } + } else if (i < n && (pc == '.' || s.charAt(i) == pc)) { // next pc not equal to '*' + memo[i+1][j+1] = dfs(s, i+1, p, j+1); + return memo[i+1][j+1]; + } + memo[i][j] = false; + return memo[i][j]; + } +} + + +/* +Method2: DP +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/100. Same Tree.java b/Java/100. Same Tree.java new file mode 100755 index 0000000..cc8c42a --- /dev/null +++ b/Java/100. Same Tree.java @@ -0,0 +1,100 @@ +E +tags: Tree, DFS, BFS +time: O(n) +space: O(logn) + +给两个 binary tree, 看两个tree是否identical. + +#### Method1: DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### Method2: BFS with 2 queues +- 两个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; } + * } + */ +/* +Method1: DFS +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); + } +} + +// Method2: BFS +class Solution { + public boolean isSameTree(TreeNode p, TreeNode q) { + Queue pp = new LinkedList<>(); + Queue qq = new LinkedList<>(); + pp.offer(p); + qq.offer(q); + + while (!pp.isEmpty() && !qq.isEmpty()) { + p = pp.poll(); + q = qq.poll(); + if (p == null && q == null) continue; + if (p == null ^ q == null) return false; + if (p.val != q.val) return false; + offer(p, pp); + offer(q, qq); + } + + return true; + } + + private void offer(TreeNode node, Queue queue) { + queue.offer(node.left); + queue.offer(node.right); + } +} +``` \ No newline at end of file diff --git a/Java/1004. Max Consecutive Ones III.java b/Java/1004. Max Consecutive Ones III.java new file mode 100755 index 0000000..5c8c823 --- /dev/null +++ b/Java/1004. Max Consecutive Ones III.java @@ -0,0 +1,80 @@ +M +tags: Sliding Window, Two Pointers +time: O(n) +space: O(1) + + +#### Sliding window + Left/Right Two Pointers +- https://leetcode.com/problems/max-consecutive-ones-iii/solution/ +- Start with DFS thought, but realize redundant calculations: + - we never need to flip 2 indexes [A], [C] from 0 -> 1, if there is a [B] in middle that is 0 too + - the flipped k zeroes must be consecutive too +- we can utilize two pointers to establish a window that captures k zeroes + - always expend right pointer; if seeing an zero, k-- + - note: `len = right - left + 1` is the ongoing max length + - when k < 0 (too many zeros), we need to slide the left side of the window to make sure: + - keep window len + - potentially do k++ when A[left]==0 +- goal: matain a max size of the window, until right == n +- return (right - left). at this moment, right == n, so no need to (right - left + 1) + +``` + +/* +Given an array A of 0s and 1s, we may change up to K values from 0 to 1. + +Return the length of the longest (contiguous) subarray that contains only 1s. + + + +Example 1: + +Input: A = [1,1,1,0,0,0,1,1,1,1,0], K = 2 +Output: 6 +Explanation: +[1,1,1,0,0,1,1,1,1,1,1] +Bolded numbers were flipped from 0 to 1. The longest subarray is underlined. +Example 2: + +Input: A = [0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], K = 3 +Output: 10 +Explanation: +[0,0,1,1,1,1,1,1,1,1,1,1,0,0,0,1,1,1,1] +Bolded numbers were flipped from 0 to 1. The longest subarray is underlined. + + +Note: + +1 <= A.length <= 20000 +0 <= K <= A.length +A[i] is 0 or 1 +*/ +/* +- Start with DFS thought, but realize redundant calculations: + - we never need to flip 2 indexes [A], [C] from 0 -> 1, if there is a [B] in middle that is 0 too + - the flipped k zeroes must be consecutive too +- we can utilize two pointers to establish a window that captures k zeroes + - always expend right pointer; if seeing an zero, k-- + - note: `len = right - left + 1` is the ongoing max length + - when k < 0 (too many zeros), we need to slide the left side of the window to make sure: + - keep window len + - potentially do k++ when A[left]==0 +- goal: matain a max size of the window, until right == n +- return (right - left). at this moment, right == n, so no need to (right - left + 1) +*/ +class Solution { + + public int longestOnes(int[] A, int k) { + int left = 0, right = 0, n = A.length; + while (right < n) { + if (A[right++] == 0) k--; + if (k < 0) { + if (A[left] == 0) k++; + left++; + } + } + return right - left; // at the end, right == n + } + +} +``` \ No newline at end of file diff --git a/Java/1007. Minimum Domino Rotations For Equal Row.java b/Java/1007. Minimum Domino Rotations For Equal Row.java new file mode 100755 index 0000000..b5baf80 --- /dev/null +++ b/Java/1007. Minimum Domino Rotations For Equal Row.java @@ -0,0 +1,164 @@ +M +tags: Array, Greedy +time: O(n) +space: O(1) + + +#### Method1: Count all occurrance, and count on overlap indexes +- when there is a value that can cover entire row of size n + - it must be: `n = countA[i] + countB[i] - overlap[i]` +- Code easy to write and read +- time: O(n) +- space: O(1) + +#### Method2: Negative count +- Observation: if A[0] works, no need to check B[0]. +- Because if both A[0] and B[0] exist in all dominoes, + - when you swap A[0] in a whole row, + - you will swap B[0] in a whole at the same time. + - The result of trying A[0] and B[0] will be the same. +- time: O(n) +- space: O(1) + +#### Method3: positive count Match +- there should exist 1 numbers, that can appear in (A[i], B[i]). +- failure case: there exist at least 1 index, that does not have the common number +- maximum case: there can be 2 numbers, that both will make it work. +- findCommon2, and count them: + - set.add(A[0], A[B]), + - if any new one does not exist in set, remove it from set + - if set is empty() , return -1 +- use the 2 numbers from set to do a sweep and count in A, O(n), return the less appearance one. +- time: O(n) +- space: O(1) + +``` + +/** +In a row of dominoes, A[i] and B[i] represent the top and bottom halves of the i-th domino. (A domino is a tile with two numbers from 1 to 6 - one on each half of the tile.) + +We may rotate the i-th domino, so that A[i] and B[i] swap values. + +Return the minimum number of rotations so that all the values in A are the same, or all the values in B are the same. + +If it cannot be done, return -1. + + +Example 1: + + + +Input: A = [2,1,2,4,2,2], B = [5,2,6,2,3,2] +Output: 2 +Explanation: +The first figure represents the dominoes as given by A and B: before we do any rotations. +If we rotate the second and fourth dominoes, we can make every value in the top row equal to 2, as indicated by the second figure. +Example 2: + +Input: A = [3,5,1,2,3], B = [3,6,3,3,4] +Output: -1 +Explanation: +In this case, it is not possible to rotate the dominoes to make one row of values equal. + + +Note: + +1 <= A[i], B[i] <= 6 +2 <= A.length == B.length <= 20000 +*/ + +/* +#### Method1: Count all occurrance, and count on overlap indexes + - when there is a value that can cover entire row of size n + - it must be: n = countA[i] + countB[i] - overlap[i]. + - return a min flip count +- time: O(n) +- space: O(1) +*/ +class Solution { + public int minDominoRotations(int[] A, int[] B) { + int[] countA = new int[7], countB = new int[7], overlap = new int[7]; + int n = A.length; + for (int i = 0; i < n; ++i) { + countA[A[i]]++; + countB[B[i]]++; + if (A[i] == B[i]) overlap[A[i]]++; + } + for (int i = 1; i < 7; ++i) { + if (countA[i] + countB[i] - overlap[i] == n) return n - Math.max(countA[i], countB[i]); + } + return -1; + } +} + +/* +#### Method2: Negative count +- Observation: if A[0] works, no need to check B[0]. + - Because if both A[0] and B[0] exist in all dominoes, + - when you swap A[0] in a whole row, + - you will swap B[0] in a whole at the same time. + - The result of trying A[0] and B[0] will be the same. +- time: O(n) +- space: O(1) +*/ +class Solution { + public int minDominoRotations(int[] A, int[] B) { + if (A == null || B == null || A.length == 0 || B.length == 0) return 0; + + int rotationWithA = checkRotation(A, B, A[0]); + if (rotationWithA != -1 || A[0] == B[0]) return rotationWithA; + return checkRotation(A, B, B[0]); // if trial with A[0], try B[0] + } + + private int checkRotation(int[] A, int[] B, int candidate) { + int rotationA = 0, rotationB = 0; + for (int i = 0; i < A.length; i++) { + if (A[i] != candidate && B[i] != candidate) return -1; + else if (A[i] != candidate) rotationA++; // flip A to set to correct candidate + else if (B[i] != candidate) rotationB++; + } + return Math.min(rotationA, rotationB); + } +} + + +/* +#### Method3: positive count Match +- there should exist 1 numbers, that can appear in (A[i], B[i]). +- failure case: there exist at least 1 index, that does not have the common number +- maximum case: there can be 2 numbers, that both will make it work. +- findCommon2, and count them: + - set.add(A[0], A[B]), + - if any new one does not exist in set, remove it from set + - if set is empty() , return -1 +- use the 2 numbers from set to do a sweep and count in A, O(n), return the less appearance one. +- time: O(n) +- space: O(1) +*/ +class Solution { + public int minDominoRotations(int[] A, int[] B) { + if (A == null || B == null || A.length == 0 || B.length == 0) return 0; + int n = A.length; + // validation + Integer a = A[0], b = B[0]; + for (int i = 1; i < n; i++) { + if (a != null && a != A[i] && a != B[i]) a = null; + if (b != null && b != A[i] && b != B[i]) b = null; + if (a == null && b == null) return -1; + } + + if (a == null) return n - Math.max(count(B, b), count(A, b)); + if (b == null) return n - Math.max(count(B, a), count(A, a)); + int countA = n - Math.max(count(A, a), count(A, b)); + int countB = n - Math.max(count(B, a), count(B, b)); + return Math.min(countA, countB); + } + + private int count(int[] A, Integer candidate) { + int count = 0; + for (int num : A) count += (candidate == num) ? 1 : 0; + return count; + } +} + +``` \ No newline at end of file diff --git a/Java/1008. Construct Binary Search Tree from Preorder Traversal.java b/Java/1008. Construct Binary Search Tree from Preorder Traversal.java new file mode 100755 index 0000000..c35f86f --- /dev/null +++ b/Java/1008. Construct Binary Search Tree from Preorder Traversal.java @@ -0,0 +1,76 @@ +M +tags: DFS, Tree +time: O(n) +space: O(n) + +#### Method1: Top Down DFS +- This approach highly relies on the preorder rules + - we can use validation rules to navigate throug hteh preorder array + - use a global index +- time: O(n) + + +``` +/** +Return the root node of a binary search tree that matches the given preorder traversal. + +(Recall that a binary search tree is a binary tree where for every node, any descendant of node.left has a value < node.val, and any descendant of node.right has a value > node.val. Also recall that a preorder traversal displays the value of the node first, then traverses node.left, then traverses node.right.) + + + +Example 1: + +Input: [8,5,1,7,10,12] +Output: [8,5,10,1,7,null,12] + + + +Note: + +1 <= preorder.length <= 100 +The values of preorder are distinct. +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Method1: Top Down O(n) +- Use preorder to pick one index at a time +*/ +class Solution { + int index = 0; + public TreeNode bstFromPreorder(int[] preorder) { + if (preorder == null || preorder.length == 0) return null; + + return dfs(preorder, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + private TreeNode dfs(int[] preorder, int min, int max) { + if (index == preorder.length) return null; + int val = preorder[index]; + + if (val <= min || val >= max) return null; + index++; + TreeNode node = new TreeNode(val); + node.left = dfs(preorder, min, val); + node.right = dfs(preorder, val, max); + return node; + } +} + +/* +Alternatives tried: use start as root; in remaining array, search for mid point such that +- nums[mid] < root.val < nums[mid + 1] +- we can split the array into 2 parts and build sub tree accordingly +- time: O(n * logn) since it requires search. Not prefered + +*/ + +``` \ No newline at end of file diff --git a/Java/101. Symmetric Tree.java b/Java/101. Symmetric Tree.java new file mode 100755 index 0000000..e7a8ff7 --- /dev/null +++ b/Java/101. Symmetric Tree.java @@ -0,0 +1,151 @@ +E +tags: Tree, DFS, BFS +time: O(n) +space: O(n) + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### Method1: DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Method2: interative with queue +- put left or right children in pair + +#### Method3: iterative with 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); + } + + private boolean dfs(TreeNode left, TreeNode right) { + if (left == null && right == null) return true; + if (left == null || right == null) return false; + return left.val == right.val + && dfs(left.right, right.left) + && dfs(left.left, right.right); + } +} + +//Non-recursive, iterative + +class Solution { + public boolean isSymmetric(TreeNode root) { + if (root == null) return true; + Queue queue = new LinkedList<>(); + queue.offer(root.left); + queue.offer(root.right); + + while (!queue.isEmpty()) { + TreeNode left = queue.poll(); + TreeNode right = queue.poll(); + + if (left == null && right == null) continue; + if (left == null || right == null) return false; + if (left.val != right.val) return false; + queue.offer(left.left); + queue.offer(right.right); + queue.offer(left.right); + queue.offer(right.left); + } + + return true; + } +} + +/* + 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/102. Binary Tree Level Order Traversal.java b/Java/102. Binary Tree Level Order Traversal.java new file mode 100755 index 0000000..19cdbe1 --- /dev/null +++ b/Java/102. Binary Tree Level Order Traversal.java @@ -0,0 +1,106 @@ +M +tags: Tree, BFS, DFS +time: O(n) +space: O(n) + +如题. + +#### Method1: BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### Method2: 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; + * } + * } + */ +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) { + 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; + } +} + +//Method2: DFS +//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 List> levelOrder(TreeNode root) { + List> result = new ArrayList<>(); + if (root == null) return result; + + dfs(root, 0, result); + return result; + } + + public void dfs(TreeNode root, int level, List> 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/Java/1021. Remove Outermost Parentheses.java b/Java/1021. Remove Outermost Parentheses.java new file mode 100755 index 0000000..657ce43 --- /dev/null +++ b/Java/1021. Remove Outermost Parentheses.java @@ -0,0 +1,99 @@ +E +tags: Stack + +#### Stack +- use stack to hold potential pair +- when stack is empty: detect outtermost element, dont add to final result +- time: O(n), space O(n) + +#### Count occurance +- solution from discussion, time O(n), space O(1) +- save space, but less scalable: think about if there are 100 different pairs, then the couting will be a bit complex to handle. + +``` +/* +A valid parentheses string is either empty (""), "(" + A + ")", or A + B, where A and B are valid parentheses strings, and + represents string concatenation. For example, "", "()", "(())()", and "(()(()))" are all valid parentheses strings. + +A valid parentheses string S is primitive if it is nonempty, and there does not exist a way to split it into S = A+B, with A and B nonempty valid parentheses strings. + +Given a valid parentheses string S, consider its primitive decomposition: S = P_1 + P_2 + ... + P_k, where P_i are primitive valid parentheses strings. + +Return S after removing the outermost parentheses of every primitive string in the primitive decomposition of S. + + + +Example 1: + +Input: "(()())(())" +Output: "()()()" +Explanation: +The input string is "(()())(())", with primitive decomposition "(()())" + "(())". +After removing outer parentheses of each part, this is "()()" + "()" = "()()()". +Example 2: + +Input: "(()())(())(()(()))" +Output: "()()()()(())" +Explanation: +The input string is "(()())(())(()(()))", with primitive decomposition "(()())" + "(())" + "(()(()))". +After removing outer parentheses of each part, this is "()()" + "()" + "()(())" = "()()()()(())". +Example 3: + +Input: "()()" +Output: "" +Explanation: +The input string is "()()", with primitive decomposition "()" + "()". +After removing outer parentheses of each part, this is "" + "" = "". + + +Note: + +S.length <= 10000 +S[i] is "(" or ")" +S is a valid parentheses string + + */ + +class Solution { + public String removeOuterParentheses(String S) { + if (S == null || S.length() == 0) return S; + + Stack stack = new Stack<>(); + StringBuffer sb = new StringBuffer(); + + for (char c : S.toCharArray()) { + if (stack.isEmpty()) { // detect outter most element + stack.push(c); + } else { + if (isPair(stack.peek(), c)) { // handle pair + stack.pop(); + if (!stack.isEmpty()) { + sb.append(c); + } + } else {// handle single + stack.push(c); + sb.append(c); + } + } + } + + return sb.toString(); + } + + private boolean isPair(char a, char b) { + return a == '(' && b ==')'; + } +} + +// count +class Solution { + public String removeOuterParentheses(String S) { + StringBuilder s = new StringBuilder(); + int count = 0; + for (char c : S.toCharArray()) { + if (c == '(' && count++ > 0) s.append(c); + if (c == ')' && count-- > 1) s.append(c); + } + return s.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/1026. Maximum Difference Between Node and Ancestor.java b/Java/1026. Maximum Difference Between Node and Ancestor.java new file mode 100755 index 0000000..4f814f2 --- /dev/null +++ b/Java/1026. Maximum Difference Between Node and Ancestor.java @@ -0,0 +1,99 @@ +M +tags: Tree, DFS +time: O(n) +space: O(logn) + +#### Method1: Top-Down DFS +- cache parent max and min => produce current max and min +- pass the max and min to dfs +- compare and return the max of dfs(left), dfs(right) +- time: O(n) +- space: O(logn) +- easy to write, a bit hard to think of + +#### Method2: Bottom-up DFS +- pass up the local (min, max) as object `Val{max, min}` +- easy to think of, but more code to write + +``` +/* + +Given the root of a binary tree, find the maximum value V for which there exists different nodes A and B where V = |A.val - B.val| and A is an ancestor of B. + +(A node A is an ancestor of B if either: any child of A is equal to B, or any child of A is an ancestor of B.) + + + +Example 1: + + + +Input: [8,3,10,1,6,null,14,null,null,4,7,13] +Output: 7 +Explanation: +We have various ancestor-node differences, some of which are given below : +|8 - 3| = 5 +|3 - 7| = 4 +|8 - 1| = 7 +|10 - 13| = 3 +Among all possible differences, the maximum value of 7 is obtained by |8 - 1| = 7. + + +Note: + +The number of nodes in the tree is between 2 and 5000. +Each node will have value between 0 and 100000. + +*/ +// Method1 +class Solution { + public int maxAncestorDiff(TreeNode root) { + return dfs(root, root.val, root.val); + } + + private int dfs(TreeNode node, int max, int min) { + if (node == null) return max - min; + max = Math.max(max, node.val); + min = Math.min(min, node.val); + return Math.max(dfs(node.left, max, min), dfs(node.right, max, min)); + } +} + + +/* + +Method2: bottom up approach: pass up the local (min, max) +- end state: leaf +- +*/ +class Solution { + + class Val{ + int max, min; + public Val(int max, int min) { + this.max = max; + this.min = min; + } + } + + int rst = Integer.MIN_VALUE; + + public int maxAncestorDiff(TreeNode root) { + dfs(root); + return rst; + } + + private Val dfs(TreeNode node) { + if (node == null) return new Val(Integer.MIN_VALUE, Integer.MAX_VALUE); + if (node.left == null && node.right == null) return new Val(node.val, node.val); + Val left = dfs(node.left), right = dfs(node.right); + int min = Math.min(left.min, right.min); + int max = Math.max(left.max, right.max); + + int maxDiff = Math.max(Math.abs(node.val - min), Math.abs(node.val - max)); + rst = Math.max(rst, maxDiff); + + return new Val(Math.max(node.val, max), Math.min(node.val, min)); + } +} +``` \ No newline at end of file diff --git a/Java/103. Binary Tree Zigzag Level Order Traversal.java b/Java/103. Binary Tree Zigzag Level Order Traversal.java new file mode 100755 index 0000000..3c71b4f --- /dev/null +++ b/Java/103. Binary Tree Zigzag Level Order Traversal.java @@ -0,0 +1,78 @@ +M +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}, + + 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 + +*/ + +/** + * 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. 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(); + List list = new ArrayList<>(); + for (int i = 0 ; i < size; i++) { + TreeNode node = queue.poll(); + if (level % 2 == 0) list.add(node.val); + else list.add(0, node.val); + if(node.left!= null) queue.offer(node.left); + if(node.right!= null) queue.offer(node.right); + } + level++; + rst.add(list); + } + + return rst; + } +} + +``` diff --git a/Java/1033. Moving Stones Until Consecutive.java b/Java/1033. Moving Stones Until Consecutive.java new file mode 100755 index 0000000..1e6f393 --- /dev/null +++ b/Java/1033. Moving Stones Until Consecutive.java @@ -0,0 +1,102 @@ +E +tags: Sort, Basic Implementation +time: O(1), only 3 elements +space: O(1) + +#### Analyze to understand +- put 3 elements into array, sort and follow below rules: +- min: + - if 3 elements consecutive, 0 move. + - if only 1 pair of the two elemnets consecutive or if they have 1 slot in between, it needs exactly 1 move + - otherwise, at most 2 moves +- max: # of open slots between them (high - low + 1) - n, where n = 3 +- Follow up: `1040. Moving Stones Until Consecutive` is more interesting with special rulese (cannot move to `ending spot`), and it uses sliding window concept + +``` +/* +Three stones are on a number line at positions a, b, and c. + +Each turn, you pick up a stone at an endpoint (ie., either the lowest or highest position stone), and move it to an unoccupied position between those endpoints. Formally, let's say the stones are currently at positions x, y, z with x < y < z. You pick up the stone at either position x or position z, and move that stone to an integer position k, with x < k < z and k != y. + +The game ends when you cannot make any more moves, ie. the stones are in consecutive positions. + +When the game ends, what is the minimum and maximum number of moves that you could have made? Return the answer as an length 2 array: answer = [minimum_moves, maximum_moves] + + + +Example 1: + +Input: a = 1, b = 2, c = 5 +Output: [1,2] +Explanation: Move the stone from 5 to 3, or move the stone from 5 to 4 to 3. +Example 2: + +Input: a = 4, b = 3, c = 2 +Output: [0,0] +Explanation: We cannot make any moves. +Example 3: + +Input: a = 3, b = 5, c = 1 +Output: [1,2] +Explanation: Move the stone from 1 to 4; or move the stone from 1 to 2 to 4. + + +Note: + +1 <= a <= 100 +1 <= b <= 100 +1 <= c <= 100 +a != b, b != c, c != a + + */ + +/* +- min: min dist between them or 2 +- max: # of open slots between them c - a + 1 - 3 +*/ +class Solution { + public int[] numMovesStones(int a, int b, int c) { + int[] arr = new int[]{a, b, c}; + Arrays.sort(arr); + int max = arr[2] - arr[0] - 2; + if (arr[2] - arr[0] == 2) return new int[]{0, max}; + if (arr[1] - arr[0] <= 2 || arr[2] - arr[1] <= 2) return new int[]{1, max}; + return new int[]{2, max}; + } +} + +/* +if consecutive pts, no need to move, return {0, 0} +max steps: the amount of empty slots between end - start - 2 +min steps case1: +if min of the two intervals has <= 1 spot, we can fill the spot => 1 step +if min of the two intervals has > 1 spot, we have to move 2 steps. + +Tip: +Sort input, no need manual find min/mid/max. Adds some delay. +*/ +class Solution { + public int[] numMovesStones(int a, int b, int c) { + int[] input = {a, b, c}; + Arrays.sort(input); + + // calculate + int max = calDist(input[0], input[2]) - 1; + int left = calDist(input[0], input[1]); + int right = calDist(input[1], input[2]); + + int min = 2; + if (left + right == 0) { + min = 0; + } else if (Math.min(left, right) <= 1) { + min = 1; + } + + return new int[] {min, max}; + } + + private int calDist(int start, int end) { + return end - start - 1; + } +} +``` \ No newline at end of file diff --git a/Java/104. Maximum Depth of Binary Tree.java b/Java/104. Maximum Depth of Binary Tree.java new file mode 100755 index 0000000..98a7b5c --- /dev/null +++ b/Java/104. Maximum Depth of Binary Tree.java @@ -0,0 +1,61 @@ +E +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 + +``` +/* +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. + +Note: A leaf is a node with no children. + +Example: + +Given binary tree [3,9,20,null,null,15,7], + + 3 + / \ + 9 20 + / \ + 15 7 +return its depth = 3. + +*/ + +/* +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 for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +public class Solution { + public int maxDepth(TreeNode root) { + if (root == null) { + return 0; + } + return Math.max(maxDepth(root.left), maxDepth(root.right)) + 1; + } +} + +``` \ No newline at end of file diff --git a/Java/1040. Moving Stones Until Consecutive II.java b/Java/1040. Moving Stones Until Consecutive II.java new file mode 100755 index 0000000..0e29c0f --- /dev/null +++ b/Java/1040. Moving Stones Until Consecutive II.java @@ -0,0 +1,99 @@ +M +tags: Sliding Window, Array +time: O(nlogn) +space: O(n) + + +#### Analyze to understand +- Make sure to sort array: we need to use the actual number range `A[j] - A[i]`, which requires the array to be sorted +- we want to form a new array where A[n-1] - A[0] + 1 == n; order does not matter but all slots need to be filled consecutivly +- max moves: https://leetcode.com/problems/moving-stones-until-consecutive-ii/discuss/289357/c%2B%2B-with-picture + - A interval will be automatically dropped between A[0] and A[1], if moving A[0] first + - Same, a interval between A[n-2] and A[n-1] will be dropped when moving A[n-1] first + - so largest possible move = firstItem + remaining range size - n items = 1 + (A[n-1] - A[1] + 1) - n = A[n-1] - A[1] -n + 2 + - or A[n-2] - A[0] - n + 2 +- min moves: `Sliding Window` + - use slinding window to assume a right pointer, to make sure A[right] - A[left] + 1 < n; otherwise, move left++ + - check # of included stones + - calculate remaining, which is remaining moves +- Handle min move edge case: + - Consecutive Array up to right = n - 1; need 2 moves to finish + +``` +/** +On an infinite number line, the position of the i-th stone is given by stones[i]. Call a stone an endpoint stone if it has the smallest or largest position. + +Each turn, you pick up an endpoint stone and move it to an unoccupied position so that it is no longer an endpoint stone. + +In particular, if the stones are at say, stones = [1,2,5], you cannot move the endpoint stone at position 5, since moving it to any position (such as 0, or 3) will still keep that stone as an endpoint stone. + +The game ends when you cannot make any more moves, ie. the stones are in consecutive positions. + +When the game ends, what is the minimum and maximum number of moves that you could have made? Return the answer as an length 2 array: answer = [minimum_moves, maximum_moves] + + + +Example 1: + +Input: [7,4,9] +Output: [1,2] +Explanation: +We can move 4 -> 8 for one move to finish the game. +Or, we can move 9 -> 5, 4 -> 6 for two moves to finish the game. +Example 2: + +Input: [6,5,4,3,10] +Output: [2,3] +We can move 3 -> 8 then 10 -> 7 to finish the game. +Or, we can move 3 -> 7, 4 -> 8, 5 -> 9 to finish the game. +Notice we cannot move 10 -> 2 to finish the game, because that would be an illegal move. +Example 3: + +Input: [100,101,104,102,103] +Output: [0,0] + + +Note: + +3 <= stones.length <= 10^4 +1 <= stones[i] <= 10^9 +stones[i] have distinct values. +*/ + +/* +Analyze to understand: +- we want to form a new array where A[n-1] - A[0] + 1 == n; order does not matter but all slots need to be filled consecutivly +- max moves: https://leetcode.com/problems/moving-stones-until-consecutive-ii/discuss/289357/c%2B%2B-with-picture + - A interval will be automatically dropped between A[0] and A[1], if moving A[0] first + - Same, a interval between A[n-2] and A[n-1] will be dropped when moving A[n-1] first + - so largest possible move = firstItem + remaining range size - n items = 1 + (A[n-1] - A[1] + 1) - n = A[n-1] - A[1] -n + 2 + - or A[n-2] - A[0] - n + 2 +- min moves: + - use slinding window to assume a right pointer, to make sure A[right] - A[left] + 1 < n; otherwise, move left++ + - check # of included stones + - calculate remaining, which is remaining moves +- Handle min move edge case: + - Consecutive Array up to right = n - 1; need 2 moves to finish +*/ +class Solution { + public int[] numMovesStonesII(int[] stones) { + Arrays.sort(stones); + + int n = stones.length, left = 0, right = 0, min = n; + int max = Math.max(stones[n - 1] - stones[1] - n + 2, stones[n - 2] - stones[0] - n + 2); + + while (right < n) { + while (stones[right] - stones[left] + 1 > n) left++; + int count = right - left + 1; + if (count == n - 1 && stones[right] - stones[left] + 1 == n - 1) { + min = Math.min(min, 2); + } else { + min = Math.min(min, n - count); + } + right++; + } + + return new int[]{min, max}; + } +} +``` \ No newline at end of file diff --git a/Java/1041. Robot Bounded In Circle.java b/Java/1041. Robot Bounded In Circle.java new file mode 100755 index 0000000..f83aaee --- /dev/null +++ b/Java/1041. Robot Bounded In Circle.java @@ -0,0 +1,102 @@ +E +tags: String + +简单的character checking. 各个方向, 加加减减. + +``` +/* +On an infinite plane, a robot initially stands at (0, 0) and faces north. The robot can receive one of three instructions: + +"G": go straight 1 unit; +"L": turn 90 degrees to the left; +"R": turn 90 degress to the right. +The robot performs the instructions given in order, and repeats them forever. + +Return true if and only if there exists a circle in the plane such that the robot never leaves the circle. + + + +Example 1: + +Input: "GGLLGG" +Output: true +Explanation: +The robot moves from (0,0) to (0,2), turns 180 degrees, and then returns to (0,0). +When repeating these instructions, the robot remains in the circle of radius 2 centered at the origin. +Example 2: + +Input: "GG" +Output: false +Explanation: +The robot moves north indefinitely. +Example 3: + +Input: "GL" +Output: true +Explanation: +The robot moves from (0, 0) -> (0, 1) -> (-1, 1) -> (-1, 0) -> (0, 0) -> ... + + +Note: + +1 <= instructions.length <= 100 +instructions[i] is in {'G', 'L', 'R'} + +*/ + +//TODO + + + +/* +// LintCode: +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/1043. Partition Array for Maximum Sum.java b/Java/1043. Partition Array for Maximum Sum.java new file mode 100755 index 0000000..46fd270 --- /dev/null +++ b/Java/1043. Partition Array for Maximum Sum.java @@ -0,0 +1,56 @@ +M +tags: Memoization, DFS, DP, Graph +time: O(n), calc memo[n] +space: O(n) + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + +``` +/* +Given an integer array A, you partition the array into (contiguous) subarrays of length at most K. After partitioning, each subarray has their values changed to become the maximum value of that subarray. + +Return the largest sum of the given array after partitioning. + + + +Example 1: + +Input: A = [1,15,7,9,2,5,10], K = 3 +Output: 84 +Explanation: A becomes [15,15,15,9,10,10,10] + + +Note: + +1 <= K <= A.length <= 500 +0 <= A[i] <= 10^6 +*/ + +class Solution { + Integer[] memo; + public int maxSumAfterPartitioning(int[] A, int K) { + if (A == null || A.length == 0) return 0; + + int n = A.length; + memo = new Integer[n]; + return dfs(A, K, 0); + } + + private int dfs(int[] A, int k, int index) { + if (index >= A.length) return 0; + if (memo[index] != null) return memo[index]; + + int local = Integer.MIN_VALUE, max = Integer.MIN_VALUE; + for (int i = index; i < A.length && i < index + k; i++) { + local = Math.max(local, A[i]); + max = Math.max(max, local * (i - index + 1) + dfs(A, k, i + 1)); + } + memo[index] = max; + return max; + } +} +``` \ No newline at end of file diff --git a/Java/1048. Longest String Chain.java b/Java/1048. Longest String Chain.java new file mode 100755 index 0000000..c5f2f4f --- /dev/null +++ b/Java/1048. Longest String Chain.java @@ -0,0 +1,109 @@ +M +tags: Hash Table, DP, Sort, Bucket Sort +time: O(n) +space: O(n) + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + +``` +/* +Given a list of words, each word consists of English lowercase letters. + +Let's say word1 is a predecessor of word2 if and only if we can add exactly one letter anywhere in word1 to make it equal to word2. For example, "abc" is a predecessor of "abac". + +A word chain is a sequence of words [word_1, word_2, ..., word_k] with k >= 1, where word_1 is a predecessor of word_2, word_2 is a predecessor of word_3, and so on. + +Return the longest possible length of a word chain with words chosen from the given list of words. + + + +Example 1: + +Input: ["a","b","ba","bca","bda","bdca"] +Output: 4 +Explanation: one of the longest word chain is "a","ba","bda","bdca". + + +Note: + +1 <= words.length <= 1000 +1 <= words[i].length <= 16 +words[i] only consists of English lowercase letters. +*/ +/* +Method1: sort, HashMap, DP +- store Map +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) +*/ +class Solution { + public int longestStrChain(String[] words) { + int rst = 0; + Arrays.sort(words, Comparator.comparing(a -> a.length())); + HashMap wordChainMap = new HashMap(); + for (String word : words) { + if (wordChainMap.containsKey(word)) continue; + wordChainMap.put(word, 1); + for (int i = 0; i < word.length(); i++) { + StringBuilder sb = new StringBuilder(word); + sb.deleteCharAt(i); + String lastWord = sb.toString(); + if (wordChainMap.containsKey(lastWord) && wordChainMap.get(lastWord) + 1 > wordChainMap.get(word)) { + wordChainMap.put(word, wordChainMap.get(lastWord) + 1); + } + } + if (wordChainMap.get(word) > rst) rst = wordChainMap.get(word); + } + return rst; + } +} + +// Method2: bucket sort O(n) time +class Solution { + public int longestStrChain(String[] words) { + int rst = 0; + + List[] bucket = buildBucket(words); + HashMap map = new HashMap(); + + for (List list : bucket) { + if (list == null) continue; + for (String word : list) { + if (map.containsKey(word)) continue; + map.put(word, 1); + for (int i = 0; i < word.length(); i++) { + StringBuilder sb = new StringBuilder(word); + sb.deleteCharAt(i); + String lastWord = sb.toString(); + if (map.containsKey(lastWord) && map.get(lastWord) + 1 > map.get(word)) { + map.put(word, map.get(lastWord) + 1); + } + } + if (map.get(word) > rst) rst = map.get(word); + } + } + return rst; + } + + // O(n) + private List[] buildBucket(String[] words) { + List[] bucket = new List[17]; + for (String w : words) { + int len = w.length(); + if (bucket[len] == null) bucket[len] = new ArrayList<>(); + bucket[len].add(w); + } + return bucket; + } +} +``` \ No newline at end of file diff --git a/Java/105. Construct Binary Tree from Preorder and Inorder Traversal.java b/Java/105. Construct Binary Tree from Preorder and Inorder Traversal.java new file mode 100755 index 0000000..a2c125c --- /dev/null +++ b/Java/105. Construct Binary Tree from Preorder and Inorder Traversal.java @@ -0,0 +1,152 @@ +M +tags: Array, Tree, DFS, Divide and Conquer, Hash Table +time: O(n) +space: O(n) + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 +*/ + + +/* +- use preorder to find root, 1 index at a time +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find +*/ +class Solution { + int index = 0; + Map map; + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder == null || inorder == null || preorder.length == 0 + || preorder.length != inorder.length) return null; + map = buildMap(inorder); + return dfs(inorder, preorder, 0, inorder.length - 1); + } + + public TreeNode dfs(int[] inorder, int[] preorder, int start, int end) { + if (start > end) return null; // catch dfs attempting to process visisted nodes + int rootVal = preorder[index++]; + int rootInd = map.get(rootVal); + TreeNode root = new TreeNode(rootVal); + if (start == end) return root; + + root.left = dfs(inorder, preorder, start, rootInd - 1); + root.right = dfs(inorder, preorder, rootInd + 1, end); + + return root; + } + + private Map buildMap(int[] inorder) { + Map map = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) map.put(inorder[i], i); + return map; + } +} + + /* +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; + public TreeNode buildTree(int[] preorder, int[] inorder) { + if (preorder == null || inorder == null || preorder.length == 0 + || preorder.length != inorder.length) return null; + map = buildMap(inorder); + int n = preorder.length; + return dfs(preorder, 0, n - 1, inorder, 0, n - 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 rootInd = map.get(preorder[preStart]); + + if (rootInd < 0) return null; + + //root.left + root.left = dfs(preorder, preStart + 1, preStart + (rootInd - inStart), + inorder, inStart, rootInd - 1); + //root.right + root.right = dfs(preorder, preStart + (rootInd - inStart) + 1, preEnd, + inorder, rootInd + 1, inEnd); + + return root; + } + + private Map buildMap(int[] inorder) { + Map map = new HashMap<>(); + for (int i = 0; i < inorder.length; i++) map.put(inorder[i], i); + return map; + } +} + + +/** + * 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/1053. Previous Permutation With One Swap.java b/Java/1053. Previous Permutation With One Swap.java new file mode 100755 index 0000000..d2d1536 --- /dev/null +++ b/Java/1053. Previous Permutation With One Swap.java @@ -0,0 +1,75 @@ +M +tags: Array, Greedy, Permutation +time: O(n) +space: O(1) + +#### Analyze Permutation behavior +- concept similar to `31. Next Permutation` +- 1) first pass: find the one that is in incorrect order +- 2) second pass: find the right spot to swap + +``` +/* +Given an array A of positive integers (not necessarily distinct), return the lexicographically largest permutation that is smaller than A, that can be made with one swap (A swap exchanges the positions of two numbers A[i] and A[j]). If it cannot be done, then return the same array. + + + +Example 1: + +Input: [3,2,1] +Output: [3,1,2] +Explanation: Swapping 2 and 1. +Example 2: + +Input: [1,1,5] +Output: [1,1,5] +Explanation: This is already the smallest permutation. +Example 3: + +Input: [1,9,4,6,7] +Output: [1,7,4,6,9] +Explanation: Swapping 9 and 7. +Example 4: + +Input: [3,1,1,3] +Output: [1,3,1,3] +Explanation: Swapping 1 and 3. + + +Note: + +1 <= A.length <= 10000 +1 <= A[i] <= 10000 +*/ + +class Solution { + public int[] prevPermOpt1(int[] A) { + if (A.length <= 1) return A; + int index = -1; + // find the largest i such that A[i] > A[i + 1] + for (int i = A.length - 1; i >= 1; i--) { + if (A[i] < A[i - 1]) { + index = i - 1; + break; + } + } + // the array already sorted ascendingly, no need to procceed + if (index == -1) return A; + + // find the largest i such that A[index] > A[i], then swap them + for (int i = A.length - 1; i > index; i--) { + if (A[i] < A[index] && A[i] != A[i - 1]) { + swap(A, i, index); + break; + } + } + return A; + } + + private void swap(int[] arr, int x, int y) { + int temp = arr[x]; + arr[x] = arr[y]; + arr[y] = temp; + } +} +``` \ No newline at end of file diff --git a/Java/1057. Campus Bikes.java b/Java/1057. Campus Bikes.java new file mode 100755 index 0000000..d6f064e --- /dev/null +++ b/Java/1057. Campus Bikes.java @@ -0,0 +1,177 @@ +M +tags: Greedy, Sort, PriorityQueue, Bucket Sort +time: O(mn) +space: O(mn) + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + +``` +/* + +On a campus represented as a 2D grid, there are N workers and M bikes, with N <= M. Each worker and bike is a 2D coordinate on this grid. + +Our goal is to assign a bike to each worker. Among the available bikes and workers, we choose the (worker, bike) pair with the shortest Manhattan distance between each other, and assign the bike to that worker. (If there are multiple (worker, bike) pairs with the same shortest Manhattan distance, we choose the pair with the smallest worker index; if there are multiple ways to do that, we choose the pair with the smallest bike index). We repeat this process until there are no available workers. + +The Manhattan distance between two points p1 and p2 is Manhattan(p1, p2) = |p1.x - p2.x| + |p1.y - p2.y|. + +Return a vector ans of length N, where ans[i] is the index (0-indexed) of the bike that the i-th worker is assigned to. + + + +Example 1: + + + +Input: workers = [[0,0],[2,1]], bikes = [[1,2],[3,3]] +Output: [1,0] +Explanation: +Worker 1 grabs Bike 0 as they are closest (without ties), and Worker 0 is assigned Bike 1. So the output is [1, 0]. +Example 2: + + + +Input: workers = [[0,0],[1,1],[2,0]], bikes = [[1,0],[2,2],[2,1]] +Output: [0,2,1] +Explanation: +Worker 0 grabs Bike 0 at first. Worker 1 and Worker 2 share the same distance to Bike 2, thus Worker 1 is assigned to Bike 2, and Worker 2 will take Bike 1. So the output is [0,2,1]. + + +Note: + +0 <= workers[i][j], bikes[i][j] < 1000 +All worker and bike locations are distinct. +1 <= workers.length <= bikes.length <= 1000 +*/ +/* +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited +*/ +class Solution { + class Pair { + int dist, i, j; // i = workder index, j = bikeIndex + public Pair(int dist, int i, int j) { + this.i = i; + this.j = j; + this.dist = dist; + } + } + public int[] assignBikes(int[][] workers, int[][] bikes) { + + PriorityQueue queue = buildQueue(); + + int n = workers.length, m = bikes.length; + for (int i = 0; i < n; i++) { + int[] worker = workers[i]; + for (int j = 0; j < m; j++) { + int[] bike = bikes[j]; + int dist = Math.abs(worker[0] - bike[0]) + Math.abs(worker[1] - bike[1]); + queue.offer(new Pair(dist, i, j)); + } + } + + Set visitedBike = new HashSet<>(); + int[] rst = new int[n]; + Arrays.fill(rst, -1); + while (visitedBike.size() < n) { + Pair p = queue.poll(); + if (rst[p.i] == -1 && !visitedBike.contains(p.j)) { + rst[p.i] = p.j; + visitedBike.add(p.j); + } + } + + return rst; + } + + private PriorityQueue buildQueue() { + return new PriorityQueue<>(new Comparator() { + public int compare(Pair p1, Pair p2) { + int comp = Integer.compare(p1.dist, p2.dist); + if (comp == 0) { + comp = Integer.compare(p1.i, p2.i); // compare worker index + if (comp == 0) return Integer.compare(p1.j, p2.j); // compare bike index + return comp; + } + return comp; + } + }); + } +} + + +/* +Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited +*/ +class Solution { + class Pair { + int dist, i, j; // i = workder index, j = bikeIndex + public Pair(int dist, int i, int j) { + this.i = i; + this.j = j; + this.dist = dist; + } + } + public int[] assignBikes(int[][] workers, int[][] bikes) { + List[] bucket = buildBucket(workers, bikes); + Set visitedBike = new HashSet<>(); + int[] rst = new int[workers.length]; + Arrays.fill(rst, -1); + + for (int dist = 0; dist < bucket.length; dist++) { + List list = bucket[dist]; + if (list == null) continue; + for (Pair p : list) { + if (rst[p.i] == -1 && !visitedBike.contains(p.j)) { + rst[p.i] = p.j; + visitedBike.add(p.j); + } + } + } + return rst; + } + + private List[] buildBucket(int[][] workers, int[][] bikes) { + List[] bucket = new List[2001]; + int n = workers.length, m = bikes.length; + for (int i = 0; i < n; i++) { + int[] worker = workers[i]; + for (int j = 0; j < m; j++) { + int[] bike = bikes[j]; + int dist = Math.abs(worker[0] - bike[0]) + Math.abs(worker[1] - bike[1]); + if (bucket[dist] == null) bucket[dist] = new ArrayList<>(); + bucket[dist].add(new Pair(dist, i, j)); + } + } + + return bucket; + } + +} +``` \ No newline at end of file diff --git a/Java/1060. Missing Element in Sorted Array.java b/Java/1060. Missing Element in Sorted Array.java new file mode 100755 index 0000000..73e818d --- /dev/null +++ b/Java/1060. Missing Element in Sorted Array.java @@ -0,0 +1,94 @@ +M +tags: Binary Search +time: O(logn) +space: O(1) + +#### Binary Search +- total missing nums = nums[curr] - nums[0] - curr +- edge case: if k > total missing nums, then just add the diff from nums[end] +- otherwise, find this `missing count == k` in the nums using binary search +- After binary search: `start + 1 == end`: + - re-calculate `count = nums[start] - nums[0] - start;` + - output final num: `nums[start] + k - count;` +- Option1: always compare total missing nums count +- Option2: compare partial missing nums count (inspired by: https://leetcode.com/problems/missing-element-in-sorted-array/discuss/303444/Java-O(logN)-solution-Binary-Search) + + +``` +/* + +Given a sorted array A of unique numbers, find the K-th missing number starting from the leftmost number of the array. + + + +Example 1: + +Input: A = [4,7,9,10], K = 1 +Output: 5 +Explanation: +The first missing number is 5. +Example 2: + +Input: A = [4,7,9,10], K = 3 +Output: 8 +Explanation: +The missing numbers are [5,6,8,...], hence the third missing number is 8. +Example 3: + +Input: A = [1,2,4], K = 3 +Output: 6 +Explanation: +The missing numbers are [3,5,6,7,...], hence the third missing number is 6. + + +Note: + +1 <= A.length <= 50000 +1 <= A[i] <= 1e7 +1 <= K <= 1e8 + +*/ +// Binary Search, Option1: always compare total missing nums +class Solution { + public int missingElement(int[] nums, int k) { + int start = 0, end = nums.length - 1; + int count = nums[end] - nums[0] - end; + if (count < k) { + return nums[end] + k - count; + } + + while (start + 1 < end) { + int mid = start + (end - start) / 2; + count = nums[mid] - nums[0] - mid; + if (count >= k) end = mid; + else start = mid; + } + count = nums[start] - nums[0] - start; + return nums[start] + k - count; + } +} + +// Binary Search, Option1: always compare pairtial missing nums in range [mid, end] +class Solution { + public int missingElement(int[] nums, int k) { + + int start = 0, end = nums.length - 1; + int count = nums[end] - nums[0] - end; + if (count < k) { + return nums[end] + k - count; + } + + while (start + 1 < end) { + int mid = start + (end - start) / 2; + count = nums[mid] - nums[start] - (mid - start); + if (count >= k) { + end = mid; + } else { + start = mid; + k -= count; + } + } + return nums[start] + k; + } +} +``` \ No newline at end of file diff --git a/Java/1091. Shortest Path in Binary Matrix.java b/Java/1091. Shortest Path in Binary Matrix.java new file mode 100755 index 0000000..da4c612 --- /dev/null +++ b/Java/1091. Shortest Path in Binary Matrix.java @@ -0,0 +1,90 @@ +M +tags: BFS +time: O(n^2) +time: O(n^2) + + +#### BFS +- find shortest path using queue +- time/space: O(n^2), n = grid length +- why SKIP `boolean visited[i][j]`? after a position grid[i][j] is used: + - 1) the curr path will not return to (i, j) + - 2) other route that may eventually reach (i, j) need not to be recorded, + - because the other route is already longer than the curr path + - therefore, we just simply block the visited node by `grid[x][y] = 1` + - note: block it right after it is added to the queue, so other nodes at same level will not attempt this visited node. + +``` + + +/* +In an N by N square grid, each cell is either empty (0) or blocked (1). + +A clear path from top-left to bottom-right has length k if and only if it is composed of cells C_1, C_2, ..., C_k such that: + +Adjacent cells C_i and C_{i+1} are connected 8-directionally (ie., they are different and share an edge or corner) +C_1 is at location (0, 0) (ie. has value grid[0][0]) +C_k is at location (N-1, N-1) (ie. has value grid[N-1][N-1]) +If C_i is located at (r, c), then grid[r][c] is empty (ie. grid[r][c] == 0). +Return the length of the shortest such clear path from top-left to bottom-right. If such a path does not exist, return -1. + + + +Example 1: + +Input: [[0,1],[1,0]] + + +Output: 2 + +Example 2: + +Input: [[0,0,0],[1,1,0],[1,1,0]] + + +Output: 4 + + + +Note: + +1 <= grid.length == grid[0].length <= 100 +grid[r][c] is 0 or 1 + +*/ + +class Solution { + int[] dx = new int[]{-1, -1, -1, 0, 0, 1, 1, 1}; + int[] dy = new int[]{-1, 0, 1, -1, 1, -1, 0, 1}; + int N; + public int shortestPathBinaryMatrix(int[][] grid) { + N = grid.length; + Queue queue = new LinkedList<>(); + if (grid[0][0] == 0) queue.offer(new int[] {0, 0}); + grid[0][0] = 1; + int depth = 1; + + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { + int[] pos = queue.poll(); + + if (pos[0] == N - 1 && pos[1] == N -1) return depth; + for (int i = 0; i < dx.length; i++) { + int x = pos[0] + dx[i], y = pos[1] + dy[i]; + if (validate(grid, x, y)) { + queue.offer(new int[] {x, y}); + grid[x][y] = 1; + } + } + } + depth++; + } + return -1; + } + + private boolean validate(int[][] grid, int i, int j) { + return (i >= 0 && i < N && j >= 0 && j < N && grid[i][j] == 0); + } +} +``` \ No newline at end of file diff --git a/Java/1094. Car Pooling.java b/Java/1094. Car Pooling.java new file mode 100755 index 0000000..193b208 --- /dev/null +++ b/Java/1094. Car Pooling.java @@ -0,0 +1,112 @@ +M +tags: PriorityQueue, Heap, Sort, Greedy +time: O(n) +space: O(1) only use bucket size 1000 + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + +``` +/* +You are driving a vehicle that has capacity empty seats initially available for passengers. The vehicle only drives east (ie. it cannot turn around and drive west.) + +Given a list of trips, trip[i] = [num_passengers, start_location, end_location] contains information about the i-th trip: the number of passengers that must be picked up, and the locations to pick them up and drop them off. The locations are given as the number of kilometers due east from your vehicle's initial location. + +Return true if and only if it is possible to pick up and drop off all passengers for all the given trips. + + + +Example 1: + +Input: trips = [[2,1,5],[3,3,7]], capacity = 4 +Output: false +Example 2: + +Input: trips = [[2,1,5],[3,3,7]], capacity = 5 +Output: true +Example 3: + +Input: trips = [[2,1,5],[3,5,7]], capacity = 3 +Output: true +Example 4: + +Input: trips = [[3,2,7],[3,7,9],[8,3,9]], capacity = 11 +Output: true + + + +Constraints: + +trips.length <= 1000 +trips[i].length == 3 +1 <= trips[i][0] <= 100 +0 <= trips[i][1] < trips[i][2] <= 1000 +1 <= capacity <= 100000 +*/ + + +/* +Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), n = trips size +*/ +class Solution { + public boolean carPooling(int[][] trips, int capacity) { + if (trips == null || trips.length == 0) return true; + + int[] bucket = new int[1000]; + for (int[] trip : trips) { + bucket[trip[1]] += trip[0]; + bucket[trip[2]] += -trip[0]; + } + int cap = 0; + for (int num : bucket) { + cap += num; + if (cap > capacity) return false; + } + return true; + } + +} + +// Method2: Priority Queue, sort distnace +class Solution { + public boolean carPooling(int[][] trips, int capacity) { + if (trips == null || trips.length == 0) return true; + + PriorityQueue queue = buildQueue(trips); + int cap = 0; + while (!queue.isEmpty()) { + int[] point = queue.poll(); + cap += point[1]; + while (!queue.isEmpty() && queue.peek()[0] == point[0]) { + cap += queue.poll()[1]; + } + if (cap > capacity) return false; + } + return true; + } + + private PriorityQueue buildQueue(int[][] trips) { + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(p -> p[0])); + for (int[] trip : trips) { + queue.offer(new int[]{trip[1], trip[0]}); + queue.offer(new int[]{trip[2], -trip[0]}); + } + return queue; + } +} +``` \ No newline at end of file diff --git a/Java/110. Balanced Binary Tree.java b/Java/110. Balanced Binary Tree.java new file mode 100755 index 0000000..61230fb --- /dev/null +++ b/Java/110. Balanced Binary Tree.java @@ -0,0 +1,101 @@ +E +tags: Tree, DFS + +给一个binary tree, 看是否是height-balanced + +#### DFS with end state +- 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 above solution, but cost more traversal efforts +- 试图计算所有情况 + +``` +/* +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 for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +/* +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. +*/ +class Solution { + public boolean isBalanced(TreeNode root) { + if (root == null) return true; + return calDepth(root) > 0; + } + + private int calDepth(TreeNode node) { + if (node == null) return 0; + int leftDepth = calDepth(node.left); + int rightDepth = calDepth(node.right); + if (leftDepth < 0 || rightDepth < 0 || (Math.abs(leftDepth - rightDepth)) > 1) { + return Integer.MIN_VALUE; + } + 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); + + 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; + } +} + + + + +``` \ No newline at end of file diff --git a/Java/1106. Parsing A Boolean Expression.java b/Java/1106. Parsing A Boolean Expression.java new file mode 100755 index 0000000..00c190c --- /dev/null +++ b/Java/1106. Parsing A Boolean Expression.java @@ -0,0 +1,148 @@ +H +tags: String, Stack, DFS + +#### Parse exp as sub problem +- Analyze the pattern: 1) single char, 2) with !, 3) with &, | +- Identify sub problem + - Use stack to parse the data in "()", which is a sub problem to solve with recursive call + - Handle &, | case: need to parse multiple +- Be comfortable with string parsing +- Slight improve: + - If see obvious result, directly return evaluation w/o further parsing + - use memo to store evaluated exp + +#### Evaluate inner exp and save back to Stack +- Use '(' and ')' to mark inner exp +- Evaluate the inner exp and save result back to Stack: the result will be 'f' or 't' +- This is slightly slow because: + - It requires all stack items on top to be processed before reaching the operator + - There is no room to optimize even there is simplification for specific operator + +``` +/** + +Return the result of evaluating a given boolean expression, represented as a string. + +An expression can either be: + +"t", evaluating to True; +"f", evaluating to False; +"!(expr)", evaluating to the logical NOT of the inner expression expr; +"&(expr1,expr2,...)", evaluating to the logical AND of 2 or more inner expressions expr1, expr2, ...; +"|(expr1,expr2,...)", evaluating to the logical OR of 2 or more inner expressions expr1, expr2, ... + + +Example 1: + +Input: expression = "!(f)" +Output: true +Example 2: + +Input: expression = "|(f,t)" +Output: true +Example 3: + +Input: expression = "&(t,f)" +Output: false +Example 4: + +Input: expression = "|(&(t,f,t),!(t))" +Output: false + +*/ +class Solution { + Map memo = new HashMap<>(); + public boolean parseBoolExpr(String exp) { + if (exp.length() == 1) return exp.equals("t"); + if (memo.containsKey(exp)) return memo.get(exp); + + char op = exp.charAt(0); + String s = exp.substring(2, exp.length() - 1); + + if (op == '!') return !parseBoolExpr(s); + // process the inner list of & or | + List values = parseBoolExprList(s, op); + boolean result = evalList(values, op); + + memo.put(exp, result); + return result; + } + + // Parse expr list (w/o the outter "(", ")") + private List parseBoolExprList(String s, char op) { + int i = 0; + List values = new ArrayList<>(); + while (i < s.length()) { + char c = s.charAt(i++); + if (c == ',') continue; + + // w/o oprator + if(c == 't' || c == 'f') values.add(c == 't'); + else { + // extract inner exp block including "(" and ")" + String sub = findExpBlock(s, i); + i += sub.length(); + + // evaluate + boolean val = parseBoolExpr(c + sub); + values.add(val); + + // optimization + if (op == '|' && val) return Arrays.asList(true); + if (op == '&' && !val) return Arrays.asList(false); + } + } + return values; + } + + private String findExpBlock(String s, int i) { + StringBuffer sb = new StringBuffer(); + Stack stack = new Stack<>(); + while (sb.length() == 0 || !stack.isEmpty()) { + char c = s.charAt(i++); + sb.append(c); + if (c == '(') stack.push(c); + if (c == ')') stack.pop(); + if (stack.isEmpty()) break; + } + return sb.toString(); + } + + private boolean evalList(List list, char op) { + if (op == '&') { + for (boolean b : list) { + if (!b) return false; + } + return true; + } + // op == '|' + for (boolean b : list) { + if (b) return true; + } + return false; + } +} + + +// Use '(' and ')' to mark start and end, then add eval back to stack +class Solution { + public boolean parseBoolExpr(String exp) { + Stack stack = new Stack<>(); + for (char c : exp.toCharArray()) { + if (c == ')') { + Set expItems = new HashSet<>(); + while (stack.peek() != '(') expItems.add(stack.pop()); + stack.pop(); // pop out '(' + char op = stack.pop(); + if (op == '&') stack.push(expItems.contains('f') ? 'f' : 't'); + else if (op == '|') stack.push(expItems.contains('t') ? 't' : 'f'); + else if (op == '!') stack.push(expItems.contains('t') ? 'f' : 't'); + } else if (c != ',') { + stack.push(c); + } + } + return stack.pop() == 't'; + } +} + +``` \ No newline at end of file diff --git a/Java/1108. Defanging an IP Address.java b/Java/1108. Defanging an IP Address.java new file mode 100755 index 0000000..6c5efdb --- /dev/null +++ b/Java/1108. Defanging an IP Address.java @@ -0,0 +1,35 @@ +E +tags: String, Basic Implementation + +``` + +/** +Given a valid (IPv4) IP address, return a defanged version of that IP address. + +A defanged IP address replaces every period "." with "[.]". + + + +Example 1: + +Input: address = "1.1.1.1" +Output: "1[.]1[.]1[.]1" +Example 2: + +Input: address = "255.100.50.0" +Output: "255[.]100[.]50[.]0" +*/ +class Solution { + public String defangIPaddr(String address) { + StringBuffer sb = new StringBuffer(); + for (char c : address.toCharArray()) { + if (c == '.') { + sb.append("[.]"); + } else { + sb.append(c); + } + } + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/111. Minimum Depth of Binary Tree.java b/Java/111. Minimum Depth of Binary Tree.java new file mode 100755 index 0000000..6add7c7 --- /dev/null +++ b/Java/111. Minimum Depth of Binary Tree.java @@ -0,0 +1,87 @@ +E +tags: Tree, DFS, BFS +time: O(n) +space: O(n) + +#### 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 Conquer to find min depth. + - if one of child is null, return the other child depth + 1 + - Pick the min of the two child depth + 1 +- need to visit all nodes + + +``` +/* +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; + 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; + } +} + +``` \ No newline at end of file diff --git a/Java/1110. Delete Nodes And Return Forest.java b/Java/1110. Delete Nodes And Return Forest.java new file mode 100755 index 0000000..874f767 --- /dev/null +++ b/Java/1110. Delete Nodes And Return Forest.java @@ -0,0 +1,151 @@ +M +tags: Tree, DFS, Divide and Conquer +time: O(n) +space: O(logn) + +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +- time: O(n), visit all nodes +- space: O(logn), height of the tree + +#### Method2: HashMap, DFS. +- traverse tree and create `map ` to fast O(1) removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- This approach requires a dfs build of parentMap + - it is same amount of efforts to do the regular dfs removal. + - not a good solution +- time: O(n) +- space: O(n) + +``` + +/* +Given the root of a binary tree, each node in the tree has a distinct value. + +After deleting all nodes with a value in to_delete, we are left with a forest (a disjoint union of trees). + +Return the roots of the trees in the remaining forest. You may return the result in any order. + + + +Example 1: + + + +Input: root = [1,2,3,4,5,6,7], to_delete = [3,5] +Output: [[1,2,null,4],[6],[7]] + + +Constraints: + +The number of nodes in the given tree is at most 1000. +Each node has a distinct value between 1 and 1000. +to_delete.length <= 1000 +to_delete contains distinct values between 1 and 1000. + + +*/ + + + +/* +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +*/ +class Solution { + + public List delNodes(TreeNode root, int[] toDelete) { + Set toDeleteSet = new HashSet<>(); + for (int val : toDelete) toDeleteSet.add(val); + List rst = new ArrayList<>(); + root = dfs(root, toDeleteSet, rst); + if (root != null) rst.add(root); + return rst; + } + + private TreeNode dfs(TreeNode node, Set toDeleteSet, List rst) { + if (node == null) return null; + node.left = dfs(node.left, toDeleteSet, rst); + node.right = dfs(node.right, toDeleteSet, rst); + + if (toDeleteSet.contains(node.val)) { + if (node.left != null) rst.add(node.left); + if (node.right != null) rst.add(node.right); + return null; + } + return node; + } +} + + +/* +#### Method2: HashMap, DFS +- traverse tree and create `map ` to fast removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- time: O(n) +- space: O(n) +*/ +class Solution { + + public List delNodes(TreeNode root, int[] toDelete) { + + Map rootMap = new HashMap<>(); + Map parentMap = new HashMap<>(); + buildMap(parentMap, root); + + rootMap.put(root.val, root); + + for (int val : toDelete) { + TreeNode nodeToDelete; + if (rootMap.containsKey(val)) { + nodeToDelete = rootMap.get(val); + rootMap.remove(val); + } else { + nodeToDelete = remove(parentMap.get(val), val); // node = 3, 5 + parentMap.remove(val); + } + if (nodeToDelete.left != null) rootMap.put(nodeToDelete.left.val, nodeToDelete.left); // add: 6, null + if (nodeToDelete.right != null) rootMap.put(nodeToDelete.right.val, nodeToDelete.right); // add: 7, null + } + + return new ArrayList<>(rootMap.values()); + } + + private TreeNode remove(TreeNode parent, int val) { + TreeNode node; + if (parent.left != null && parent.left.val == val) { + node = parent.left; + parent.left = null; + } else { + node = parent.right; + parent.right = null; + } + return node; + } + + private void buildMap(Map map, TreeNode node) { + if (node == null) return; + if (node.left != null) { + map.put(node.left.val, node); + buildMap(map, node.left); + } + if (node.right != null) { + map.put(node.right.val, node); + buildMap(map, node.right); + } + } +} + +``` \ No newline at end of file diff --git a/Java/1117. Building H2O.java b/Java/1117. Building H2O.java new file mode 100755 index 0000000..353924d --- /dev/null +++ b/Java/1117. Building H2O.java @@ -0,0 +1,115 @@ +M +tags: Thread, Semaphore, Lock + + +#### Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 + +#### Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +- semaphore: https://www.geeksforgeeks.org/semaphore-in-java/ + +``` +/* +There are two kinds of threads, oxygen and hydrogen. Your goal is to group these threads to form water molecules. There is a barrier where each thread has to wait until a complete molecule can be formed. Hydrogen and oxygen threads will be given releaseHydrogen and releaseOxygen methods respectively, which will allow them to pass the barrier. These threads should pass the barrier in groups of three, and they must be able to immediately bond with each other to form a water molecule. You must guarantee that all the threads from one molecule bond before any other threads from the next molecule do. + +In other words: + +If an oxygen thread arrives at the barrier when no hydrogen threads are present, it has to wait for two hydrogen threads. +If a hydrogen thread arrives at the barrier when no other threads are present, it has to wait for an oxygen thread and another hydrogen thread. +We don’t have to worry about matching the threads up explicitly; that is, the threads do not necessarily know which other threads they are paired up with. The key is just that threads pass the barrier in complete sets; thus, if we examine the sequence of threads that bond and divide them into groups of three, each group should contain one oxygen and two hydrogen threads. + +Write synchronization code for oxygen and hydrogen molecules that enforces these constraints. + + + +Example 1: + +Input: "HOH" +Output: "HHO" +Explanation: "HOH" and "OHH" are also valid answers. +Example 2: + +Input: "OOHHHH" +Output: "HHOHHO" +Explanation: "HOHHHO", "OHHHHO", "HHOHOH", "HOHHOH", "OHHHOH", "HHOOHH", "HOHOHH" and "OHHOHH" are also valid answers. + + +Constraints: + +Total length of input string will be 3n, where 1 ≤ n ≤ 20. +Total number of H will be 2n in the input string. +Total number of O will be n in the input string. +*/ + +/* +Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 +*/ +class H2O { + private Object lock = new Object(); + private int count = 0; + public H2O() { } + + public void hydrogen(Runnable releaseHydrogen) throws InterruptedException { + synchronized (lock) { + while(count==2) lock.wait(); + releaseHydrogen.run(); + count++; + lock.notifyAll(); + } + } + + public void oxygen(Runnable releaseOxygen) throws InterruptedException { + synchronized (lock) { + while(count!=2) lock.wait(); + releaseOxygen.run(); + count = 0; + lock.notifyAll(); + } + } +} + +/* +Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +*/ +class H2O { + Semaphore h, o; + public H2O() { + h = new Semaphore(2, true); + o = new Semaphore(0, true); + } + + public void hydrogen(Runnable releaseHydrogen) throws InterruptedException { + h.acquire(); + // releaseHydrogen.run() outputs "H". Do not change or remove this line. + releaseHydrogen.run(); + o.release(); + } + + public void oxygen(Runnable releaseOxygen) throws InterruptedException { + o.acquire(2); + // releaseOxygen.run() outputs "O". Do not change or remove this line. + releaseOxygen.run(); + h.release(2); + } +} + + + +``` \ No newline at end of file diff --git a/Java/112. Path Sum.java b/Java/112. Path Sum.java new file mode 100755 index 0000000..da6bc35 --- /dev/null +++ b/Java/112. Path Sum.java @@ -0,0 +1,56 @@ +E +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. + +Note: A leaf is a node with no children. + +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); + } +} + + +``` \ No newline at end of file diff --git a/Java/1123. Lowest Common Ancestor of Deepest Leaves.java b/Java/1123. Lowest Common Ancestor of Deepest Leaves.java new file mode 100755 index 0000000..ff12a8b --- /dev/null +++ b/Java/1123. Lowest Common Ancestor of Deepest Leaves.java @@ -0,0 +1,142 @@ +M +tags: DFS, Tree, BFS +time: O(n) +space: O(n) + +#### Method1: DFS, top-down +- key concetpL the `common ancester of deppest leaves` must have its `two branch being same depth`. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +- time: O(n) traversal 1 pass +- space: O(n) dfs worst case depth + +#### Method2: bottom-up, find leaves first and traverse backwards +- 1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +- 2) use leaf nodes to find way backwards till common node is found; return +- time: O(n) but two passes +- space: O(n) dsf + map storage +- this approach is more brutle and uses exrtra spaces + +``` + +/* +Given a rooted binary tree, return the lowest common ancestor of its deepest leaves. + +Recall that: + +The node of a binary tree is a leaf if and only if it has no children +The depth of the root of the tree is 0, and if the depth of a node is d, the depth of each of its children is d+1. +The lowest common ancestor of a set S of nodes is the node A with the largest depth such that every node in S is in the subtree with root A. + + +Example 1: + +Input: root = [1,2,3] +Output: [1,2,3] +Explanation: +The deepest leaves are the nodes with values 2 and 3. +The lowest common ancestor of these leaves is the node with value 1. +The answer returned is a TreeNode object (not an array) with serialization "[1,2,3]". +Example 2: + +Input: root = [1,2,3,4] +Output: [4] +Example 3: + +Input: root = [1,2,3,4,5] +Output: [2,4,5] + + +Constraints: + +The given tree will have between 1 and 1000 nodes. +Each node of the tree will have a distinct value between 1 and 1000. +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Method1: the common ancester of deppest leaves must have its two branch being same depth. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +*/ +class Solution { + TreeNode lca; + int max = 0; + public TreeNode lcaDeepestLeaves(TreeNode root) { + if (root == null) return root; + dfs(root, 0); + return lca; + } + + private int dfs(TreeNode node, int depth) { + if (node == null) { + max = Math.max(max, depth); + return depth; + } + + int leftDepth = dfs(node.left, depth + 1); + int rightDepth = dfs(node.right, depth + 1); + + if (leftDepth == max && rightDepth == max) { + lca = node; + } + return Math.max(leftDepth, rightDepth); + } +} + + +/* +Method2: +1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +2) use leaf nodes to find way backwards till common node is found; return +*/ +class Solution { + Map parentMap = new HashMap<>(); + Map> depthMap = new HashMap<>(); + int max = 0; + public TreeNode lcaDeepestLeaves(TreeNode root) { + if (root == null) return root; + dfs(root, 0); + Queue queue = depthMap.get(max); // leaves + if (queue.size() == 1) return queue.poll(); + + while (!queue.isEmpty()) { + int size = queue.size(); + Set set = new HashSet<>(); + while (size-- > 0) { + TreeNode node = queue.poll(); + set.add(parentMap.get(node)); + } + if (set.size() == 1) return set.iterator().next(); + queue.addAll(set); + } + + return root; + } + + private void dfs(TreeNode node, int depth) { + if (node.left == null && node.right == null) { + max = Math.max(max, depth); + depthMap.putIfAbsent(depth, new LinkedList<>()); + depthMap.get(depth).offer(node); + return; + } + if (node.left != null) { + parentMap.put(node.left, node); + dfs(node.left, depth + 1); + } + if (node.right != null) { + parentMap.put(node.right, node); + dfs(node.right, depth + 1); + } + } +} +``` \ No newline at end of file diff --git a/Java/114. Flatten Binary Tree to Linked List.java b/Java/114. Flatten Binary Tree to Linked List.java new file mode 100755 index 0000000..b24946b --- /dev/null +++ b/Java/114. Flatten Binary Tree to Linked List.java @@ -0,0 +1,156 @@ +M +tags: Binary Tree, DFS +time: O(n) +space: O(n), stacks + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### Method1: DFS return tail node +- DFS to return the tail of the flattened list +- Careful handling the null child. Choose one and both works: + - Option1: put non-null as right child and continue dfs + - Option2: put non-null as left child and continue dfs + +#### Method2: void DFS +- latten the tree, no extra space. + - 1. Set right node in buffer, and connect: `root.right = root.left`, DFS flatten(root.right) + - 3. 移花接木: traverse to tail of the current root.right and attach the buffer node. + - 3. flatten the remaining of buffer + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + +``` +/* +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. + +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +// Method1, Option1: DFS by returning and attaching tail, move non-null child to right side +class Solution { + public void flatten(TreeNode root) { + if (root == null) return; + dfs(root); + } + + public TreeNode dfs(TreeNode node) { + if (node.left == null && node.right == null) return node; + if (node.right == null) { // when one of them is null, attach remaining to right side + node.right = node.left; + node.left = null; + } + TreeNode temp = node.right; // buffer + + if (node.left != null) { // DFS to find tail, and attach right to tail + TreeNode tail = dfs(node.left); + node.right = node.left; + node.left = null; + tail.right = temp; + } + + // DFS the rest right + return dfs(temp); + } +} + +// Method1, Option2: DFS by returning and attaching tail, move non-null child to left +class Solution { + public void flatten(TreeNode root) { + if (root == null) return; + dfs(root); + } + + public TreeNode dfs(TreeNode node) { + if (node.left == null && node.right == null) return node; + if (node.left == null) { // node.right != null, then swich right to left, so the logic below can work + node.left = node.right; + node.right = null; + } + + TreeNode temp = node.right; + + TreeNode tail = dfs(node.left); + node.right = node.left; + node.left = null; + + if (temp == null) return tail; // do not test null right + + tail.right = temp; + return dfs(temp); + } +} + +/* +Method2: +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 (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/1146. Snapshot Array.java b/Java/1146. Snapshot Array.java new file mode 100755 index 0000000..04fced3 --- /dev/null +++ b/Java/1146. Snapshot Array.java @@ -0,0 +1,89 @@ +M +tags: Array, Hash Table, TreeMap +time: O(1) set, O(logn) get, O(x) snap, x = # of changes +space: O(n * m), n = array size, m = # of snaps + + +#### Hash Table, TreeMap; atomic save +- store efficiently: use List>. only preserve changed itemd +- if no match, find last modifed item based on snapId, use TreeMap.floorEntry + - map.floorEntry(id) return the item.key lower or equal to id +- Utilize a `buffer: Map` and perform atomic save + +``` +/* +Implement a SnapshotArray that supports the following interface: + +SnapshotArray(int length) initializes an array-like data structure with the given length. Initially, each element equals 0. +void set(index, val) sets the element at the given index to be equal to val. +int snap() takes a snapshot of the array and returns the snap_id: the total number of times we called snap() minus 1. +int get(index, snap_id) returns the value at the given index, at the time we took the snapshot with the given snap_id + + +Example 1: + +Input: ["SnapshotArray","set","snap","set","get"] +[[3],[0,5],[],[0,6],[0,0]] +Output: [null,null,0,null,5] +Explanation: +SnapshotArray snapshotArr = new SnapshotArray(3); // set the length to be 3 +snapshotArr.set(0,5); // Set array[0] = 5 +snapshotArr.snap(); // Take a snapshot, return snap_id = 0 +snapshotArr.set(0,6); +snapshotArr.get(0,0); // Get the value of array[0] with snap_id = 0, return 5 + + +Constraints: + +1 <= length <= 50000 +At most 50000 calls will be made to set, snap, and get. +0 <= index < length +0 <= snap_id < (the total number of times we call snap()) +0 <= val <= 10^9 +*/ +class SnapshotArray { + List> snaps; + Map buffer; + int snapId = 0; + public SnapshotArray(int length) { + buffer = new HashMap<>(); + snaps = new ArrayList<>(); + for (int i = 0; i < length; i++) { + snaps.add(new TreeMap<>()); + snaps.get(i).put(snapId, 0); + } + } + + public void set(int index, int val) { + buffer.put(index, val); + } + + public int snap() { + for (int index : buffer.keySet()) { + snaps.get(index).put(snapId, buffer.get(index)); + } + buffer = new HashMap<>(); + snapId++; + return snapId - 1; + } + + public int get(int index, int snap_id) { + return snaps.get(index).floorEntry(snap_id).getValue(); + } +} + +/** + * Your SnapshotArray object will be instantiated and called as such: + * SnapshotArray obj = new SnapshotArray(length); + * obj.set(index,val); + * int param_2 = obj.snap(); + * int param_3 = obj.get(index,snap_id); + */ +/** + * Your SnapshotArray object will be instantiated and called as such: + * SnapshotArray obj = new SnapshotArray(length); + * obj.set(index,val); + * int param_2 = obj.snap(); + * int param_3 = obj.get(index,snap_id); + */ +``` \ No newline at end of file diff --git a/Java/1153. String Transforms Into Another String.java b/Java/1153. String Transforms Into Another String.java new file mode 100755 index 0000000..9476284 --- /dev/null +++ b/Java/1153. String Transforms Into Another String.java @@ -0,0 +1,64 @@ +H +tags: Graph +time: O(n) +space: O(n) + +#### Graph +- analysis: + - 1) should not have mult-origin cases: 1 char maps to 1 char at maximum + - 2) need a buffer char NOT exist in target to hold inter-media transformation + - check open char (out of 26 lower letter) that is NOT in target chars +- impl the validation rules +- more to read in https://leetcode.com/problems/string-transforms-into-another-string/discuss?currentPage=1&orderBy=most_votes&query= + +``` +/* +Given two strings str1 and str2 of the same length, determine whether you can transform str1 into str2 by doing zero or more conversions. + +In one conversion you can convert all occurrences of one character in str1 to any other lowercase English character. + +Return true if and only if you can transform str1 into str2. + + + +Example 1: + +Input: str1 = "aabcc", str2 = "ccdee" +Output: true +Explanation: Convert 'c' to 'e' then 'b' to 'd' then 'a' to 'c'. Note that the order of conversions matter. +Example 2: + +Input: str1 = "leetcode", str2 = "codeleet" +Output: false +Explanation: There is no way to transform str1 to str2. + + +Note: + +1 <= str1.length == str2.length <= 10^4 +Both str1 and str2 contain only lowercase English letters. +*/ + +/* +#### Graph +- analysis: + - 1) should not have mult-origin cases: 1 char maps to 1 char at maximum + - 2) need a buffer char NOT exist in target to hold inter-media transformation + - check open char (out of 26 lower letter) that is NOT in target chars +- impl the validation rules +*/ +class Solution { + public boolean canConvert(String str1, String str2) { + if (str1.equals(str2)) return true; + Map map = new HashMap<>(); + + for (int i = 0; i < str1.length(); i++) { + char a = str1.charAt(i), b = str2.charAt(i); + map.putIfAbsent(a, b); + if (map.get(a) != b) return false; + } + + return new HashSet<>(map.values()).size() < 26; + } +} +``` \ No newline at end of file diff --git a/Java/1161. Maximum Level Sum of a Binary Tree.java b/Java/1161. Maximum Level Sum of a Binary Tree.java new file mode 100755 index 0000000..4ebc743 --- /dev/null +++ b/Java/1161. Maximum Level Sum of a Binary Tree.java @@ -0,0 +1,75 @@ +M +tags: Graph, BFS, DFS +time: O(n) visit all nodes +space: O(n) + + +#### BFS +- simply calc each level sum with BFS +- top-level is processed first, since we go from top level -> deeper level + - only update result if sum is truly > global MAX. + +``` +/* +Given the root of a binary tree, the level of its root is 1, the level of its children is 2, and so on. + +Return the smallest level X such that the sum of all the values of nodes at level X is maximal. + + + +Example 1: + + + +Input: [1,7,0,7,-8,null,null] +Output: 2 +Explanation: +Level 1 sum = 1. +Level 2 sum = 7 + 0 = 7. +Level 3 sum = 7 + -8 = -1. +So we return the level with the maximum sum which is level 2. + + +Note: + +The number of nodes in the given tree is between 1 and 10^4. +-10^5 <= node.val <= 10^5 +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + int max = Integer.MIN_VALUE; + + public int maxLevelSum(TreeNode root) { + Queue queue = new LinkedList<>(); + queue.offer(root); + int result = 0, level = 0; + + while (!queue.isEmpty()) { + int size = queue.size(); + int sum = 0; + level++; + while (size-- > 0) { + TreeNode node = queue.poll(); + sum += node.val; + if (node.left != null) queue.offer(node.left); + if (node.right != null) queue.offer(node.right); + } + if (sum > max) { + max = sum; + result = level; + } + } + + return result; + } +} +``` \ No newline at end of file diff --git a/Java/1170. Compare Strings by Frequency of the Smallest Character.java b/Java/1170. Compare Strings by Frequency of the Smallest Character.java new file mode 100755 index 0000000..8845e51 --- /dev/null +++ b/Java/1170. Compare Strings by Frequency of the Smallest Character.java @@ -0,0 +1,137 @@ +E +tags: Array, String +time: O(m + n) +space: O(m + n) + +#### Method1: letter frequency map, kinda bucket sort +- Goal: find word count that fits into `f(queries[i]) < f(W)` +- What if: we can store the f(W) as preSum, then goal: `rst[i] = preSum[end] - preSum[queryWordCount]` + - count(W) and store in count[i] + - calc preSum + - processs queries array +- kinda bucket sort: + - 1) we know the boundary of the word length, so we can create `bucket` + - 2) `function count(w)` can produce a value that sort a word into a specific bucket slot + - extend: the bucket can store keys that links back to the word (if there are follow up questions) +- time: O(m + n) +- space: O(m + n) + +#### Method2: No brain solution, basic impl based on the desc, w/o search. +- time: + - O(nm) to count all words, O(nlogn) to sort the wordCount + - O(nm) to to count all queries + - O(n^2) to perform the match +- space: O(n) + +``` +/* +Let's define a function f(s) over a non-empty string s, which calculates the frequency of the smallest character in s. For example, if s = "dcce" then f(s) = 2 because the smallest character is "c" and its frequency is 2. + +Now, given string arrays queries and words, return an integer array answer, where each answer[i] is the number of words such that f(queries[i]) < f(W), where W is a word in words. + + + +Example 1: + +Input: queries = ["cbd"], words = ["zaaaz"] +Output: [1] +Explanation: On the first query we have f("cbd") = 1, f("zaaaz") = 3 so f("cbd") < f("zaaaz"). +Example 2: + +Input: queries = ["bbb","cc"], words = ["a","aa","aaa","aaaa"] +Output: [1,2] +Explanation: On the first query only f("bbb") < f("aaaa"). On the second query both f("aaa") and f("aaaa") are both > f("cc"). + + +Constraints: + +1 <= queries.length <= 2000 +1 <= words.length <= 2000 +1 <= queries[i].length, words[i].length <= 10 +queries[i][j], words[i][j] are English lowercase letters. +*/ +/* +#### Method: letter frequency map +- Goal: find word count that fits into `f(queries[i]) < f(W)` +- What if: we can store the f(W) as preSum, then rst[i] = preSum[i] - preSum[i-1] + - count(W) and store in count[i] + - calc preSum + - processs queries array +*/ +class Solution { + public int[] numSmallerByFrequency(String[] queries, String[] words) { + int n = words.length; + int[] preSum = buildPreSum(words); + + int[] rst = new int[queries.length]; + for (int i = 0; i < queries.length; i++) { + int count = count(queries[i]); + rst[i] = preSum[preSum.length - 1] - preSum[count]; + } + return rst; + } + + private int[] buildPreSum(String[] words) { + int[] freqCounts = new int[11]; // max word length is 10 as suggested + for (String w : words) { + int count = count(w); + freqCounts[count]++; + } + int[] preSum = new int[11]; + preSum[0] = freqCounts[0]; + for (int i = 1; i < freqCounts.length; i++) preSum[i] += preSum[i - 1] + freqCounts[i]; + + return preSum; + } + + private int count(String w) { + int[] count = new int[26]; + for (char c : w.toCharArray()) count[c - 'a']++; + for (int num : count) { + if (num != 0) return num; + } + return 0; + } +} + + +/* +Method2: No brain solution, basic impl based on the desc, w/o search. +time: +- O(nm) to count all words, O(nlogn) to sort the wordCount +- O(nm) to to count all queries +- O(n^2) to perform the match +space: O(n) +*/ +class Solution { + public int[] numSmallerByFrequency(String[] queries, String[] words) { + int n = words.length; + int[] wordCounts = new int[n]; + for (int i = 0; i < n; i++) wordCounts[i] = count(words[i]); + Arrays.sort(wordCounts); + + int[] rst = new int[queries.length]; + for (int i = 0; i < queries.length; i++) { + rst[i] = calc(wordCounts, count(queries[i])); + } + return rst; + } + + private int calc(int[] wordCounts, int count) { + int rst = 0; + for (int i = wordCounts.length - 1; i >= 0; i--) { + if (wordCounts[i] <= count) break; + rst++; + } + return rst; + } + + // cound lowest alphabetical char + private int count(String w) { + int[] count = new int[26]; + for (char c : w.toCharArray()) count[c - 'a']++; + for (int num : count) if (num != 0) return num; + return 0; + } +} +``` \ No newline at end of file diff --git a/Java/118. Pascal's Triangle.java b/Java/118. Pascal's Triangle.java new file mode 100755 index 0000000..0f42908 --- /dev/null +++ b/Java/118. Pascal's Triangle.java @@ -0,0 +1,44 @@ +E +tags: Array, List, Basic Implementation +time: O(n^2) based on pascal triangle size +space: O(n^2) + +``` +/* +Given a non-negative integer numRows, generate the first numRows of Pascal's triangle. + +In Pascal's triangle, each number is the sum of the two numbers directly above it. + +Example: + +Input: 5 +Output: +[ + [1], + [1,1], + [1,2,1], + [1,3,3,1], + [1,4,6,4,1] +] +*/ + +class Solution { + public List> generate(int numRows) { + List> result = new ArrayList<>(); + if (numRows == 0) return result; + if (numRows >= 1) result.add(Arrays.asList(1)); + if (numRows >= 2) result.add(Arrays.asList(1, 1)); + + for (int row = 2; row < numRows; row++) { + List list = new ArrayList<>(Arrays.asList(1, 1)); + List lastRow = result.get(row - 1); + int end = row - 1; + for (int i = 1; i <= end; i++) { + list.add(i, lastRow.get(i) + lastRow.get(i - 1)); + } + result.add(list); + } + return result; + } +} +``` \ No newline at end of file diff --git a/Java/119. Pascal's Triangle II.java b/Java/119. Pascal's Triangle II.java new file mode 100755 index 0000000..9f4355b --- /dev/null +++ b/Java/119. Pascal's Triangle II.java @@ -0,0 +1,57 @@ +E +tags: Array, Basic Implementation +time: O(k^2), pascal triangle size +space: O(k^2) + +简单处理 list. code is very similar to Pascal triangle I. + +- 注意 `list = Arrays.asList(x, y, z ...)` 给fixed-size list, 不能直接 list.add(). +- Use `new ArrayList<>(Arrays.asList(...))` to wrap it up. + + +``` + + +/* +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 +class Solution { + public List getRow(int rowIndex) { + List result = new ArrayList<>(); + if (rowIndex == 0) result.add(1); + if (rowIndex >= 1) result.add(1); + + for (int row = 1; row <= rowIndex; row++) { + List list = new ArrayList<>(Arrays.asList(1, 1)); + List lastRow = result; + int end = row - 1; + for (int i = 1; i <= end; i++) { + list.add(i, lastRow.get(i) + lastRow.get(i - 1)); + } + result = list; + } + return result; + } +} +``` \ No newline at end of file diff --git a/Java/1197. Minimum Knight Moves.java b/Java/1197. Minimum Knight Moves.java new file mode 100755 index 0000000..d4453ab --- /dev/null +++ b/Java/1197. Minimum Knight Moves.java @@ -0,0 +1,84 @@ +M +tags: BFS +time: O(8^n) +space: O(8^n) + +#### BFS +- `from starting point, find min steps to reach certain point`: think of BFS + - similar: shortest path, shortest distance +- bfs: minimum steps, enumerate the possible moves + - move closer to x or y (test 8 possible directions) + - add possible moves in queue +- use visited to cache visited coordinates +- time: O(8^n), # of BFS branches +- space: O(8^n), # of BFS branche nodes + +``` +/* +In an infinite chess board with coordinates from -infinity to +infinity, you have a knight at square [0, 0]. + +A knight has 8 possible moves it can make, as illustrated below. Each move is two squares in a cardinal direction, then one square in an orthogonal direction. + + + +Return the minimum number of steps needed to move the knight to the square [x, y]. It is guaranteed the answer exists. + + + +Example 1: + +Input: x = 2, y = 1 +Output: 1 +Explanation: [0, 0] → [2, 1] +Example 2: + +Input: x = 5, y = 5 +Output: 4 +Explanation: [0, 0] → [2, 1] → [4, 2] → [3, 4] → [5, 5] + + +Constraints: + +|x| + |y| <= 300 +*/ + +/* +- bfs: minimum steps, enumerate the possible moves + - move closer to x or y (test 8 possible directions) + - add possible moves in queue +*/ +class Solution { + int[] dx = {-1, -2, -2, -1, 1, 2, 2, 1}; + int[] dy = {-2, -1, 1, 2, -2, -1, 1, 2}; + Set visited = new HashSet<>(); + public int minKnightMoves(int x, int y) { + x = Math.abs(x); + y = Math.abs(y); + Queue queue = new LinkedList<>(); + queue.offer(new int[]{0, 0}); + visited.add("0,0"); + int count = 0; + + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { + int[] pos = queue.poll(); + if (pos[0]==x && pos[1]==y) return count; + addMoves(queue, pos, x, y); + } + count++; + } + return -1; + } + + private void addMoves(Queue queue, int[] pos, int x, int y) { + for (int i = 0; i < 8; i++) { + int nx = pos[0] + dx[i], ny = pos[1] + dy[i]; // (x,y) at positive direction; `nx >= -1 && ny >= -1` moves towards (x,y) + if (!visited.contains(nx+","+ny) && nx >= -1 && ny >= -1) { + queue.offer(new int[]{nx, ny}); + visited.add(nx+","+ny); + } + } + } +} +``` \ No newline at end of file diff --git a/Java/12. Integer to Roman.java b/Java/12. Integer to Roman.java new file mode 100755 index 0000000..50e7ed3 --- /dev/null +++ b/Java/12. Integer to Roman.java @@ -0,0 +1,106 @@ +M +tags: Math, String, Basic Implementation +time: O(n) +space: O(n) + +#### String, Basic implementation +- Parse each digit based on rules +- 1) parse: analyze the situations +/* +- /1000 -> M's, +- /100: -> C's for (100 - 399), CD(400), DC's for (501 - 899), CM(900+) +- /10: -> X's (10, 39), XL (40's), LX(51 - 89), XC(90+) +- /1: -> I's for (1-3), IV(4), VI(5-8), IX (9) +*/ +- 2) print, print some letter repeatedly. i.e. `III` + +``` +/** +Roman numerals are represented by seven different symbols: I, V, X, L, C, D and M. + +Symbol Value +I 1 +V 5 +X 10 +L 50 +C 100 +D 500 +M 1000 +For example, two is written as II in Roman numeral, just two one's added together. Twelve is written as, XII, which is simply X + II. The number twenty seven is written as XXVII, which is XX + V + II. + +Roman numerals are usually written largest to smallest from left to right. However, the numeral for four is not IIII. Instead, the number four is written as IV. Because the one is before the five we subtract it making four. The same principle applies to the number nine, which is written as IX. There are six instances where subtraction is used: + +I can be placed before V (5) and X (10) to make 4 and 9. +X can be placed before L (50) and C (100) to make 40 and 90. +C can be placed before D (500) and M (1000) to make 400 and 900. +Given an integer, convert it to a roman numeral. Input is guaranteed to be within the range from 1 to 3999. + +Example 1: + +Input: 3 +Output: "III" +Example 2: + +Input: 4 +Output: "IV" +Example 3: + +Input: 9 +Output: "IX" +Example 4: + +Input: 58 +Output: "LVIII" +Explanation: L = 50, V = 5, III = 3. +Example 5: + +Input: 1994 +Output: "MCMXCIV" +Explanation: M = 1000, CM = 900, XC = 90 and IV = 4. +*/ + +/* +- Divide by each symbol value to get remain +- translate the remain by symbo combinations + - create map of 4/9, 40/90, 400/900 +- /1000 -> M's, +- /100: -> C's for (100 - 399), CD(400), DC's for (501 - 899), CM(900+) +- /10: -> X's (10, 39), XL (40's), LX(51 - 89), XC(90+) +- /1: -> I's for (1-3), IV(4), VI(5-8), IX (9) +*/ +class Solution { + public String intToRoman(int num) { + + StringBuffer sb = new StringBuffer(); + + sb.append(parse("M", null, null, num / 1000)); // M's + num %= 1000; + + sb.append(parse("C", "D", "M", num / 100)); // C's + num %= 100; + + sb.append(parse("X", "L", "C", num / 10)); // X's + num %= 10; + + sb.append(parse("I", "V", "X", num)); // I's + + return sb.toString(); + } + + private String parse(String curr, String mid, String next, int num) { + if (num == 0) return ""; + if (num <= 3) return repeat(curr, num); + if (num == 4) return curr + mid; + if (num <= 8) return mid + repeat(curr, num % 5); + return curr + next; + } + + private String repeat(String curr, int num) { + StringBuffer sb = new StringBuffer(); + while (num-- > 0) { + sb.append(curr); + } + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/1203. Sort Items by Groups Respecting Dependencies.java b/Java/1203. Sort Items by Groups Respecting Dependencies.java new file mode 100755 index 0000000..3bab0f6 --- /dev/null +++ b/Java/1203. Sort Items by Groups Respecting Dependencies.java @@ -0,0 +1,170 @@ +H +tags: DFS, BFS, Graph, Topological Sort +time: O(V + E) to traverse the graph, #nodes + #edges +space: O(V + E) + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + +``` +/* +There are n items each belonging to zero or one of m groups where group[i] is the group that the i-th item belongs to and it's equal to -1 if the i-th item belongs to no group. The items and the groups are zero indexed. A group can have no item belonging to it. + +Return a sorted list of the items such that: + +The items that belong to the same group are next to each other in the sorted list. +There are some relations between these items where beforeItems[i] is a list containing all the items that should come before the i-th item in the sorted array (to the left of the i-th item). +Return any solution if there is more than one solution and return an empty list if there is no solution. + + + +Example 1: + + + +Input: n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3,6],[],[],[]] +Output: [6,3,4,1,5,2,0,7] +Example 2: + +Input: n = 8, m = 2, group = [-1,-1,1,0,0,1,0,-1], beforeItems = [[],[6],[5],[6],[3],[],[4],[]] +Output: [] +Explanation: This is the same as example 1 except that 4 needs to be before 6 in the sorted list. + + +Constraints: + +1 <= m <= n <= 3*10^4 +group.length == beforeItems.length == n +-1 <= group[i] <= m-1 +0 <= beforeItems[i].length <= n-1 +0 <= beforeItems[i][j] <= n-1 +i != beforeItems[i][j] +beforeItems[i] does not contain duplicates elements. +*/ + +/* +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + 1) group items to map > + 2) build group graph + 3) topo sort group -> return sorted group id list + 4) for each group: build item graph, topo sort items -> return sorted item list + 5) flatten and return results +*/ +/* +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + 1) group items to map > + 2) build group graph + 3) topo sort group -> return sorted group id list + 4) for each group: build item graph, topo sort items -> return sorted item list + 5) flatten and return results +*/ +/* +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + 1) group items to map > + 2) build group graph + 3) topo sort group -> return sorted group id list + 4) for each group: build item graph, topo sort items -> return sorted item list + 5) flatten and return results +*/ +class Solution { + public int[] sortItems(int n, int m, int[] group, List> beforeItems) { + + Map> groupMap = buildGroup(n, m, group); + Map> groupGraph = buildGroupGraph(n, group, beforeItems); + List sortedGroupIds = topoSort(groupGraph, groupMap.keySet()); + List rst = new ArrayList<>(); + for (int groupId : sortedGroupIds) { + Map> itemGraph = buildItemGraph(groupMap.get(groupId), beforeItems); + List sortedItemIds = topoSort(itemGraph, new HashSet<>(groupMap.get(groupId))); + rst.addAll(sortedItemIds); + } + + if (rst.size() != n) return new int[] {}; + + int[] result = new int[n]; + for (int i = 0; i < rst.size(); i++) { + result[i] = rst.get(i); + } + return result; + } + + private Map> buildGroup(int n, int m, int[] group) { + Map> map = new HashMap<>(); + for (int i = 0; i < n; i++) { + int groupId = group[i] == -1 ? m++ : group[i]; + group[i] = groupId; + map.putIfAbsent(groupId, new ArrayList<>()); + map.get(groupId).add(i); + } + return map; + } + + private List topoSort(Map> graph, Set set) { + Map indegree = new HashMap<>(); + for (int item : set) { // init all elements from the set + indegree.put(item, 0); + } + for (int node : graph.keySet()) { // sink node + if (!set.contains(node)) continue; // ignore the dependency on nodes outside of the target set + for (int parent : graph.get(node)) { // parent node + indegree.put(parent, indegree.get(parent) + 1); + } + } + Queue queue = new LinkedList<>(); + for (int key : indegree.keySet()) { + if (indegree.get(key) == 0) queue.offer(key); + } + + List rst = new ArrayList<>(); + while (!queue.isEmpty()) { + int node = queue.poll(); + rst.add(node); + if (!graph.containsKey(node)) continue; + for (int parent : graph.get(node)) { + indegree.put(parent, indegree.get(parent) - 1); + if (indegree.get(parent) == 0) queue.offer(parent); + } + } + + return rst; + } + + private Map> buildItemGraph(List items, List> beforeItems) { + Map> graph = new HashMap<>(); + for (int i : items) { + for (int item : beforeItems.get(i)) { + graph.putIfAbsent(item, new ArrayList<>()); + graph.get(item).add(i); + } + } + return graph; + } + + private Map> buildGroupGraph(int n, int[] group, List> beforeItems) { + Map> graph = new HashMap<>(); + for (int i = 0; i < n; i++) { + int groupId = group[i]; + for (int item : beforeItems.get(i)) { + if (groupId == group[item]) continue; + graph.putIfAbsent(group[item], new ArrayList<>()); + graph.get(group[item]).add(groupId); + } + } + return graph; + } + +} +``` \ No newline at end of file diff --git a/Java/121. Best Time to Buy and Sell Stock.java b/Java/121. Best Time to Buy and Sell Stock.java new file mode 100755 index 0000000..c8d77af --- /dev/null +++ b/Java/121. Best Time to Buy and Sell Stock.java @@ -0,0 +1,115 @@ +E +tags: Array, DP, Sequence DP + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### min[n] +- 每天都就交易价格,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 dont need to buyin at 5, 3, 6, 4 later on; + - only need to 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 (i.e., buy one and sell one share of the stock), design an algorithm to find the maximum profit. + +Note that you cannot sell a stock before you buy one. + +Example 1: + +Input: [7,1,5,3,6,4] +Output: 5 +Explanation: Buy on day 2 (price = 1) and sell on day 5 (price = 6), profit = 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 +Explanation: In this case, no transaction is done, i.e. max profit = 0. +*/ + +/* +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. +*/ +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length <= 1) return 0; + int profit = 0, n = prices.length; + int[] min = new int[n]; + min[0] = prices[0]; + for (int i = 1; i < n; 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. +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; + } +} + +// DP +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; + } +} + +``` \ No newline at end of file diff --git a/Java/1213. Intersection of Three Sorted Arrays.java b/Java/1213. Intersection of Three Sorted Arrays.java new file mode 100755 index 0000000..c66ea69 --- /dev/null +++ b/Java/1213. Intersection of Three Sorted Arrays.java @@ -0,0 +1,103 @@ +E +tags: Hash Table, Two Pointers +time: O(m + n + h) two pointers approach +space: O(1) + +Very similar to 349.Intersection of Two Arrays. + +#### Hash Table +- Use set to check +- Verify duplicates at end rst + +#### Two Pointers +- similar to Intersection of Two Sorted Arrays +- Start from front/back, process 1 item at a time + - if match, move all pointers +- Optoin1: check from back +- Optoin2: check from frotn + +``` + +/* + +Given three integer arrays arr1, arr2 and arr3 sorted in strictly increasing order, return a sorted array of only the integers that appeared in all three arrays. + + + +Example 1: + +Input: arr1 = [1,2,3,4,5], arr2 = [1,2,5,7,9], arr3 = [1,3,4,5,8] +Output: [1,5] +Explanation: Only 1 and 5 appeared in the three arrays. + + +Constraints: + +1 <= arr1.length, arr2.length, arr3.length <= 1000 +1 <= arr1[i], arr2[i], arr3[i] <= 2000 +*/ + +class Solution { + public List arraysIntersection(int[] arr1, int[] arr2, int[] arr3) { + Set set1 = toSet(arr1), set2 = toSet(arr2); + + List rst = new ArrayList<>(); + for (int num : arr3) { + if (set1.contains(num) && set2.contains(num)) { + if (rst.size() == 0) rst.add(num); + else if (rst.size() > 0 && rst.get(rst.size() - 1) != num) rst.add(num); + } + } + return rst; + } + + private Set toSet(int[] nums) { + Set set = new HashSet<>(); + for (int num : nums) set.add(num); + return set; + } +} + +// Method2: Two Pointers, Optoin1: check from back +class Solution { + public List arraysIntersection(int[] arr1, int[] arr2, int[] arr3) { + List rst = new LinkedList<>(); + int i = arr1.length - 1, j = arr2.length - 1, k = arr3.length - 1; + + while (i >= 0 && j >= 0 && k >= 0) { + if (arr1[i] == arr2[j] && arr2[j] == arr3[k]) { + if (rst.isEmpty() || arr1[i] != rst.get(rst.size() - 1)) rst.add(0, arr1[i]); + i--; + j--; + k--; + } else if (arr2[j] < arr3[k]) k--; + else if (arr1[i] < arr2[j]) j--; + else i--; + } + + return rst; + } +} + +// Method2: Two Pointers, Optoin2: check from front +class Solution { + public List arraysIntersection(int[] arr1, int[] arr2, int[] arr3) { + List rst = new LinkedList<>(); + + int i = 0, j = 0, k = 0; + + while (i < arr1.length && j < arr2.length && k < arr3.length) { + if (arr1[i] == arr2[j] && arr2[j] == arr3[k]) { + if (rst.isEmpty() || arr1[i] != rst.get(rst.size() - 1)) rst.add(arr1[i]); + i++; + j++; + k++; + } else if (arr1[i] < arr2[j]) i++; + else if (arr2[j] < arr3[k]) j++; + else k++; + } + + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/1216. Valid Palindrome III.java b/Java/1216. Valid Palindrome III.java new file mode 100755 index 0000000..bf48b2a --- /dev/null +++ b/Java/1216. Valid Palindrome III.java @@ -0,0 +1,123 @@ +H +tags: String, DP, DFS, Memoization +time: O(n^2) +space: O(n^2) + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + +``` +/** +Given a string s and an integer k, find out if the given string is a K-Palindrome or not. + +A string is K-Palindrome if it can be transformed into a palindrome by removing at most k characters from it. + + + +Example 1: + +Input: s = "abcdeca", k = 2 +Output: true +Explanation: Remove 'b' and 'e' characters. + + +Constraints: + +1 <= s.length <= 1000 +s has only lowercase English letters. +1 <= k <= s.length +*/ + +// Method1: DP Find Longest Palindrome Subsequence length +class Solution { + public boolean isValidPalindrome(String s, int k) { + int n = s.length(); + int lps = findLpsLength(s); + return Math.abs(lps - n) <= k; + } + + private int findLpsLength(String s) { + int n = s.length(); + int[][] dp = new int[n][n]; + for (int i = n - 1; i >= 0; i--){ + dp[i][i] = 1; + for (int j = i + 1; j < n; j++) { + if (s.charAt(i) == s.charAt(j)) dp[i][j] = dp[i + 1][j - 1] + 2; + else dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]); + } + } + return dp[0][n - 1]; + } +} + +/* +Method2: +- top-down approach: pick non-equal left/right point to skip, and recursively procceed +- dfs(s, left, right, k) +- use boolean memo[left][right] to cache calculated range to avoid repeating +- end state: + - if visited, return val + - k == 0, return if left and right char equal + +*/ +// Option1: with 3d memo. exceed memory: O(n^k) space +class Solution { + Boolean[][][] memo; + public boolean isValidPalindrome(String s, int k) { + int n = s.length(); + memo = new Boolean[n][n][k + 1]; + return dfs(s, 0, n - 1, k); + } + + private boolean dfs(String s, int left, int right, int k) { + if (left >= right) return true; + if (memo[left][right][k] != null) return memo[left][right][k]; + + char a = s.charAt(left), b = s.charAt(right); + if (a == b) memo[left][right][k] = dfs(s, left + 1, right - 1, k); + else if (k > 0) memo[left][right][k] = dfs(s, left + 1, right, k - 1) || dfs(s, left, right - 1, k - 1); + else memo[left][right][k] = false; + + return memo[left][right][k]; + } +} + + +// Option2: with hashmap of custom key; timeout: O(n^2) time +class Solution { + Map memo; + public boolean isValidPalindrome(String s, int k) { + int n = s.length(); + memo = new HashMap<>(); + return dfs(s, 0, n - 1, k); + } + + private boolean dfs(String s, int left, int right, int k) { + if (left >= right) return true; + String key = getKey(left, right, k); + if (memo.containsKey(key)) return memo.get(key); + + char a = s.charAt(left), b = s.charAt(right); + if (a == b) memo.put(key, dfs(s, left + 1, right - 1, k)); + else if (k > 0) memo.put(key, dfs(s, left + 1, right, k - 1) || dfs(s, left, right - 1, k - 1)); + else memo.put(key, false); + + return memo.get(key); + } + + private String getKey(int left, int right, int k) { + return String.format("%s@%s@%s", left, right, k); + } +} +``` \ No newline at end of file diff --git a/Java/1219. Path with Maximum Gold.java b/Java/1219. Path with Maximum Gold.java new file mode 100755 index 0000000..5cd35f9 --- /dev/null +++ b/Java/1219. Path with Maximum Gold.java @@ -0,0 +1,89 @@ +M +tags: DFS, Backtracking +time: O(n^2) +space: O(n) recursive depth + + +### DFS, Backtracking +- typical recursive visit all situation + + +``` +/* +In a gold mine grid of size m * n, each cell in this mine has an integer representing the amount of gold in that cell, 0 if it is empty. + +Return the maximum amount of gold you can collect under the conditions: + +Every time you are located in a cell you will collect all the gold in that cell. +From your position you can walk one step to the left, right, up or down. +You can't visit the same cell more than once. +Never visit a cell with 0 gold. +You can start and stop collecting gold from any position in the grid that has some gold. + + +Example 1: + +Input: grid = [[0,6,0],[5,8,7],[0,9,0]] +Output: 24 +Explanation: +[[0,6,0], + [5,8,7], + [0,9,0]] +Path to get the maximum gold, 9 -> 8 -> 7. +Example 2: + +Input: grid = [[1,0,7],[2,0,6],[3,4,5],[0,3,0],[9,0,20]] +Output: 28 +Explanation: +[[1,0,7], + [2,0,6], + [3,4,5], + [0,3,0], + [9,0,20]] +Path to get the maximum gold, 1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7. + + +Constraints: + +1 <= grid.length, grid[i].length <= 15 +0 <= grid[i][j] <= 100 +There are at most 25 cells containing gold. + */ +class Solution { + int max = 0; + int[] dx = new int[]{0, 0, -1, 1}; + int[] dy = new int[]{1, -1, 0, 0}; + public int getMaximumGold(int[][] grid) { + if (grid == null || grid.length == 0) return 0; + + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] != 0) { + max = Math.max(max, dfs(grid, i, j)); + } + } + } + + return max; + } + + private int dfs(int[][] grid, int i, int j) { + if (check(grid, i, j)) return 0; + int curr = grid[i][j], localMax = 0; + + grid[i][j] = 0; // mark visited + + for (int k = 0; k < dx.length; k++) { + localMax = Math.max(localMax, dfs(grid, i + dx[k], j + dy[k])); + } + + grid[i][j] = curr; // backtrack + + return localMax + curr; + } + + private boolean check(int[][] grid, int i, int j) { + return i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == 0; + } +} +``` \ No newline at end of file diff --git a/Java/122. Best Time to Buy and Sell Stock II.java b/Java/122. Best Time to Buy and Sell Stock II.java new file mode 100755 index 0000000..7b4cac9 --- /dev/null +++ b/Java/122. Best Time to Buy and Sell Stock II.java @@ -0,0 +1,222 @@ +E +tags: Array, Greedy, DP, Sequence DP, Status DP +time: O(n) +space: O(1) greedy, O(n) dp + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### 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 (i.e., buy one and sell one share of the stock multiple times). + +Note: You may not engage in multiple transactions at the same time (i.e., you must sell the stock before you buy again). + +Example 1: + +Input: [7,1,5,3,6,4] +Output: 7 +Explanation: Buy on day 2 (price = 1) and sell on day 3 (price = 5), profit = 5-1 = 4. + Then buy on day 4 (price = 3) and sell on day 5 (price = 6), profit = 6-3 = 3. +Example 2: + +Input: [1,2,3,4,5] +Output: 4 +Explanation: Buy on day 1 (price = 1) and sell on day 5 (price = 5), profit = 5-1 = 4. + Note that you cannot buy on day 1, buy on day 2 and sell them later, as you are + engaging multiple transactions at the same time. You must sell before buying again. +Example 3: + +Input: [7,6,4,3,1] +Output: 0 +Explanation: In this case, no transaction is done, i.e. max profit = 0. + +*/ + + +/* +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. + +// 1. 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; + } +} + +// 2. find peek and use peek +// O(n) time, O(1) space +class Solution { + public int maxProfit(int[] prices) { + if (prices == null || prices.length <= 1) { + return 0; + } + int curr = 0; + int profit = 0; + int peek = 1; + while(curr < prices.length) { + while (peek < prices.length && prices[peek - 1] <= prices[peek]) { + peek++; + } + profit += prices[peek - 1] - prices[curr]; + curr = peek; + peek = curr + 1; + } + return profit; + } +} + +//3. 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][0] = - prices[0]; + + for (int i = 2; i <= n; i++) { + dp[i][0] = Math.max(dp[i-1][0], dp[i-1][1] - prices[i-1]); // be careful, now i is 1-based + dp[i][1] = Math.max(dp[i-1][1], dp[i-1][0] + prices[i-1]); + } + return dp[n][1]; + } +} + +// 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]; + } +}; + + +/* +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/124. Binary Tree Maximum Path Sum.java b/Java/124. Binary Tree Maximum Path Sum.java new file mode 100755 index 0000000..4d79857 --- /dev/null +++ b/Java/124. Binary Tree Maximum Path Sum.java @@ -0,0 +1,159 @@ +H +tags: Tree, DFS, DP, Tree DP +time: O(n) +space: O(logn) + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + +``` +/* +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; + * } + * } + */ +// DP, DFS, 99.73% +public class Solution { + int global = Integer.MIN_VALUE; + + public int maxPathSum(TreeNode root) { + dfs(root); + return global; + + // int pathOverRootMax = dfs(root); Math.max(pathOverRootMax, global); // NO NEED, because single path is covered in dfs, when left/right == 0. + } + + private int dfs(TreeNode node) { + if (node == null) return 0; + int left = Math.max(0, dfs(node.left)); // left single path w/0 node, OR: no left path at all + int right = Math.max(0, dfs(node.right)); // right single path w/0 node, OR: no right path at all + global = Math.max(global, left + right + node.val); // compare combo max with global max: 3 cases. + return Math.max(left, right) + node.val; // always return the max single continuous path + } +} + +// DFS +// Slight different writing than above, but similar to the `PathSum` approach below +public class Solution { + int global = Integer.MIN_VALUE; + + public int maxPathSum(TreeNode root) { + dfs(root); + return global; + } + + private int dfs(TreeNode node) { + if (node == null) return 0; + int left = dfs(node.left); // left single path max + int right = dfs(node.right); // right single path max + global = Math.max(global, left + right + node.val); + + // always return the max path over curr node; OR: skip completely if negative + // if any path < 0, it should be ignore instead adding to parent combo path + return Math.max(0, Math.max(left, right) + node.val); + } +} + +// 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); + } + +} + + +``` \ No newline at end of file diff --git a/Java/1249. Minimum Remove to Make Valid Parentheses.java b/Java/1249. Minimum Remove to Make Valid Parentheses.java new file mode 100755 index 0000000..e7c153c --- /dev/null +++ b/Java/1249. Minimum Remove to Make Valid Parentheses.java @@ -0,0 +1,83 @@ +M +tags: String, Stack +time: O(n) +space: O(n) + +#### Stack +- Goal: remove extra '(' or ')' so it is valid. +- Forward thinking: use stack to track '(' and ')', then keep appending partial string to output +- Backward thinking: use stack to filter out false indexes, and remove them in the end + + +``` +/* +Given a string s of '(' , ')' and lowercase English characters. + +Your task is to remove the minimum number of parentheses ( '(' or ')', in any positions ) so that the resulting parentheses string is valid and return any valid string. + +Formally, a parentheses string is valid if and only if: + +It is the empty string, contains only lowercase characters, or +It can be written as AB (A concatenated with B), where A and B are valid strings, or +It can be written as (A), where A is a valid string. + + +Example 1: + +Input: s = "lee(t(c)o)de)" +Output: "lee(t(c)o)de" +Explanation: "lee(t(co)de)" , "lee(t(c)ode)" would also be accepted. +Example 2: + +Input: s = "a)b(c)d" +Output: "ab(c)d" +Example 3: + +Input: s = "))((" +Output: "" +Explanation: An empty string is also valid. +Example 4: + +Input: s = "(a(b(c)d)" +Output: "a(b(c)d)" + + +Constraints: + +1 <= s.length <= 10^5 +s[i] is one of '(' , ')' and lowercase English letters. + +*/ + + +/* +Goal: remove extra ( or ) so it is valid. +Forward thinking: use stack to track ( and ), then keep appending partial string to final result +Backward thinking: use stack to filter out false indexes, and remove them in the end +*/ +class Solution { + public String minRemoveToMakeValid(String s) { + + Set set = new HashSet<>(); + Stack stack = new Stack<>(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (c == '(') stack.push(i); + if (c == ')') { + if (!stack.isEmpty()) stack.pop(); + else set.add(i); // given ')' but missing '(', record ')' as false index + } + } + // remaining false indexes of '(' + while (!stack.isEmpty()) set.add(stack.pop()); + + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s.length(); i++) { + if (!set.contains(i)) sb.append(s.charAt(i)); + } + + return sb.toString(); + } +} + +``` \ No newline at end of file diff --git a/Java/125. Valid Palindrome.java b/Java/125. Valid Palindrome.java new file mode 100755 index 0000000..44329f4 --- /dev/null +++ b/Java/125. Valid Palindrome.java @@ -0,0 +1,128 @@ +E +tags: Two Pointers, String + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Two Pointers +- Time O(n), Space O(1). +- 普通方法, 两边check, 速度相比较regular expression更快. leetcode 4ms. +- Use helper functions. + +#### 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. + +Note: For the purpose of this problem, we define empty string as valid palindrome. + +Example 1: + +Input: "A man, a plan, a canal: Panama" +Output: true +Example 2: + +Input: "race a car" +Output: false + +*/ + +/* +Two pointer: keep start++, end-- until valid char found. +Use helper function. +Runtime O(n), Space O(1) +*/ + +public class Solution { + public boolean isPalindrome(String s) { + if (s == null || s.length() == 0) { + return true; + } + int length = s.length(); + int start = 0, end = length - 1; + while (start < end) { + while (start < length && !isValid(s.charAt(start))) start++; + while (end >= 0 && !isValid(s.charAt(end))) end--; + if (start < end && !compare(s.charAt(start), s.charAt(end))) return false; + start++; + end--; + } + return true; + } + + private boolean compare(char m, char n) { + if (isValid(m) & isValid(n)) return Character.toLowerCase(m) == Character.toLowerCase(n); + return false; + } + + private boolean isValid(char c) { + if (c >= '0' && c <= '9') return true; + if (c >= 'a' && c <= 'z') return true; + if (c >= 'A' && c <= 'Z') return true; + return false; + } +} + + +/* +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; + } + 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; + } +} + +/* +Same solution as above: just manual filter string +*/ + +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; + } +} + + +``` \ No newline at end of file diff --git a/Java/1267. Count Servers that Communicate.java b/Java/1267. Count Servers that Communicate.java new file mode 100755 index 0000000..c02d8c1 --- /dev/null +++ b/Java/1267. Count Servers that Communicate.java @@ -0,0 +1,117 @@ +M +tags: Array, Graph +time: O(mn) +space: O(m + n) + +#### two axis array, cross-reference +- analyze problem, and realize we want to eliminate `isolated servers` +- count row[], count col[] +- cross-reference row[] and col[]: `row[i]==1 & col[j]==1` indicates a isolated server + +### DFS brutle +- Unfortunately, this problems checks unconnected items, so dfs needs to brutlely check entire row or column +- Only add if `vertical + horizontal count` > 1 +- time: O(mn) * O(m + n) + +``` +/* +You are given a map of a server center, represented as a m * n integer matrix grid, where 1 means that on that cell there is a server and 0 means that it is no server. Two servers are said to communicate if they are on the same row or on the same column. + +Return the number of servers that communicate with any other server. + + + +Example 1: + + + +Input: grid = [[1,0],[0,1]] +Output: 0 +Explanation: No servers can communicate with others. +Example 2: + + + +Input: grid = [[1,0],[1,1]] +Output: 3 +Explanation: All three servers can communicate with at least one other server. +Example 3: + + + +Input: grid = [[1,1,0,0],[0,0,1,0],[0,0,1,0],[0,0,0,1]] +Output: 4 +Explanation: The two servers in the first row can communicate with each other. The two servers in the third column can communicate with each other. The server at right bottom corner can't communicate with any other server. + + +Constraints: + +m == grid.length +n == grid[i].length +1 <= m <= 250 +1 <= n <= 250 +grid[i][j] == 0 or 1 +*/ + +class Solution { + public int countServers(int[][] grid) { + + int total = 0; + int m = grid.length, n = grid[0].length; + int[] row = new int[m], col = new int[n]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + total++; + row[i]++; + col[j]++; + } + } + } + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1 && row[i] == 1 && col[j] == 1) { // both row and col has count 1; isolated server + total--; + } + } + } + + return total; + } +} + +// DFS: exhaust vertical, and horizontal completely +// time: O(mn) * O(m + n) +class Solution { + public int countServers(int[][] grid) { + boolean visited[][] = new boolean[grid.length + 1][grid[0].length + 1]; + int total = 0; + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == 1) { + int count = dfs(grid, i, j, visited); + if (count > 1) total += count; + } + visited[i][j] = true; + } + } + return total; + } + + private int dfs(int[][] grid, int i, int j, boolean[][] visited) { + if (visited[i][j] || grid[i][j] == 0) return 0; // skip 0 or visited[i][j] + + visited[i][j] = true; + int count = 1; + for (int x = 0; x < grid.length; x++) { // dfs on row + count += dfs(grid, x, j, visited); + } + for (int y = 0; y < grid[0].length; y++) { // dfs on col + count += dfs(grid, i, y, visited); + } + return count; + } + +} +``` \ No newline at end of file diff --git a/Java/13. Roman to Integer.java b/Java/13. Roman to Integer.java new file mode 100755 index 0000000..0f64311 --- /dev/null +++ b/Java/13. Roman to Integer.java @@ -0,0 +1,155 @@ +E +tags: Math, String +time: O(n) +space: O(1) + +#### 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 + +#### use map to store combinations + + +``` +/* +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 +*/ +// Use math to remove double-counted value +class Solution { + public int romanToInt(String s) { + if (s == null || s.length() == 0) return 0; + int sum = getPlainSum(s); + + 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; + } + + private int getPlainSum(String s) { + 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; + } + } + return sum; + } +} + + +// works, but kinda slow because we are building additona O(n) structure +class Solution { + public int romanToInt(String s) { + if (s == null || s.length() == 0) return 0; + int sum = 0, i = 0; + Map map = buildMap(); + Map specialMap = buildSpecialMap(); + for (; i < s.length() - 1; i++) { + char c = s.charAt(i); + if (c == 'I' || c == 'X' || c == 'C') { + String pair = s.substring(i, i+2); + if(specialMap.containsKey(pair)) { + sum += specialMap.get(pair); + i++; + continue; + } + } + sum += map.get(c); + } + if (i < s.length()) { + sum += map.get(s.charAt(i)); + } + return sum; + } + + private Map buildSpecialMap() { + Map map = new HashMap<>(); + map.put("IV", 4); + map.put("IX", 9); + map.put("XL", 40); + map.put("XC", 90); + map.put("CD", 400); + map.put("CM", 900); + return map; + } + + private Map buildMap() { + Map map = new HashMap<>(); + map.put('I', 1); + map.put('V', 5); + map.put('X', 10); + map.put('L', 50); + map.put('C', 100); + map.put('D', 500); + map.put('M', 1000); + return map; + } +} + + + +``` diff --git a/Java/1306. Jump Game III.java b/Java/1306. Jump Game III.java new file mode 100755 index 0000000..a555b69 --- /dev/null +++ b/Java/1306. Jump Game III.java @@ -0,0 +1,105 @@ +M +tags: BFS, Graph +time: O(n) +space: O(n) + +### Method1: BFS +- Find possibility to reach certain point, we can BFS: faster to find shortest candidate +- use queue to hold left, right candidates +- use set to record visited + +### Method2: DFS +- attemp all nodes, use set to record visited. +- time: O(n) +- space: O(n) + +``` +/* + +Given an array of non-negative integers arr, you are initially positioned at start index of the array. When you are at index i, you can jump to i + arr[i] or i - arr[i], check if you can reach to any index with value 0. + +Notice that you can not jump outside of the array at any time. + + + +Example 1: + +Input: arr = [4,2,3,0,3,1,2], start = 5 +Output: true +Explanation: +All possible ways to reach at index 3 with value 0 are: +index 5 -> index 4 -> index 1 -> index 3 +index 5 -> index 6 -> index 4 -> index 1 -> index 3 +Example 2: + +Input: arr = [4,2,3,0,3,1,2], start = 0 +Output: true +Explanation: +One possible way to reach at index 3 with value 0 is: +index 0 -> index 4 -> index 1 -> index 3 +Example 3: + +Input: arr = [3,0,2,1,2], start = 2 +Output: false +Explanation: There is no way to reach at index 1 with value 0. + + +Constraints: + +1 <= arr.length <= 5 * 10^4 +0 <= arr[i] < arr.length +0 <= start < arr.length + + +*/ + + +/* +Method1: BFS:use queue to hold left, right candidates +*/ +class Solution { + + public boolean canReach(int[] arr, int start) { + + Set visited = new HashSet<>(); + Queue queue = new LinkedList<>(); + add(queue, arr, start); + + while (!queue.isEmpty()) { + int size = queue.size(); + + while (size-- > 0) { + int index = queue.poll(); + if (!visited.add(index)) continue; // visited before + if (arr[index] == 0) return true; + add(queue, arr, index); + } + } + return false; + } + + private void add(Queue queue, int[] arr, int start) { + int left = start - arr[start], right = start + arr[start]; + if (left >= 0) queue.offer(left); + if (right < arr.length) queue.offer(right); + } +} + +// Method2: DFS. +class Solution { + public boolean canReach(int[] arr, int start) { + return dfs(arr, new HashSet<>(), start); + } + + private boolean dfs(int[] arr, Set visited, int index) { + if (arr[index] == 0) return true; + if (!visited.add(index)) return false; + if (visited.size() == arr.length) return false; + + int left = index - arr[index], right = index + arr[index]; + + return (left >= 0 && dfs(arr, visited, left)) + || (right < arr.length && dfs(arr, visited, right)); + } +} +``` \ No newline at end of file diff --git a/Java/131. Palindrome Partitioning.java b/Java/131. Palindrome Partitioning.java new file mode 100755 index 0000000..e808a43 --- /dev/null +++ b/Java/131. Palindrome Partitioning.java @@ -0,0 +1,143 @@ +M +tags: DFS, Backtracking +time: O(2^n) +space: O(n^2) + +给个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. + +For example, given s = "aab", +Return + +[ + ["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; + int n; + public List> partition(String s) { + List> rst = new ArrayList<>(); + if (s == null || s.length() == 0) return rst; + n = s.length(); + calcPalin2(s); + dfs(rst, new ArrayList<>(), s, 0); + return rst; + } + + private void dfs(List> rst, List list, String s, int index) { + if (index == n) { + rst.add(new ArrayList<>(list)); + return; + } + for (int i = index + 1; i <= n; i++) { + if (isPalin[index][i - 1]) { // 也需要查看自身是不是 palindrome: s.charAt(x). isPalin[i][j] 是 inclusive的 + list.add(s.substring(index, i)); + dfs(rst, list, s, i); + list.remove(list.size() - 1); + } + } + } + // Optiona, faster. Kinda DP, isPalin[i][j] shows palindrome status for s[i,j] inclusivly + private void calcPalin(String s) { + char[] arr = s.toCharArray(); + 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; + } + + // 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; + } + } + } + + // Option2: a bit slower, 95%. + private void calcPalin2(String s) { + char[] arr = s.toCharArray(); + isPalin = new boolean[n][n]; + + // 1) init single digit, len=1 + for (int i = 0; i < n; i++) { + isPalin[i][i] = true; + } + + // 2) init len=2 + for (int i = 0; i < n - 1; i++) { + isPalin[i][i + 1] = arr[i] == arr[i + 1]; + } + + // 3) finish len=3~n + for (int i = n-1; i >= 0; i--) { + for (int j = i + 2; j < n; j++) { + isPalin[i][j] = arr[i] == arr[j] && isPalin[i+1][j-1]; + } + } + } + +/* + // Manual 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; + } +*/ +} + + +``` \ No newline at end of file diff --git a/Java/133. Clone Graph.java b/Java/133. Clone Graph.java new file mode 100755 index 0000000..c5edd92 --- /dev/null +++ b/Java/133. Clone Graph.java @@ -0,0 +1,154 @@ +M +tags: DFS, BFS, Graph +time: O(n) +space: O(n) + +给一个graph node, 每个node有list of neighbors. 复制整个graph, return new head node. + +实现起来就好像在crawl urls. + +#### 思想 +- Use HashMap to mark cloned nodes: `map` + - 1) make new curr node; + - 2) clone all neibhors and add them +- Use the map to avoid visited nodes +- time: O(n). visit all nodes +- space: O(n). Technically only travels n levels/stacks to circle all nodes (undirected & connected) + +#### 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. + +``` +/* +Given a reference of a node in a connected undirected graph, return a deep copy (clone) of the graph. +Each node in the graph contains a val (int) and a list (List[Node]) of its neighbors. + + +Input: +{"$id":"1","neighbors":[{"$id":"2","neighbors":[{"$ref":"1"},{"$id":"3","neighbors":[{"$ref":"2"},{"$id":"4","neighbors":[{"$ref":"3"},{"$ref":"1"}],"val":4}],"val":3}],"val":2},{"$ref":"4"}],"val":1} + +Explanation: +Node 1's value is 1, and it has two neighbors: Node 2 and 4. +Node 2's value is 2, and it has two neighbors: Node 1 and 3. +Node 3's value is 3, and it has two neighbors: Node 2 and 4. +Node 4's value is 4, and it has two neighbors: Node 1 and 3. + + +Note: + +The number of nodes will be between 1 and 100. +The undirected graph is a simple graph, which means no repeated edges and no self-loops in the graph. +Since the graph is undirected, if node p has node q as neighbor, then node q must have node p as neighbor too. +You must return the copy of the given node as a reference to the cloned graph. + + +*/ +/* +// Definition for a Node. +class Node { + public int val; + public List neighbors; + + public Node() {} + + public Node(int _val,List _neighbors) { + val = _val; + neighbors = _neighbors; + } +}; +*/ + +/* +DFS +- Mark visited in map +- Add neibhors via dfs +- return node +*/ +public class Solution { + // old -> node node map + Map map = new HashMap<>(); + public Node cloneGraph(Node node) { + if (node == null) return node; + if (map.containsKey(node)) return map.get(node); + + Node newNode = new Node(node.val, new ArrayList<>()); + map.put(node, newNode); + for (Node 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 Node cloneGraph(Node node) { + if (node == null) return null; + + map.put(node, new Node(node.val, new ArrayList<>())); + dfs(node); + return map.get(node); + } + + public void dfs(Node node) { + if (node == null) return; + for (Node neighbor: node.neighbors) { + if (!map.containsKey(neighbor)) { + map.put(neighbor, new Node(neighbor.val, new ArrayList<>())); + dfs(neighbor); + } + map.get(node).neighbors.add(map.get(neighbor)); + } + } +} + +/* +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 +*/ +public class Solution { + public Node cloneGraph(Node node) { + if (node == null) return null; + + HashMap map = new HashMap<>(); + map.put(node, new Node(node.val, new ArrayList<>())); // copy root + + Queue queue = new LinkedList<>(); + queue.offer(node); + + while(!queue.isEmpty()) { + Node curr = queue.poll(); + for (Node neighbor: curr.neighbors) { + if (!map.containsKey(neighbor)) { // Init neighbors + queue.offer(neighbor); + map.put(neighbor, new Node(neighbor.val, new ArrayList<>())); + } + map.get(curr).neighbors.add(map.get(neighbor)); // link neighbor + } + } + + return map.get(node); + } +} +``` diff --git a/Java/134. Gas Station.java b/Java/134. Gas Station.java new file mode 100755 index 0000000..62f4a56 --- /dev/null +++ b/Java/134. Gas Station.java @@ -0,0 +1,101 @@ +M +tags: Greedy +time: O(n) +space: O(1) + +给一串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]. + +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 in the clockwise direction, otherwise return -1. + +Note: + +If there exists a solution, it is guaranteed to be unique. +Both input arrays are non-empty and have the same length. +Each element in the input arrays is a non-negative integer. +Example 1: + +Input: +gas = [1,2,3,4,5] +cost = [3,4,5,1,2] + +Output: 3 + +Explanation: +Start at station 3 (index 3) and fill up with 4 unit of gas. Your tank = 0 + 4 = 4 +Travel to station 4. Your tank = 4 - 1 + 5 = 8 +Travel to station 0. Your tank = 8 - 2 + 1 = 7 +Travel to station 1. Your tank = 7 - 3 + 2 = 6 +Travel to station 2. Your tank = 6 - 4 + 3 = 5 +Travel to station 3. The cost is 5. Your gas is just enough to travel back to station 3. +Therefore, return 3 as the starting index. +Example 2: + +Input: +gas = [2,3,4] +cost = [3,4,3] + +Output: -1 + +Explanation: +You can't start at station 0 or 1, as there is not enough gas to travel to the next station. +Let's start at station 2 and fill up with 4 unit of gas. Your tank = 0 + 4 = 4 +Travel to station 0. Your tank = 4 - 3 + 2 = 3 +Travel to station 1. Your tank = 3 - 3 + 3 = 3 +You cannot travel back to station 2, as it requires 4 unit of gas but you only have 3. +Therefore, you can't travel around the circuit once no matter where you start. + +*/ + +/** + +Thoughts: Loop through the gas station, and track the possible starting index. +Start from i = 0, use a second pointer move to track how far we are travelling +calculate: remain += gas[i] - cost[i]. +if remain < 0, fail. This mean: with all remain gas before index i + gas[i], we won't make it to index i+1 +Therefore, reset startIndex = i + 1: moving on to next possible starting point. + +'total':simply indicates if we can make it a circle + */ +public class Solution { + public int canCompleteCircuit(int[] gas, int[] cost) { + if (gas == null || cost == null || gas.length == 0 || cost.length == 0) { + return -1; + } + int startIndex = 0, remain = 0, total = 0; + for (int i = 0; i < gas.length; i++) { + total += gas[i] - cost[i]; + remain += gas[i] - cost[i]; + if (remain < 0) { + remain = 0; + startIndex = i + 1; // restart + } + } + return total < 0 ? -1 : startIndex; + } +} + +``` \ No newline at end of file diff --git a/Java/136. Single Number.java b/Java/136. Single Number.java new file mode 100755 index 0000000..c7dbdcc --- /dev/null +++ b/Java/136. Single Number.java @@ -0,0 +1,47 @@ +E +tags: Hash Table, Bit Manipulation + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + +``` +/* +Given a non-empty array of integers, every element appears twice except for one. 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,1] +Output: 1 +Example 2: + +Input: [4,1,2,1,2] +Output: 4 + + +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. +*/ + +class Solution { + public int singleNumber(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int result = 0; + for (int i : nums) { + result = result ^ i; + } + return result; + } +} + + + +``` \ No newline at end of file diff --git a/Java/138. Copy List with Random Pointer.java b/Java/138. Copy List with Random Pointer.java new file mode 100755 index 0000000..9ebd789 --- /dev/null +++ b/Java/138. Copy List with Random Pointer.java @@ -0,0 +1,140 @@ +M +tags: Hash Table, Linked List +time: O(n) +space: O(n) + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List, time, space: O(n) +- Basic Implementation of copy linked list: + - use a iterator node to iterate over the list: 遍历head.next .... null. + - use a dummy node to hold reference to the iterator node. +- Map: 1. avoid creating same node; 2. return the new node if existing + - 每一步都check map里面有没有head. 没有? 加上 + - 每一步都check map里面有没有head.random. 没有? 加上 +- Note, there is a way to skip the extra map O(n): https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43491/A-solution-with-constant-space-complexity-O(1)-and-linear-time-complexity-O(N) + - However, creating a deep clone of the list is already O(n) extra space, so it is NOT effectively O(1) w/o map + - It may be beneficial, if we can not hold all nodes in memory, then the approach w/o map is more applicable. + +``` +/* +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. + + +Input: +{ + "$id":"1", + "next":{ + "$id":"2", + "next":null, + "random":{"$ref":"2"}, + "val":2 + }, + "random":{"$ref":"2"}, + "val":1 +} + +Explanation: +Node 1's value is 1, both of its next and random pointer points to Node 2. +Node 2's value is 2, its next pointer points to null and its random pointer points to itself. + +*/ + +/** + * Definition for singly-linked list with a random pointer. + * class RandomListNode { + * int label; + * RandomListNode next, random; + * RandomListNode(int x) { this.label = x; } + * }; + */ + +/* + 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 +*/ + +class Solution { + public Node copyRandomList(Node head) { + Node node = new Node(); //creat node, used to iterate over all nodes + Node dummy = node; + HashMap map = new HashMap<>(); // + + while(head != null) { + // replicate head and head.random + replicateNode(map, head); + replicateNode(map, head.random); + + node.next = map.get(head); + node.next.random = map.get(head.random); + + node = node.next; + head = head.next; + } + return dummy.next; + } + + private void replicateNode(HashMap map, Node original) { + if (original == null) return; + if (!map.containsKey(original)) { + Node newNode = new Node(); + newNode.val = original.val; + map.put(original, newNode); + } + } +} + + +/* +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 { + public Node copyRandomList(Node head) { + HashMap map = new HashMap<>(); + Node dummy = new Node(); + Node pre = dummy; + Node newNode; + while (head != null) { + //Add new node + if (map.containsKey(head)) { + newNode = map.get(head); + } else { + newNode = new Node(); + newNode.val = head.val; + 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 Node(head.random.val); + map.put(head.random, newNode.random); + } + } + //append and shift + pre.next = newNode; + pre = newNode; + head = head.next; + } + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Java/139. Word Break.java b/Java/139. Word Break.java new file mode 100755 index 0000000..1a965a6 --- /dev/null +++ b/Java/139. Word Break.java @@ -0,0 +1,184 @@ +M +tags: DP, Sequence DP, Hash Table +time: O(n^2) +space: O(n) + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- Bottom-up, test simply case. Sequence DP. +- true/false problem, think about dp + - 子问题: 前i个字母, 是否可以有valid break + - check: 1) dp[j] && 2) `if substring(j, i) valid`, for all j = [0 ~ i] + - dp = new boolean[n + 1]; dp[0] = true; + - test: `dp[i] |= dp[j] == true && word[j, n] in dict`. + - Need iterate over i = [0 ~ n], also j = [0, i] + - When there is a way to make dp[i] == true, then break the [j ~ i] loop, move on to test dp[i++] +- Use set dict: `dict.contains()` +- Improvement: O(n) to figure out max length, so we can skip some substring[j~i] dict.contains() +- overall O(n^2) time since the double for loop + +#### DFS +- Top-Down, break into small problems: Check front subString, and put the rest substring into dfs to test +- Memoization: for tested failed substring, record and do NOT test them again. +- Same Improvement as in DP: use max/min length of dict words as boundary + +``` +/* +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: + +Input: s = "catsandog", wordDict = ["cats", "dog", "sand", "and", "cat"] +Output: false +*/ + +/* +Method1: DP +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) { + Set words = new HashSet<>(dict); + + int n = s.length(); // init dp + boolean[] dp = new boolean[n + 1]; + dp[0] = true; // base case, no letter, assume true for dp[1] to work + + // 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]; + } +} + + +// Method1 Improvement: Same DP as above, improve by building boundary of min/max +class Solution { + int min = 0, max = 0; + + public boolean wordBreak(String s, List dictWords) { + int n = s.length(); + Set dict = new HashSet<>(dictWords); + buildBoundary(dict); + + // init dp + boolean[] dp = new boolean[n + 1]; + dp[0] = true; // base case, no letter, assume true for dp[1] to work + + for (int i = 1; i <= n; i++) {// process dp[1] ~ dp[n] + for (int j = 0; j < i; j++) { + dp[i] |= dp[j] && validate(dict, s, j, i); + if (dp[i]) break; + } + } + return dp[n]; + } + + private boolean validate(Set dict, String s, int j, int i) { + if (i - j > max || i - j < min) return false; + return dict.contains(s.substring(j, i)); + } + + private void buildBoundary(Set dict) { + for (String s : dict) { + int len = s.length(); + min = Math.min(min, len); + max = Math.max(max, len); + } + } +} + + +//Method2: Raw DFS + memoization, but still a bit slow +class Solution { + public boolean wordBreak(String s, List wordDict) { + Set dict = new HashSet<>(wordDict); + return dfs(dict, new HashSet<>(), s); + } + + private boolean dfs(Set dict, Set set, String s) { + if (dict.contains(s)) return true; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + sb.append(c); + if (dict.contains(sb.toString())) { + String sub = s.substring(i + 1); + if (set.contains(sub)) continue; + if (dfs(dict, set, sub)) return true; + set.add(sub); + } + } + return false; + } +} + +//Method2 Improvement: DFS, Memoization + max/min boundary improvement +class Solution { + int min = 0, max = 0; + public boolean wordBreak(String s, List wordDict) { + Set dict = new HashSet<>(wordDict); + buildBoundary(dict); + return dfs(dict, new HashSet<>(), s); + } + + private boolean dfs(Set dict, Set set, String s) { + if (dict.contains(s)) return true; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + sb.append(c); + if (validate(dict, sb.toString())) { + String sub = s.substring(i + 1); + if (set.contains(sub)) continue; + if (dfs(dict, set, sub)) return true; + set.add(sub); + } + } + return false; + } + + private void buildBoundary(Set dict) { + for (String s : dict) { + int len = s.length(); + min = Math.min(min, len); + max = Math.max(max, len); + } + } + + private boolean validate(Set dict, String s) { + int n = s.length(); + if (n > max || n < min) return false; + return dict.contains(s); + } +} + +``` diff --git a/Java/14. Longest Common Prefix.java b/Java/14. Longest Common Prefix.java new file mode 100755 index 0000000..8edc824 --- /dev/null +++ b/Java/14. Longest Common Prefix.java @@ -0,0 +1,130 @@ +E +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) + +``` + +/** +Leetcode +Write a function to find the longest common prefix string amongst an array of strings. + +If there is no common prefix, return an empty string "". + +Example 1: + +Input: ["flower","flow","flight"] +Output: "fl" +Example 2: + +Input: ["dog","racecar","car"] +Output: "" +Explanation: There is no common prefix among the input strings. +Note: + +All given inputs are in lowercase letters a-z. +*/ + +/* +lintcode +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 + +*/ + +/* +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. + +Note: +Arrays: use strs.length +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 { + 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/Java/140. Word Break II.java b/Java/140. Word Break II.java new file mode 100755 index 0000000..9eee33a --- /dev/null +++ b/Java/140. Word Break II.java @@ -0,0 +1,312 @@ +H +tags: DP, Backtracking, DFS, Memoization, Hash Table +time: O(n!) +space: O(n!) + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 non-empty string s and a dictionary wordDict containing a list of non-empty words, +add spaces in s to construct a sentence where each word is a valid dictionary word. + +"Return all such possible sentences." + +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 = "catsanddog" +wordDict = ["cat", "cats", "and", "sand", "dog"] +Output: +[ + "cats and dog", + "cat sand dog" +] +Example 2: + +Input: +s = "pineapplepenapple" +wordDict = ["apple", "pen", "applepen", "pine", "pineapple"] +Output: +[ + "pine apple pen apple", + "pineapple pen apple", + "pine applepen apple" +] +Explanation: Note that you are allowed to reuse a dictionary word. +Example 3: + +Input: +s = "catsandog" +wordDict = ["cats", "dog", "sand", "and", "cat"] +Output: +[] + +*/ +// Simplier solution, memoization +class Solution { + Map> memo; + public List wordBreak(String s, List dict) { + List rst = new ArrayList<>(); + 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 + } +} + +// Method2: DFS like in WordBreakI, building individual candidate in list till substring exhaust +// Test front sub, and dfs on end substring. Use list to track candidates. +// End state: when i == end of string && test it with map. +// apply the same improvement as in wordBreakI: existing failure check, min/max boundary. +class Solution { + int min = 0, max = 0; + public ListwordBreak(String s, List wordDict) { + List rst = new ArrayList<>(); + Set dict = new HashSet<>(wordDict); + buildBoundary(dict); + + dfs(rst, new ArrayList<>(), dict, new HashSet<>(), s); + return rst; + } + // core + private void dfs(List rst, List list, Set dict, Set failureSet, String s) { + int rstSize = 0; + if (validate(dict, s)) { + list.add(s); + rst.add(output(list)); + list.remove(list.size() - 1); + } + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < s.length(); i++) { + sb.append(s.charAt(i)); + String front = sb.toString(); + if (validate(dict, front)) { + String end = s.substring(i + 1); + if (failureSet.contains(end)) continue; + + list.add(front); + dfs(rst, list, dict, failureSet, end); + list.remove(list.size() - 1); + } + } + + if (rst.size() == rstSize) failureSet.add(s); + } + + // improvement fns: test boundary & dict contains + private void buildBoundary(Set dict) { + for (String s : dict) { + int len = s.length(); + min = Math.min(min, len); + max = Math.max(max, len); + } + } + + private boolean validate(Set dict, String s) { + int n = s.length(); + if (n > max || n < min) return false; + return dict.contains(s); + } + + // Helpers: + private String output(List list) { + StringBuffer sb = new StringBuffer(); + for (String s : list) sb.append(s).append(" "); + return sb.toString().trim(); + } +} + + + + +/* +Works but supper slow. +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; + } +} + + +``` diff --git a/Java/141. Linked List Cycle.java b/Java/141. Linked List Cycle.java new file mode 100755 index 0000000..47eea7b --- /dev/null +++ b/Java/141. Linked List Cycle.java @@ -0,0 +1,75 @@ +E +tags: Linked List, Two Pointers, Slow Fast Pointer, Cycle Detection +time: O(n) +space: O(1) + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + +``` +/* +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; + * } + * } + */ +/* +// method 1: 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, fast = head.next; // start at different spot + while (slow != fast) { + if (fast == null || fast.next == null) return false; + slow = slow.next; // move 1 step + fast = fast.next.next; // move 2 steps + } // end while loop when slow==fast + return true; + } +} + +// Method2: hash table +public class Solution { + public boolean hasCycle(ListNode head) { + if (head == null || head.next == null) return false; + Set set = new HashSet<>(); + + while (head != null) { + if (!set.add(head)) return true; + head = head.next; + } + return false; + } +} + +``` \ No newline at end of file diff --git a/Java/142. Linked List Cycle II.java b/Java/142. Linked List Cycle II.java new file mode 100755 index 0000000..09a15fe --- /dev/null +++ b/Java/142. Linked List Cycle II.java @@ -0,0 +1,70 @@ +M +tags: Two Pointers, Slow Fast Pointer, Cycle Detection, Linked List +time: O(n) +space: O(1) + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + +``` +/* +Given a linked list, return the node where the cycle begins. If there is no cycle, return null. + +To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list. + +Note: Do not modify the linked list. + + + +Example 1: + +Input: head = [3,2,0,-4], pos = 1 +Output: tail connects to node index 1 +Explanation: There is a cycle in the linked list, where tail connects to the second node. + + +Example 2: + +Input: head = [1,2], pos = 0 +Output: tail connects to node index 0 +Explanation: There is a cycle in the linked list, where tail connects to the first node. + + +Example 3: + +Input: head = [1], pos = -1 +Output: no cycle +Explanation: There is no cycle in the linked list. + + + + +Follow-up: +Can you solve it without using extra space? +*/ + +/* +- find slow/fast to detect the meeting point +- use a new `finder` pointer traverse from head, also move slow; utill it meets slow +*/ +public class Solution { + public ListNode detectCycle(ListNode head) { + ListNode slow = head, fast = head; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + if (slow == fast) return findBegin(head, slow); + } + return null; + } + + private ListNode findBegin(ListNode head, ListNode slow) { + while (head != slow) { + head = head.next; + slow = slow.next; + } + return head; + } +} +``` \ No newline at end of file diff --git a/Java/144. Binary Tree Preorder Traversal.java b/Java/144. Binary Tree Preorder Traversal.java new file mode 100755 index 0000000..a7db4c1 --- /dev/null +++ b/Java/144. Binary Tree Preorder Traversal.java @@ -0,0 +1,101 @@ +M +tags: Stack, Tree, DFS, BFS +time: O(n) +space: O(n) + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### 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. + +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/ + +*/ + +/* +DFS Option1: 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; + } +} + +// DFS Option2: add root, then left, then right +public class Solution { + public List preorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) return rst; + dfs(rst, root); + return rst; + } + + public void dfs(Listrst, TreeNode node){ + if (node != null) { + rst.add(node.val); + dfs(rst, node.left); + dfs(rst, node.right); + } + } +} + +/* +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) { + if (root == null) return new ArrayList<>(); + List rst = new ArrayList<>(); + 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; + } +} + + + +``` \ No newline at end of file diff --git a/Java/145. Binary Tree Postorder Traversal.java b/Java/145. Binary Tree Postorder Traversal.java new file mode 100755 index 0000000..847474e --- /dev/null +++ b/Java/145. Binary Tree Postorder Traversal.java @@ -0,0 +1,134 @@ +M +tags: Stack, Two Stacks, Tree +time: O(n) +space: O(n) + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Method1: DFS, Recursive +- trivial, 先加left recursively, 再加right recursively, 然后组成头部. +- Option1 w/o helper; option2 with dfs helper. + +#### Method2, Iterative, Stack +- Option1: reversely add to list +- 双stack的思想, 需要在图纸上画一画 + - 原本需要的顺序是: 先leftChild, rightChild, currNode. + - 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild + - 这样出来的结果是reverse的, 那么翻转一下就可以了. +- reverse add: `list.add(0, x)`; +- 利用stack的特点 + - 每次加element进stack的时候, 想要在 bottom/后process的, 先加 + - 想要下一轮立刻process的, 最后push进stack. +- Option2: regular sequence add to stack: add curr, right, left + - Use set to contain the processed children + - only process curr if its children is processed + + +``` +/* +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 + +*/ + +// Method1, Option1: dfs w/o helper. always add left, add right, then add middle +class Solution { + public List postorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) return rst; + rst.addAll(postorderTraversal(root.left)); + rst.addAll(postorderTraversal(root.right)); + rst.add(root.val); + + return rst; + } +} + +// Method1, Option2: recursive with dfs helper +public class Solution { + public List postorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + dfs(rst, root); + return rst; + } + + public void dfs(Listrst, TreeNode node) { + if (node == null) return; + dfs(rst, node.left); + dfs(rst, node.right); + rst.add(node.val); + } +} + +// 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; + } +} + +// Method2, Iterative, Option2: regular sequence add to stack: add curr, right, left +// only process curr if its children is processed +class Solution { + public List postorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) return rst; + Stack stack = new Stack<>(); + Set set = new HashSet<>(); + stack.push(root); + + while (!stack.isEmpty()) { + TreeNode node = stack.peek(); + if (validate(set, node)) { + stack.pop(); + rst.add(node.val); + set.add(node); + continue; + } + if (node.right != null) stack.push(node.right); + if (node.left != null) stack.push(node.left); + } + return rst; + } + + private boolean validate(Set set, TreeNode node) { + if(node.left == null && node.right == null) return true; + boolean left = set.contains(node.left), right = set.contains(node.right); + if (left && right) return true; + if ((node.left == null && right) || (node.right == null && left)) return true; + return false; + } +} +``` \ No newline at end of file diff --git a/Java/146. LRU Cache.java b/Java/146. LRU Cache.java new file mode 100755 index 0000000..0872056 --- /dev/null +++ b/Java/146. LRU Cache.java @@ -0,0 +1,186 @@ +M +tags: Design, Linked List, Hash Table, Doubly Linked List +time: O(1) +space: O(1) + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + +``` +/* +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; + } + } + 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; + } +} + + +// Deque Less Efficient: +// Deque remove() loops over the list to remove, so it's inefficient +public class LRUCache { + class Node { + int key, val; + public Node(int key, int val){ + this.key = key; + this.val = val; + } + } + public int capacity; + public HashMap map; + public Deque queue; + public LRUCache(int capacity) { + this.capacity = capacity; + this.map = new HashMap<>(); + this.queue = new LinkedList<>(); + } + + public int get(int key) { + if(!map.containsKey(key)) return -1; + Node 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 { + Node node = new Node(key, value); + if (map.size() >= this.capacity) { + Node rm = queue.pollLast(); + map.remove(rm.key); + } + queue.offerFirst(node); + map.put(key, node); + } + } + + public void moveToHead(Node node) { + queue.remove(node); + queue.offerFirst(node); + } +} + +``` \ No newline at end of file diff --git a/Java/149. Max Points on a Line.java b/Java/149. Max Points on a Line.java new file mode 100755 index 0000000..08585e7 --- /dev/null +++ b/Java/149. Max Points on a Line.java @@ -0,0 +1,100 @@ +H +tags: Array, Hash Table, Geometry, Math +time: O(n^2) +space: O() + +给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 { + int X = 0; + int Y = 1; + public int maxPoints(int[][] points) { + if (points == null || points.length == 0) return 0; + int result = 0; + Map map = new HashMap<>(); + for (int i = 0; i < points.length; i++) { // for loop to try all points. O(n^2) + int max = 0, overlap = 0; + map.clear(); + for (int j = i + 1; j < points.length; j++) { + int x = points[j][X] - points[i][X], 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); // # max num on certain slop + # of overlaop points + self + } + return result; + } + + private int findGCD(int a, int b) { + if (b == 0) return a; + return findGCD(b, a % b); + } +} + + + +``` \ No newline at end of file diff --git a/Java/15. 3Sum.java b/Java/15. 3Sum.java new file mode 100755 index 0000000..c9501a9 --- /dev/null +++ b/Java/15. 3Sum.java @@ -0,0 +1,125 @@ +M +tags: Array, Sort, Two Pointers +time: O(n^2) + +#### sort array, for loop + two pointer +- 处理duplicate wthin triplets: + - 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. + - 如果是triplet内有重复, 用完start point, 移动到结尾. +- Note: + - 1. 找 value triplets, 多个结果。注意,并非找index。 + - 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + - 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 +- 时间 O(n^2), 两个nested loop + +#### For loop + 2Sum +- HashMap 2Sum. Remember to handle duplicates + - 1. For loop 挑个数字A + - 2. 2Sum 出一堆2个数字的结果 + - 3. Cross match 步骤1里面的A + +``` +/* +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) +*/ + +// Simplified solution +class Solution { + public List> threeSum(int[] nums) { + List> result = new ArrayList<>(); + Arrays.sort(nums);// O(nlogn) + int n = nums.length; + for (int i = 0; i < n - 2; i++) { // check lo/hi after i + if (i > 0 && nums[i] == nums[i - 1]) continue; // check duplicate + + int lo = i + 1, hi = n - 1; + while (lo < hi) { + int sum = nums[i] + nums[lo] + nums[hi]; + if (sum == 0) { + result.add(Arrays.asList(nums[i], nums[lo], nums[hi])); + while (lo < hi && nums[lo] == nums[lo + 1]) lo++; + while (lo < hi && nums[hi] == nums[hi - 1]) hi--; + lo++; + hi--; + } else if (sum < 0) { + lo++; + } else { + hi--; + } + } + } + return result; + } +} + +/* + Thoughts: use HashMap with 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/Java/151. Reverse Words in a String.java b/Java/151. Reverse Words in a String.java new file mode 100755 index 0000000..97b9c15 --- /dev/null +++ b/Java/151. Reverse Words in a String.java @@ -0,0 +1,144 @@ +M +tags: String +time: O(n) +spacee: O(n) + +#### Method1: Split string by space, then flip +- Option1: With `s.split(" ")`: No brain, and super fast +- Option2: With `s.split("\\s+")`, it skips space, but slow. Use sb.insert(0, xxx) +- trim() output +- Time, Space: O(n) + +#### Method2: Flip entire, then individual, two pointer +- flip entire string, then flip each individual string +- Time, Space: O(n) + +``` +/* +Given an input string, reverse the string word by word. + +For example, +Given s = "the sky is blue", +return "blue is sky the". + +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. +Hide Company Tags Bloomberg +Hide Tags String +Hide Similar Problems (M) Reverse Words in a String II + + +*/ + +/* +Method1: + 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. +*/ +public class Solution { + public String reverseWords(String s) { + if (s == null) return s; + + String[] strs = s.split(" "); + StringBuffer sb = new StringBuffer(); + for (String str : strs) { + if (str.length() != 0) sb.insert(0, str + " "); + } + return sb.toString().trim(); + } +} + + +/* +1. Reverse it like reversing a int array +2. Use Split into arrays. +3. When reversing, make sure not empty string "" +*/ +public class Solution { + public String reverseWords(String s) { + String[] strs = s.split(" "); + reverse(strs); + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < strs.length; i++){ + if (strs[i].length() > 0) { + sb.append(strs[i]); + sb.append(" "); + } + } + return sb.toString().trim(); + } + + private void reverse(String[] strs) { + int i = 0, j = strs.length - 1; + while (i < j) { + String temp = strs[i]; + strs[i++] = strs[j]; + strs[j--] = temp; + } + } +} + +/* +Method2: +1. to char array and flip all +2. flip individual +3. skip all spaces +*/ +public class Solution { + public String reverseWords(String s) { + if (s == null) return s; + int n = s.length(); + // 1. flip all + char[] arr = s.toCharArray(); + reverse(arr, 0, n - 1); + // 2. reverse individual + reverseIndividual(arr, n); + // 3. output and skip space + return output(arr); + } + + private String output(char[] arr) { + StringBuffer sb = new StringBuffer(); + int count = 0; + for (char c : arr) { + count += c == ' ' ? 1 : 0; + if (count != 0 & c != ' ') count = 0; + if (count <= 1) sb.append(c); + } + return sb.toString().trim(); + } + + private void reverseIndividual(char[] arr, int n) { + int i = 0, j = 0; + while (i < n) { + while (i < j || i < n && arr[i] == ' ') i++; // skip space + while (j < i || j < n && arr[j] != ' ') j++; // skip non-space + reverse(arr, i , j - 1); + } + } + + private void reverse(char[] arr, int i, int j) { + while (i < j) { + char temp = arr[i]; + arr[i++] = arr[j]; + arr[j--] = temp; + } + } +} + + + +``` \ No newline at end of file diff --git a/Java/152. Maximum Product Subarray.java b/Java/152. Maximum Product Subarray.java new file mode 100755 index 0000000..cd3ae5e --- /dev/null +++ b/Java/152. Maximum Product Subarray.java @@ -0,0 +1,152 @@ +M +tags: Array, DP, Subarray, PreProduct +time: O(n) +space: O(1) + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + +``` +/* +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 +*/ +/* +Method1: 2 preProduct array; 'Largest', DP. +Consider positivie/Negative numbers. +- If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +*/ +class Solution { + public int maxProduct(int[] nums) { + int n = nums.length; + int[] maxProduct = new int[n], minProduct = new int[n]; + maxProduct[0] = nums[0]; + minProduct[0] = nums[0]; + int max = nums[0]; + for (int i = 1; i < n; i++) { + int num = nums[i]; + if (num > 0) { + maxProduct[i] = Math.max(num, maxProduct[i - 1] * num); + minProduct[i] = Math.min(num, minProduct[i - 1] * num); + } else { + maxProduct[i] = Math.max(num, minProduct[i - 1] * num); + minProduct[i] = Math.min(num, maxProduct[i - 1] * num); + } + max = Math.max(max, maxProduct[i]); + } + return max; + } +} + +/* +Method1: improve with Rolling array +Space: O(1) + */ +class Solution { + public int maxProduct(int[] nums) { + int n = nums.length; + int[] maxProduct = new int[2], minProduct = new int[2]; + maxProduct[0] = nums[0]; + minProduct[0] = nums[0]; + int max = nums[0]; + for (int i = 1; i < n; i++) { + int num = nums[i]; + if (num > 0) { + maxProduct[i % 2] = Math.max(num, maxProduct[(i - 1) % 2] * num); + minProduct[i % 2] = Math.min(num, minProduct[(i - 1) % 2] * num); + } else { + maxProduct[i % 2] = Math.max(num, minProduct[(i - 1) % 2] * num); + minProduct[i % 2] = Math.min(num, maxProduct[(i - 1) % 2] * num); + } + max = Math.max(max, maxProduct[i % 2]); + } + return max; + } +} + +// Method1 simplification +class Solution { + public int maxProduct(int[] nums) { + int n = nums.length; + int[] maxProduct = new int[n], minProduct = new int[n]; + maxProduct[0] = nums[0]; + minProduct[0] = nums[0]; + int max = nums[0]; + for (int i = 1; i < n; i++) { + int num = nums[i]; + // lazy writing: knowing what to look for, so just calculate regardless if num < 0 + maxProduct[i] = Math.max(num, Math.max(maxProduct[i - 1] * num, minProduct[i - 1] * num)); + minProduct[i] = Math.min(num, Math.min(minProduct[i - 1] * num, maxProduct[i - 1] * num)); + max = Math.max(max, maxProduct[i]); + } + return max; + } +} + + +/* +Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- time: O(n), +- space: O(1) +*/ +class Solution { + int maxProduct(int nums[]) { + // store the result that is the max we have found so far + int max = nums[0]; + + // imax/imin stores the max/min product of + // subarray that ends with the current number nums[i] + for (int i = 1, imax = max, imin = max; i < nums.length; i++) { + // multiplied by a negative makes big number smaller, small number bigger + // so we redefine the extremums by swapping them + if (nums[i] < 0) { + int temp = imax; + imax = imin; + imin = temp; + } + + // max/min product for the current number is either the current number itself + // or the max/min by the previous number times the current one + imax = Math.max(nums[i], imax * nums[i]); + imin = Math.min(nums[i], imin * nums[i]); + + // the newly computed max value is a candidate for our global result + max = Math.max(max, imax); + } + return max; + } +} + +``` \ No newline at end of file diff --git a/Java/156. Binary Tree Upside Down.java b/Java/156. Binary Tree Upside Down.java new file mode 100755 index 0000000..6cea911 --- /dev/null +++ b/Java/156. Binary Tree Upside Down.java @@ -0,0 +1,79 @@ +M +tags: Tree, DFS +time: O(n) +space: O(h) + +#### DFS +- Given that left & right nodes are always available in pair, at each level: + - perform dfs to find new root: return deepest left node as root + - pick out curr left node and fix current level: + - add right node as left node + - add root as right node + +``` +/* +Given a binary tree where all the right nodes are either leaf nodes with a sibling (a left node that shares the same parent node) or empty, flip it upside down and turn it into a tree where the original right nodes turned into left leaf nodes. Return the new root. + +Example: + +Input: [1,2,3,4,5] + + 1 + / \ + 2 3 + / \ +4 5 + +Output: return the root of the binary tree [4,5,2,#,#,3,1] + + 4 + / \ + 5 2 + / \ + 3 1 +Clarification: + +Confused what [4,5,2,#,#,3,1] means? Read more below on how binary tree is serialized on OJ. + +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]. +*/ + + +/* +- Given that left & right nodes are always available in pair, at each level: + - perform dfs to find new root: return deepest left node as root + - pick out curr left node and fix current level: + - add right node as left node + - add root as right node +*/ +class Solution { + public TreeNode upsideDownBinaryTree(TreeNode root) { + if (root == null || root.left == null) return root; + TreeNode leftNode = root.left; + // DFS to finish work on left path and return it as new root + TreeNode newRoot = upsideDownBinaryTree(leftNode); + + // set original right to be root; + leftNode.left = root.right; + leftNode.right = root; + + // turn curr root into a leaf + root.right = null; + root.left = null; + + // return new root + return newRoot; + } +} +``` \ No newline at end of file diff --git a/Java/157. Read N Characters Given Read4.java b/Java/157. Read N Characters Given Read4.java new file mode 100755 index 0000000..ddf5ab0 --- /dev/null +++ b/Java/157. Read N Characters Given Read4.java @@ -0,0 +1,110 @@ +E +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)` +- Edge Case: + - When there is not enough space to the result buffer, `i + 3 > n`, then only copy what we can: `Math.min(n - i, count)` + - `count < 4` meaning there is not enough content to read, break + +``` +/* +Given a file and assume that you can only read the file using a given method read4, implement a method to read n characters. + + + +Method read4: + +The API read4 reads 4 consecutive characters from the file, then writes those characters into the buffer array buf. + +The return value is the number of actual characters read. + +Note that read4() has its own file pointer, much like FILE *fp in C. + +Definition of read4: + + Parameter: char[] buf + Returns: int + +Note: buf[] is destination not source, the results from read4 will be copied to buf[] +Below is a high level example of how read4 works: + +File file("abcdefghijk"); // File is "abcdefghijk", initially file pointer (fp) points to 'a' +char[] buf = new char[4]; // Create buffer with enough space to store characters +read4(buf); // read4 returns 4. Now buf = "abcd", fp points to 'e' +read4(buf); // read4 returns 4. Now buf = "efgh", fp points to 'i' +read4(buf); // read4 returns 3. Now buf = "ijk", fp points to end of file + + +Method read: + +By using the read4 method, implement the method read that reads n characters from the file and store it in the buffer array buf. Consider that you cannot manipulate the file directly. + +The return value is the number of actual characters read. + +Definition of read: + + Parameters: char[] buf, int n + Returns: int + +Note: buf[] is destination not source, you will need to write the results to buf[] + + +Example 1: + +Input: file = "abc", n = 4 +Output: 3 +Explanation: After calling your read method, buf should contain "abc". We read a total of 3 characters from the file, so return 3. Note that "abc" is the file's content, not buf. buf is the destination buffer that you will have to write the results to. +Example 2: + +Input: file = "abcde", n = 5 +Output: 5 +Explanation: After calling your read method, buf should contain "abcde". We read a total of 5 characters from the file, so return 5. +Example 3: + +Input: file = "abcdABCD1234", n = 12 +Output: 12 +Explanation: After calling your read method, buf should contain "abcdABCD1234". We read a total of 12 characters from the file, so return 12. +Example 4: + +Input: file = "leetcode", n = 5 +Output: 5 +Explanation: After calling your read method, buf should contain "leetc". We read a total of 5 characters from the file, so return 5. + + +Note: + +Consider that you cannot manipulate the file directly, the file is only accesible for read4 but not for read. +The read function will only be called once for each test case. +You may assume the destination buffer array, buf, is guaranteed to have enough space for storing n characters. +*/ + +/* +- 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/158. Read N Characters Given Read4 II - Call multiple times.java b/Java/158. Read N Characters Given Read4 II - Call multiple times.java new file mode 100755 index 0000000..858ed05 --- /dev/null +++ b/Java/158. Read N Characters Given Read4 II - Call multiple times.java @@ -0,0 +1,77 @@ +H +tags: String, Enumeration +time: O(n) +space: O(n) + +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就好了. +- alaternatively: use queue to hold so we do not need to worry about size + +``` +/* +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 { + char[] cache = new char[4]; + int cacheIndex = 0; + int count = 0; + //int index = 0; + 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/159. Longest Substring with At Most Two Distinct Characters.java b/Java/159. Longest Substring with At Most Two Distinct Characters.java new file mode 100755 index 0000000..3a25b79 --- /dev/null +++ b/Java/159. Longest Substring with At Most Two Distinct Characters.java @@ -0,0 +1,112 @@ +M +tags: Hash Table, Two Pointers, String, Sliding Window +time: O(n) +space: O(1) + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + +``` +/* +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. + +*/ + +/* +Method1: Slinding window +- Typical slinding window: the goal is to keep a distinct char size/window of size 2: +- use a map to track; map.size() is the window size + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +*/ +class Solution { + public int lengthOfLongestSubstringTwoDistinct(String s) { + if (s == null) return 0; + int left = 0, right = 0, max = 0, n = s.length(); + Map freq = new HashMap<>(); + + while (right < n) { + // 1) expand right + char head = s.charAt(right++); + freq.put(head, freq.getOrDefault(head, 0) + 1); + + // 2) process when window is reached + if (freq.size() <= 2) max = Math.max(max, right - left); + + // 3) contract left + if (freq.size() > 2) { + char tail = s.charAt(left++); + freq.put(tail, freq.get(tail) - 1); + if (freq.get(tail) == 0) freq.remove(tail); + } + } + + return max; + } +} + + +/* +#### Method2: sliding window but truncate whole block +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/160. Intersection of Two Linked Lists.java b/Java/160. Intersection of Two Linked Lists.java new file mode 100755 index 0000000..065986b --- /dev/null +++ b/Java/160. Intersection of Two Linked Lists.java @@ -0,0 +1,145 @@ +E +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. + +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 +*/ +// 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: + 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/Java/168. Excel Sheet Column Title.java b/Java/168. Excel Sheet Column Title.java new file mode 100755 index 0000000..a994bc0 --- /dev/null +++ b/Java/168. Excel Sheet Column Title.java @@ -0,0 +1,53 @@ +E +tags: Math +time: O(n) +space: O(1) + +#### 基本换算 +- 26位, 像10位一样去思考 +- 从末尾开始, 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/169. Majority Element.java b/Java/169. Majority Element.java new file mode 100755 index 0000000..a75fd16 --- /dev/null +++ b/Java/169. Majority Element.java @@ -0,0 +1,209 @@ +E +tags: Array, Divide and Conquer, Bit Manipulation, Moore Voting, Sort +time: O(n) +space: O(1) + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + +``` +/* +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. + +Example 1: + +Input: [3,2,3] +Output: 3 +Example 2: + +Input: [2,2,1,1,1,2,2] +Output: 2 + +*/ + + +/* +HashMap +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; + } +} + +/* +Sort +*/ +class Solution { + public int majorityElement(int[] nums) { + if (nums == null || nums.length == 0) { + return -1; + } + Arrays.sort(nums); + return nums[nums.length / 2]; + } +} + +/** +Divide an conquer + */ +class Solution { + public int majorityElement(int[] nums) { + if (nums == null || nums.length == 0) { + return -1; + } + return subMajorElement(nums, 0, nums.length - 1); + } + + private int subMajorElement(int[] nums, int lo, int hi) { + if (lo == hi) return nums[lo]; + int mid = lo + (hi - lo) / 2; + int left = subMajorElement(nums, lo, mid); + int right = subMajorElement(nums, mid + 1, hi); + + // if same, that ele is definitely the major element in range + if (left == right) return left; + + // if not same, compare + int leftCount = countInRange(nums, left, lo, hi); + int rightCount = countInRange(nums, right, lo, hi); + + return leftCount > rightCount ? left : right; + } + + private int countInRange(int[] nums, int value, int lo, int hi) { + int count = 0; + for (int i = lo; i <= hi; i++) { + if (nums[i] == value) count++; + } + return count; + } +} + +/* +Moore Voting +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; + } +} + + +/* +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/170. Two Sum III - Data structure design.java b/Java/170. Two Sum III - Data structure design.java new file mode 100755 index 0000000..c0de757 --- /dev/null +++ b/Java/170. Two Sum III - Data structure design.java @@ -0,0 +1,70 @@ +E +tags: Hash Table, Design, Memoization +time: O(n) +space: O(n) + +#### Hash Table, Memo +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +- time: O(n), loop over all elements in map +- space: O(n), store all elements in map & memoization set + +``` +/* +Design and implement a TwoSum class. It should support the following operations: add and find. + +add - Add the number to an internal data structure. +find - Find if there exists any pair of numbers which sum is equal to the value. + +Example 1: + +add(1); add(3); add(5); +find(4) -> true +find(7) -> false +Example 2: + +add(3); add(1); add(2); +find(3) -> true +find(6) -> false +*/ +/* +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +*/ +class TwoSum { + Map map = new HashMap<>(); + Set memo = new HashSet<>(); + /** Initialize your data structure here. */ + public TwoSum() { } + + /** Add the number to an internal data structure.. */ + public void add(int number) { + map.putIfAbsent(number, 0); + map.put(number, map.get(number) + 1); + } + + /** Find if there exists any pair of numbers which sum is equal to the value. */ + public boolean find(int value) { + if (memo.contains(value)) return true; + for (Map.Entry entry : map.entrySet()) { + int num = entry.getKey(), count = entry.getValue(); + int remain = value - num; + if (map.containsKey(remain)) { + if (remain == num && count < 2) continue; // only 1 instance of the element exist, skip + memo.add(value); + return true; + } + } + return false; + } +} + +/** + * Your TwoSum object will be instantiated and called as such: + * TwoSum obj = new TwoSum(); + * obj.add(number); + * boolean param_2 = obj.find(value); + */ +``` \ No newline at end of file diff --git a/Java/173. Binary Search Tree Iterator.java b/Java/173. Binary Search Tree Iterator.java new file mode 100755 index 0000000..52b5dee --- /dev/null +++ b/Java/173. Binary Search Tree Iterator.java @@ -0,0 +1,226 @@ +M +tags: Stack, Tree, Design, BST +time: O(1) average +space: O(h) + +#### BST in order traversal +- 用stack记录最小值, 放在top. O(h) space. +- 每次消耗TreeNode, 都看看rightNode(其实就是下一个最小的candidate), 并且一条龙stack叠上rightNode所有的left子孙. + +#### Previous Notes: +- 用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过,如此便会死循环。 + + +``` +/* +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. + +Example +For the following binary search tree, in-order traversal by using iterator is [1, 6, 10, 11, 12] + + 7 + / \ +3 15 + / \ + 9 12 +BSTIterator iterator = new BSTIterator(root); +iterator.next(); // return 3 +iterator.next(); // return 7 +iterator.hasNext(); // return true +iterator.next(); // return 9 +iterator.hasNext(); // return true +iterator.next(); // return 15 +iterator.hasNext(); // return true +iterator.next(); // return 20 +iterator.hasNext(); // return false + +Note: +next() and hasNext() should run in average O(1) time and uses O(h) memory, where h is the height of the tree. +You may assume that next() call will always be valid, that is, there will be at least a next smallest number in the BST when next() is called. + +*/ + + +/** + * 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: +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 { + Stack stack = new Stack<>(); + public BSTIterator(TreeNode root) { + pushLeftNodes(root); + } + + /** @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(); + pushLeftNodes(rst.right); + return rst.val; + } + + private void pushLeftNodes(TreeNode node) { + while(node != null) { + stack.push(node); + node = node.left; + } + } +} + +/** + Below are from LintCode, not passing LeetCode +*/ + +/* + Alternative O(h) space Solution. + Keep it light in constructor, more in next() +*/ +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/Java/189. Rotate Array.java b/Java/189. Rotate Array.java new file mode 100755 index 0000000..f861e64 --- /dev/null +++ b/Java/189. Rotate Array.java @@ -0,0 +1,63 @@ +E +tags: Array, Rotation + +#### Rotate array in place +- rotate all +- rotate 2 sides: < k or >= + + +#### Rotate by buffer the k array + +``` +/* +Given an array, rotate the array to the right by k steps, where k is non-negative. + +Example 1: + +Input: [1,2,3,4,5,6,7] and k = 3 +Output: [5,6,7,1,2,3,4] +Explanation: +rotate 1 steps to the right: [7,1,2,3,4,5,6] +rotate 2 steps to the right: [6,7,1,2,3,4,5] +rotate 3 steps to the right: [5,6,7,1,2,3,4] +Example 2: + +Input: [-1,-100,3,99] and k = 2 +Output: [3,99,-1,-100] +Explanation: +rotate 1 steps to the right: [99,-1,-100,3] +rotate 2 steps to the right: [3,99,-1,-100] +Note: + +Try to come up as many solutions as you can, there are at least 3 different ways to solve this problem. +Could you do it in-place with O(1) extra space? + + */ + +/* +1. rotate all +1. rotate 2 sides: < k or >= k +*/ +class Solution { + public void rotate(int[] nums, int k) { + int n = nums.length; + k = k % n; + if (k == 0) return; + // rotate all + rotateByRange(nums, 0, n - 1); + // rotate side + rotateByRange(nums, 0, k - 1); + rotateByRange(nums, k, n - 1); + } + + private void rotateByRange(int[] nums, int x, int y) { + while (x >= 0 && x < y) { + int temp = nums[x]; + nums[x] = nums[y]; + nums[y] = temp; + x++; + y--; + } + } +} +``` \ No newline at end of file diff --git a/Java/19. Remove Nth Node From End of List.java b/Java/19. Remove Nth Node From End of List.java new file mode 100755 index 0000000..7f6bdbc --- /dev/null +++ b/Java/19. Remove Nth Node From End of List.java @@ -0,0 +1,63 @@ +M +tags: Linked List, Two Pointers +time: O(n) +space: O(1) + +#### Two Pointer +- 1 end pointer to define the window based n steps +- 1 pre pointer to track the node before the targeting node +- when end reaches null, remove nth node: link pre and head.next + +``` +/* +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 + + +*/ +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +/* +Thoughts: +Find starting piont of the window, and keep moving forward, until candidate reach end. +*/ +class Solution { + public ListNode removeNthFromEnd(ListNode head, int n) { + ListNode dummy = new ListNode(-1), pre = new ListNode(0); + pre.next = head; + dummy = pre; + + // Establish window + ListNode end = head; // end node + while (n--> 0 && end != null) end = end.next; + + // move window + while (end != null) { + end = end.next; + pre = pre.next; + head = head.next; + } + // remove nth node + pre.next = head.next; + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Java/198. House Robber.java b/Java/198. House Robber.java new file mode 100755 index 0000000..a160699 --- /dev/null +++ b/Java/198. House Robber.java @@ -0,0 +1,110 @@ +E +tags: DP, Sequence DP, Status 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虽然抽象, 但是更加实用. + +#### Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- calculation + - dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]). The prior house can be either state. + - dp[i][1] = dp[i - 1][0] + nums[i]. The prior house must be `NOT ROBBED`. +- return `Math.max(dp[n - 1][0], dp[n - 1][1])` + +``` +/* +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. + + +Example +Given [3, 8, 4], return 8. + +Challenge +O(n) time and O(1) memory. + +Tags Expand +Dynamic Programming + +*/ + +/** +Method1: Sequence DP +*/ +// 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]; + } +} +// Method1: improve, rolling array +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int n = nums.length; + long[] dp = new long[2]; + dp[0] = 0; + dp[1] = nums[0]; + for (int i = 2; i <= n; i++) { + dp[i % 2] = Math.max(dp[(i - 1)%2], dp[(i - 2)%2] + nums[i - 1]); + } + return (int) dp[n % 2]; + } +} + + + + +/* +Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- dp[i][0] = max of (dp[i - 1][1], dp[i - 1][0]) +- dp[i][1] = dp[i - 1][0] + nums[i] +*/ + +class Solution { + public int rob(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int n = nums.length; + int[][] dp = new int[n][2]; + dp[0][1] = nums[0]; + for (int i = 1; i < n; i++) { + dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]); + dp[i][1] = dp[i - 1][0] + nums[i]; + } + + return Math.max(dp[n - 1][0], dp[n - 1][1]); + } +} + + +``` \ No newline at end of file diff --git a/Java/199. Binary Tree Right Side View.java b/Java/199. Binary Tree Right Side View.java new file mode 100755 index 0000000..25dd041 --- /dev/null +++ b/Java/199. Binary Tree Right Side View.java @@ -0,0 +1,103 @@ +M +tags: Tree, DFS, BFS +time: O(n) +space: O(n) + +给一个binary tree, 从右边看过来, return all visible nodes + +#### BFS +- 最右: 即level traversal每一行的最末尾. +- BFS, queue 来存每一行的内容, save end node into list +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n nodes in final results + + +#### DFS +- Use Map to override the result at each level +- dfs: + - dfs(node.left) and then dfs(node.right) because we want to log right side last +- record global max depth for iteration purpose +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n stacks (and n nodes in final results) + + +``` +/* +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<>(); + if (root == null) return result; + + // init queue + 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 { + int maxDepth = -1; + public List rightSideView(TreeNode root) { + // init map, dfs + Map map = new HashMap<>(); + dfs(map, root, 0); + + List result = new ArrayList<>(); + for (int i = 0; i <= maxDepth; i++) result.add(map.get(i)); + return result; + } + + private void dfs(Map map, TreeNode node, int depth) { + if(node == null) return; + map.put(depth, node.val); + maxDepth = Math.max(maxDepth, depth); + dfs(map, node.left, depth + 1); + dfs(map, node.right, depth + 1); + } +} + + +``` \ 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/2. Add Two Numbers.java b/Java/2. Add Two Numbers.java new file mode 100755 index 0000000..fa4c792 --- /dev/null +++ b/Java/2. Add Two Numbers.java @@ -0,0 +1,76 @@ +M +tags: Linked List, Math +time: O(max(m,n)) +space: O(max(m,n)) + +LinkedList都已经反转好了,直接做. 跟Add Binary的理解方式一模一样. + +#### Math, Linked List +- reverse order helps calculation + - add additional carry to end + - not same length: align on left +- traverse till both ends +- 遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + + +``` +/* +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; + * } + * } + */ + +class Solution { + public ListNode addTwoNumbers(ListNode l1, ListNode l2) { + ListNode dummy = new ListNode(-1); + ListNode node = dummy; + int carry = 0; + while (l1 != null || l2 != null) { + node.next = new ListNode(carry); + node = node.next; + + if (l1 != null) { + node.val += l1.val; + l1 = l1.next; + } + if (l2 != null) { + node.val += l2.val; + l2 = l2.next; + } + + carry = node.val / 10; + node.val = node.val % 10; + } + + if (carry != 0) node.next = new ListNode(carry); + + return dummy.next; + } +} + +``` \ No newline at end of file diff --git a/Java/20. Valid Parentheses.java b/Java/20. Valid Parentheses.java new file mode 100755 index 0000000..097dc22 --- /dev/null +++ b/Java/20. Valid Parentheses.java @@ -0,0 +1,45 @@ +E +tags: String, Stack +time: O(n) +space: O(n) + +#### Stack +- 剥皮过程。解铃还须系铃人 +- 左边的外皮'{['在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 +*/ + +class Solution { + public boolean isValid(String s) { + if (s == null) return true; + if (s.length() % 2 != 0) return false; + + Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + if (stack.isEmpty() || (c == '(' || c == '{' || c == '[')) stack.push(c); + else if (validate(stack.peek(), c)) stack.pop(); + else return false; + } + return stack.isEmpty(); + } + + private boolean validate(char a, char b) { + if (a == '(') return b == ')'; + if (a == '{') return b == '}'; + if (a == '[') return b == ']'; + return false; + } +} + + +``` \ No newline at end of file diff --git a/Java/200. Number of Islands.java b/Java/200. Number of Islands.java new file mode 100755 index 0000000..9b6cead --- /dev/null +++ b/Java/200. Number of Islands.java @@ -0,0 +1,323 @@ +M +tags: DFS, BFS, Union Find, Matrix DFS +time: O(n) +space: O(n) + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + +``` +/* +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: + +Input: +11110 +11010 +11000 +00000 + +Output: 1 +Example 2: + +Input: +11000 +11000 +00100 +00011 + +Output: 3 +*/ + + +/* +Method1: DFS +- 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) 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'; + } +} + +/* +Method2: Union Find +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) return 0; + int m = grid.length, n = grid[0].length; + + // init with # of island blocks, goal is to connect them all. + UnionFind uf = new UnionFind(m * n); + uf.setCount(countIsland(grid)); + + 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], y = j + dy[k]; + if (!validate(grid, x, y)) { // Attemp to union all of the 4 directions + uf.union(convertToIndex(i, j, n), convertToIndex(x, y, n)); + } + } + } + } + } + // output the united total # of islands + return uf.query(); + } + + private int countIsland(char[][] grid) { + int m = grid.length, n = grid[0].length; + int count = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + count += grid[i][j] == '1' ? 1 : 0; + } + } + return count; + } + + // 1D index = rowNum * numOfColumn + colNum + private int convertToIndex(int x, int y, int rowLength) { + return x * rowLength + y; + } + private boolean validate(char[][] grid, int x, int y) { + return x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == '0'; + } +} + +// Union Find definition +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]); + } +} + +// Method3: BFS +class Solution { + public int numIslands(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0] == null || grid[0].length == 0) return 0; + int count = 0; + Queue queue = new LinkedList<>(); + for (int i = 0; i < grid.length; i++) { + for (int j = 0; j < grid[0].length; j++) { + if (grid[i][j] == '1') { + count++; + grid[i][j] = '0'; + queue.offer(i * grid[0].length + j); + processQueue(queue, grid); + } + } + } + return count; + } + + int[] dx = {0, 0, 1, -1}; + int[] dy = {1, -1, 0, 0}; + public void processQueue(Queue queue, char[][] grid) { + int m = grid.length, n = grid[0].length; + while(!queue.isEmpty()) { + int key = queue.poll(); + int x = key / n, y = key % n; + for (int i = 0; i < 4; i++) { + int nx = x + dx[i], ny = y + dy[i]; + if (validate(grid, nx, ny)) { + grid[nx][ny] = '0'; + queue.offer(nx * n + ny); + } + } + } + } + + private boolean validate(char[][] grid, int x, int y) { + return x >= 0 && x < grid.length && y >= 0 && y < grid[0].length && grid[x][y] == '1'; + } +} + + +/* +Thoughts: +UnionFind with map rather than array +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; + } +} + + +``` \ No newline at end of file diff --git a/Java/202. Happy Number.java b/Java/202. Happy Number.java new file mode 100755 index 0000000..ef5027a --- /dev/null +++ b/Java/202. Happy Number.java @@ -0,0 +1,57 @@ +E +tags: Hash Table, Math +time: O(m), m iterations +space: O(m), m number in set + +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) { + Set set = new HashSet<>(); + long sum = n; + set.add(sum); + + while(sum != 1) { + sum = process(sum); // 1 + 81 = 82 + if(!set.add(sum)) return false; // 82, 68, 100, 1 + } + return true; + } + + private long process(long n) { + long sum = 0; + while (n != 0) { + long mod = n % 10; + sum += mod * mod; + n /= 10; + } + return sum; + } +} + +``` \ No newline at end of file diff --git a/Java/203. Remove Linked List Elements.java b/Java/203. Remove Linked List Elements.java new file mode 100755 index 0000000..77a875c --- /dev/null +++ b/Java/203. Remove Linked List Elements.java @@ -0,0 +1,48 @@ +E +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. + +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. +*/ + +class Solution { + public ListNode removeElements(ListNode head, int val) { + 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; + } +} + +``` \ No newline at end of file diff --git a/Java/204. Count Primes.java b/Java/204. Count Primes.java new file mode 100755 index 0000000..8fe9eed --- /dev/null +++ b/Java/204. Count Primes.java @@ -0,0 +1,91 @@ +E +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/205. Isomorphic Strings.java b/Java/205. Isomorphic Strings.java new file mode 100755 index 0000000..07f9883 --- /dev/null +++ b/Java/205. Isomorphic Strings.java @@ -0,0 +1,54 @@ +E +tags: Hash Table +time: O(n) +space: O(n) + +#### HashMap +- check 2 failture cases: + - same key, value not matching + - two key maps to same value + +``` +/* +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; + } +} + +``` diff --git a/Java/206. Reverse Linked List.java b/Java/206. Reverse Linked List.java new file mode 100755 index 0000000..9bdf20b --- /dev/null +++ b/Java/206. Reverse Linked List.java @@ -0,0 +1,71 @@ +E +tags: Linked List + +#### Iterative +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + +#### Recursive with a helper function +- source node: head +- target node: new head + +``` +/* +Reverse a linked list. + +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 +*/ + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +// 1) create dummy = head. 2) always insert in front of dummy, and set dummy to new head 3) move head = head.next +class Solution { + public ListNode reverseList(ListNode head) { + if (head == null) return head; + + ListNode dummy = null; + while(head != null) { + ListNode temp = head.next; + head.next = dummy; + dummy = head; + head = temp; + } + return dummy; + } +} + +// recursive +class Solution { + + public ListNode reverseList(ListNode head) { + if (head == null || head.next == null) return head; + return reverse(head, null); + } + + public ListNode reverse(ListNode source, ListNode target) { + if (source == null) return target; + + ListNode temp = source.next; + source.next = target; + target = source; + source = temp; + return reverse(source, target); + } + +} +``` \ No newline at end of file diff --git a/Java/207. Course Schedule.java b/Java/207. Course Schedule.java new file mode 100755 index 0000000..f52ae8a --- /dev/null +++ b/Java/207. Course Schedule.java @@ -0,0 +1,228 @@ +M +tags: DFS, BFS, Graph, Topological Sort, Backtracking +time: O(n) +space: O(n) + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + +``` +/* +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? + +Example 1: + +Input: 2, [[1,0]] +Output: true +Explanation: There are a total of 2 courses to take. + To take course 1 you should have finished course 0. So it is possible. +Example 2: + +Input: 2, [[1,0],[0,1]] +Output: false +Explanation: 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. +You may assume that there are no duplicate edges in the input prerequisites. + + +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. +*/ + +/* +Method1: Topological sort, BFS Kahn's algorithem. +- 1) build inDegreeEdges, 2) build dependencyCount +- topologically process: 1) add leaf node to queue, get ready to process; 2) process leafNode, like cutting of leaf +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +*/ +class Solution { + public boolean canFinish(int numCourses, int[][] prerequisites) { + if (validateInput(numCourses, prerequisites)) return true; + + List[] inDegreeEdges = new List[numCourses]; + int[] dependencyCount = new int[numCourses]; + + // Initialize + for (int i = 0; i < numCourses; i++) inDegreeEdges[i] = new ArrayList<>(); + + // Build inDegreeEdges, and dependencyCount + for (int[] prerequisite : prerequisites) { + inDegreeEdges[prerequisite[1]].add(prerequisite[0]); + dependencyCount[prerequisite[0]]++; + } + + // BFS: 1) add leaf nodes into queue. Get ready to process its incoming edges + Queue queue = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (dependencyCount[i] == 0) queue.add(i); // leaf node that depends on nobody + } + + // 2) process leafNode + int count = 0; // count of leaf node that can eventually can be independent: when all its dependencies are exhausted + while (!queue.isEmpty()) { + count++; + int leafNode = queue.poll(); + List dependencies = inDegreeEdges[leafNode]; + for (int node : dependencies) { + dependencyCount[node]--; // imaging we are cutting off leafNode, so its parent will decrese the dependency count + if (dependencyCount[node] == 0) queue.add(node); // add to result when any node's dependencies count are exhausted + } + } + + // BFS will always end. Validate if # of leaf/independent course equals to total numCourses + // If not equal, then there is cycle edges that cannot be resolved to 0 + 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; + } +} + +/* +Method2: DFS, Option1, using List[] edges to mark inDegree Structure + Similarly, can use HashMap> map; rather than List[]. +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 +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 { + public boolean canFinish(int numCourses, int[][] prerequisites) { + if (numCourses == 0 || prerequisites == null) return true; + List[] edges = buildIndegreeEdges(numCourses, prerequisites); + + // DFS serach && marking visited + int[] visited = new int[numCourses]; + for (int i = 0; i < numCourses; i++) { + if(!dfs(edges, visited, i)) return false; + } + return true; + } + + private boolean dfs(List[] edges, int[] visited, int node) { + if (visited[node] == 1) return true; + if (visited[node] == -1) return false; // cyclic + + visited[node] = -1; + List childNodes = edges[node]; + for (int childNode : childNodes) { + if (!dfs(edges, visited, childNode)) return false; + } + visited[node] = 1; + + return true; + } + + private List[] buildIndegreeEdges(int numCourses, int[][] prerequisites) { + List[] edges = new ArrayList[numCourses]; + // Initialize with empty list of indgree edges + for (int i = 0; i < numCourses; i++) edges[i] = new ArrayList<>(); + // Populate graph edges. prerequisite[1]=current node; prerequisite[0] = incoming dependency + for (int[] prerequisite : prerequisites) { + edges[prerequisite[1]].add(prerequisite[0]); + } + return edges; + } +} + +// DFS Option2: with `class Node {Boolean visiting; Map inDegreeMap}` to build inDegree structure +class Solution { + class Node { + Boolean visiting = null; + Map inDegreeMap = new HashMap<>(); + } + public boolean canFinish(int numCourses, int[][] prerequisites) { + if (numCourses == 0 || prerequisites == null) return true; + Node root = buildIndegreeMap(numCourses, prerequisites); + + // DFS serach && marking visited + return dfs(root); + } + + private boolean dfs(Node node) { + // if visiting: cyclic, return false; visited but now node.visiting == false, return true; + if (node.visiting != null) return !node.visiting; + + node.visiting = true; + for (Node childNode : node.inDegreeMap.values()) { + if (!dfs(childNode)) return false; + } + node.visiting = false; + + return true; + } + + private Node buildIndegreeMap(int numCourses, int[][] prerequisites) { + Node root = new Node(); + // Initialize with empty list of indgree edges + for (int i = 0; i < numCourses; i++) root.inDegreeMap.put(i, new Node()); + // Populate graph edges. prerequisite[1]=current node; prerequisite[0] = incoming dependency + Map rootIndegreeMap = root.inDegreeMap; + for (int[] prerequisite : prerequisites) { + Node currNode = rootIndegreeMap.get(prerequisite[1]); + Node dependencyNode = rootIndegreeMap.get(prerequisite[0]); + currNode.inDegreeMap.put(prerequisite[0], dependencyNode); + } + return root; + } +} + + +``` \ No newline at end of file diff --git a/Java/208. Implement Trie (Prefix Tree).java b/Java/208. Implement Trie (Prefix Tree).java new file mode 100755 index 0000000..b665e5c --- /dev/null +++ b/Java/208. Implement Trie (Prefix Tree).java @@ -0,0 +1,110 @@ +M +tags: Design, Trie + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- Trie Structure: + - trace the char to children node: Map + - boolean isEnd to indicate if there is string end with this node + - `TrieNode {boolean isEnd; Map children}`. +- No need to store letter c in TrieNode +- 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。 + + + +``` +/** +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. +*/ +/* +Trie Structure: +- trace the char to children node: Map +- boolean isEnd to indicate if there is string end with this node +*/ +class Trie { + class TrieNode { + boolean isEnd = false; + Map children = new HashMap<>(); + } + + /** Initialize your data structure here. */ + TrieNode root; + public Trie() { + root = new TrieNode(); + } + + /** Inserts a word into the trie. */ + public void insert(String word) { + TrieNode node = root; + for (char c : word.toCharArray()) { + node.children.putIfAbsent(c, new TrieNode()); + node = node.children.get(c); + } + node.isEnd = true; + } + + /** Returns if the word is in the trie. */ + public boolean search(String word) { + TrieNode node = findNode(word); + if (node == null || !node.isEnd) return false; + return true; + } + + /** Returns if there is any word in the trie that starts with the given prefix. */ + public boolean startsWith(String prefix) { + TrieNode node = findNode(prefix); + return node != null; + } + + private TrieNode findNode(String s) { + TrieNode node = root; + for (char c : s.toCharArray()) { + if (!node.children.containsKey(c)) return null; + node = node.children.get(c); + } + return node; + } +} + +/** + * 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); + */ +``` \ No newline at end of file diff --git a/Java/21. Merge Two Sorted Lists.java b/Java/21. Merge Two Sorted Lists.java new file mode 100755 index 0000000..8049b09 --- /dev/null +++ b/Java/21. Merge Two Sorted Lists.java @@ -0,0 +1,60 @@ +E +tags: Linked List +time: O(n) +space: O(1) + +如题 + +#### 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) { + 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;; + if (l2 != null) node.next = l2; + return head.next; + } +} + +``` \ No newline at end of file diff --git a/Java/210. Course Schedule II.java b/Java/210. Course Schedule II.java new file mode 100755 index 0000000..cb79060 --- /dev/null +++ b/Java/210. Course Schedule II.java @@ -0,0 +1,235 @@ +M +tags: DFS, BFS, Graph, Topological Sort +time: O(n) +space: O(n) + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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. + +Example 1: + +Input: 2, [[1,0]] +Output: [0,1] +Explanation: 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] . +Example 2: + +Input: 4, [[1,0],[2,0],[3,1],[3,2]] +Output: [0,1,2,3] or [0,2,1,3] +Explanation: 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 || prerequisites == null) { + return new int[numCourses]; + } + + List[] inDegreeEdges = new ArrayList[numCourses]; + int[] dependencyCount = new int[numCourses]; + + // Initialize + for (int i = 0; i < numCourses; i++) inDegreeEdges[i] = new ArrayList<>(); + + // Build indegree edges, and dependency count + for (int[] prerequisite : prerequisites) { + inDegreeEdges[prerequisite[1]].add(prerequisite[0]); + dependencyCount[prerequisite[0]]++; + } + + // BFS: 1) prepare the sink/leaf node + Queue queue = new LinkedList<>(); + for (int i = 0; i < numCourses; i++) { + if (dependencyCount[i] == 0) queue.add(i); + } + + List sortedList = new ArrayList<>(); + while (!queue.isEmpty()) { + int leafNode = queue.poll(); + sortedList.add(leafNode); // leaf node as prerequisite, sits in front + List dependencies = inDegreeEdges[leafNode]; + for (int node : dependencies) { + if (--dependencyCount[node] == 0) queue.add(node); + } + } + + // 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[] inDegreeEdges; + List sortedList = new ArrayList<>(); + + public int[] findOrder(int numCourses, int[][] prerequisites) { + if (numCourses == 0) return new int[numCourses]; + + inDegreeEdges = buildInDegreeEdges(numCourses, prerequisites); + visited = new int[numCourses]; + + // 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) return false; // cyclic + + visited[node] = -1; + List dependencies = inDegreeEdges[node]; + for (Integer childNode : dependencies) { + if (!dfs(childNode)) return false; + } + visited[node] = 1; + sortedList.add(0, node); + return true; + } + + private List[] buildInDegreeEdges(int numCourses, int[][] prerequisites) { + List[] inDegreeEdges = new ArrayList[numCourses]; + // Initialize + for (int i = 0; i < numCourses; i++) inDegreeEdges[i] = new ArrayList(); + + // Build graph inDegreeEdges + for (int[] prerequisite : prerequisites) inDegreeEdges[prerequisite[1]].add(prerequisite[0]); + return inDegreeEdges; + } +} + + +/* + 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/211. Add and Search Word - Data structure design.java b/Java/211. Add and Search Word - Data structure design.java new file mode 100755 index 0000000..7e197c9 --- /dev/null +++ b/Java/211. Add and Search Word - Data structure design.java @@ -0,0 +1,96 @@ +M +tags: Trie, Design, Backtracking +time: O(n) to search and to add word +space: < O(mn), depends on the input. m = # of words + +#### Trie, prefix tree. +- Trie Structure: `boolean isEnd`, `HashMap children` + - trie.addWord: 没node就加,有node就移动 + - trie.search: 没node就return false,有node就移动 +- Alternatively, the hash can be `TrieNode[26]` a fixed size array when applicable + - I like map better for the simplicity to write (w/o converting char -> index) + + +``` +/* +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 +*/ + + +/* +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 = new HashMap(); + boolean 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 (char c : word.toCharArray()) { + node.children.putIfAbsent(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 node, String word, int index) { + if (index == word.length()) return node.isEnd; + HashMap children = node.children; + char c = word.charAt(index); + if (children.containsKey(c)) { + return searchHelper(children.get(c), word, index + 1); + } else if (c == '.'){ // wild card + for (TrieNode childNode : children.values()) { + if (searchHelper(childNode, word, index + 1)) return true; + } + } + return false; // all search failed + } +} + +/** + * Your WordDictionary object will be instantiated and called as such: + * WordDictionary obj = new WordDictionary(); + * obj.addWord(word); + * boolean param_2 = obj.search(word); + */ + +``` \ No newline at end of file diff --git a/Java/215. Kth Largest Element in an Array.java b/Java/215. Kth Largest Element in an Array.java new file mode 100755 index 0000000..8241e62 --- /dev/null +++ b/Java/215. Kth Largest Element in an Array.java @@ -0,0 +1,100 @@ +M +tags: Divide and Conquer, Heap, PriorityQueue, MinHeap, Quick Sort, Quick Select +time: O(nlogk) +space: O(k) + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `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) { + 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, pivotNum = nums[pivot]; + + // move pointer and swap + while (low < high) { + while (low < high && nums[low] < pivotNum) low++; // break when nums[low] >= pivotNum + while (low < high && nums[high] >= pivotNum) high--; // break when nums[high] < pivotNum + 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); + 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; + } +} +``` \ No newline at end of file diff --git a/Java/217. Contains Duplicate.java b/Java/217. Contains Duplicate.java new file mode 100755 index 0000000..57c994f --- /dev/null +++ b/Java/217. Contains Duplicate.java @@ -0,0 +1,60 @@ +E +tags: Array, Hash Table +time: O(n) +space: O(1) + + +无序数组, 找是否有重复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; + } + 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/218. The Skyline Problem.java b/Java/218. The Skyline Problem.java new file mode 100755 index 0000000..bfc3220 --- /dev/null +++ b/Java/218. The Skyline Problem.java @@ -0,0 +1,380 @@ +H +tags: Divide and Conquer, Heap, PriorityQueue, BIT, Segment Tree, Sweep Line, HashHeap +time: O(n^2logn) +space: O(n) + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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. + +*/ + +// Sweep line, option1: sort by pos O(nlogn); process 1 pos at a time O(mlogm) +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; + + // init: sort points by pos + PriorityQueue queue = buildQueue(buildings); + + // 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.peek(); + + // 1) Add and trim all points on one X position + while (!queue.isEmpty() && queue.peek().pos == point.pos) { + point = queue.poll(); + if (point.height < 0) maxHeightQueue.offer(-point.height); + else maxHeightQueue.remove(point.height); // remove 1 instance of this height, which marks end point of a building + } + // 2) Add peak + int currPeak = maxHeightQueue.peek(); + if (currPeak != prevPeak) { + List list = new ArrayList<>(); + list.add(point.pos); + list.add(currPeak); + rst.add(list); + prevPeak = currPeak; + } + } + return rst; + } + + private PriorityQueue buildQueue(int[][] buildings) { + PriorityQueue queue = new PriorityQueue<>((a, b) -> a.pos - b.pos); + for (int i = 0; i < buildings.length; i++) { + queue.offer(new Point(buildings[i][0], -buildings[i][2])); // mark starting height negative + queue.offer(new Point(buildings[i][1], buildings[i][2])); + } + return queue; + } + private boolean isInvalid(int[][] buildings) { + return buildings == null || buildings.length == 0 || buildings[0] == null || buildings[0].length == 0; + } +} + +// SweepLine option2: sort by pos, and then by height; process 1 node at a time. +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 maxHeightQueue.remove(point.height); // is end point of a building + + int currPeak = maxHeightQueue.peek(); + if (currPeak != prevPeak) { + List list = new ArrayList<>(); + list.add(point.pos); + list.add(currPeak); + rst.add(list); + prevPeak = currPeak; + } + } + return rst; + } + + private boolean isInvalid(int[][] buildings) { + return buildings == null || buildings.length == 0 || buildings[0] == null || buildings[0].length == 0; + } +} + + + + + +/* +#### HashHeap +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/ +This is not applicable during an interview +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/219. Contains Duplicate II.java b/Java/219. Contains Duplicate II.java new file mode 100755 index 0000000..8790453 --- /dev/null +++ b/Java/219. Contains Duplicate II.java @@ -0,0 +1,89 @@ +E +tags: Array, Hash Table +time: O(n) +space: O(n) + +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 +- Time O(nm), m = # of duplicates. 太慢 +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k + + +#### 这两种做法的区别很有艺术感觉 +- 方法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; + } + 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; + } + Map> map = new HashMap<>(); + for (int i = 0; i < nums.length; i++) { + if (map.containsKey(nums[i])) { + for (int index : map.get(nums[i])) { // wasting time + 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/22. Generate Parentheses.java b/Java/22. Generate Parentheses.java new file mode 100755 index 0000000..a59c461 --- /dev/null +++ b/Java/22. Generate Parentheses.java @@ -0,0 +1,78 @@ +M +tags: String, DFS, Backtracking, Sequence DFS +time: O(2^n) +space: O(2^n) + +#### 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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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 + +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: +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); + } + } +} + +``` \ No newline at end of file diff --git a/Java/221. Maximal Square.java b/Java/221. Maximal Square.java new file mode 100755 index 0000000..3c80256 --- /dev/null +++ b/Java/221. Maximal Square.java @@ -0,0 +1,135 @@ +M +tags: DP, Coordinate DP +time: O(mn) +space: O(mn) + +只能往右边,下面走, 找面积最大的 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; +- init: 每个点都可能是边长1, 如果 matrix[i][j] == '1' +- Space, time O(mn) +- Rolling array: [i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + +``` + +/* +Given a 2D binary matrix filled with 0's and 1's, find the largest square containing only 1's and return its area. + +Example: + +Input: + +1 0 1 0 0 +1 0 1 1 1 +1 1 1 1 1 +1 0 0 1 0 + +Output: 4 +*/ + +// dp = new int[m + 1][n + 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, n = matrix[0].length; + int[][] dp = new int[m + 1][n + 1]; + int maxLen = 0; + + // calculate + for (int i = 1; i <= m; i++) { + for (int j = 1; j <= n; j++) { + if (matrix[i - 1][j - 1] == '0') continue; + 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; + } +} + +/* +dp = new int[m][n]; +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; + } +} + +``` \ No newline at end of file diff --git a/Java/222. Count Complete Tree Nodes.java b/Java/222. Count Complete Tree Nodes.java new file mode 100755 index 0000000..b9242cb --- /dev/null +++ b/Java/222. Count Complete Tree Nodes.java @@ -0,0 +1,143 @@ +M +tags: Binary Search, Tree, DFS +time: O(n) +space: O(h) + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### Method1: DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样 + - 如果一样, 直接 2 ^ h - 1就好 + - 不一样的话, 再DFS +- calculate `2^(h)`: 位运算, Math.pow(2, h) = 2 << (h - 1). 神奇! + - 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- time: O(n) visit all nodes on 1 side +- space: O(h) visit all nodes on 1 side + + +#### Method2: Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +#### Method3: Binary Search +- NOT DONE, TODO: https://leetcode.com/problems/count-complete-tree-nodes/solution/ + +``` +/* +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. + +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 = 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; + } +} + + + +/* + +Method2: 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/229. Majority Element II.java b/Java/229. Majority Element II.java new file mode 100755 index 0000000..d9de126 --- /dev/null +++ b/Java/229. Majority Element II.java @@ -0,0 +1,118 @@ +M +tags: Array, Moore Voting +time: O(n) +space: (1) + +#### Two counters, Moore Voting +1. Moore voting: vote可以加减, 一旦为零, 换下一个candidate, 之前抵消掉的算作清零. +1. 一个array里面, 最多也只有2个数字 出现次数大于2次, 所以用A/B表示. +1. count overall apperance at the end for the two items: countA, countB +1. save to result as valA, valB +1. 有点 moore voting的意思: + - 当count == 0的时候, reset + - 两个candidate A/B都不等, 那么countA--, countB-- +1. 最终重新计数, 然后比较出结局. +1. 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +#### Sort + count +- O(nlogN) + +``` + +/* +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. +*/ + + +// O(n) time, O(1) space +class Solution { + public List majorityElement(int[] nums) { + List result = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return result; + } + int valA = 0, valB = 0, countA = 0, 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; + } +} + +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); + } + } +} + + +``` \ No newline at end of file diff --git a/Java/23. Merge k Sorted Lists.java b/Java/23. Merge k Sorted Lists.java new file mode 100755 index 0000000..7102916 --- /dev/null +++ b/Java/23. Merge k Sorted Lists.java @@ -0,0 +1,146 @@ +M +tags: Linked List, Divide and Conquer, Heap, PriorityQueue, Merge Sort +time: O(nlogk) +space: O(logk) + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### 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; } + * } + */ + +/* +Method1: Merge sort, faster +- Divide the and merge sub list, with start, end index; output listNode +- merge the 2 list node and output +- end case: start==end, return single node +- time: divide by log(k)times, each time merge n nodes => O(nlogk) +*/ +class Solution { + public ListNode mergeKLists(ListNode[] lists) { + if (lists == null || lists.length == 0) return null; + return merge(lists, 0, lists.length - 1); + } + + private ListNode merge(ListNode[] lists, int start, int end) { + if (start == end) return lists[start]; + + int mid = start + (end - start) / 2; + ListNode listA = merge(lists, start, mid); + ListNode listB = merge(lists, mid + 1, end); + + ListNode node = new ListNode(0); + ListNode dummy = node; + + while (listA != null && listB != null) { + if (listA.val <= listB.val) { + node.next = listA; + listA = listA.next; + } else { + node.next = listB; + listB = listB.next; + } + node = node.next; + } + + if (listA == null) node.next = listB; + if (listB == null) node.next = listA; + + return dummy.next; + } + + private void next(ListNode node, ListNode target) { + node.next = target; + target = target.next; + } +} + + +/* +Method2: PriorityQueue +- Put candidates into pq, to return small one first. sort: O(logk) +- skip null list once it is exhausted +- use quee to hold listNode + +overall time: O(nlogk), n total nodes +space: O(k) +*/ + +class Solution { + public ListNode mergeKLists(ListNode[] lists) { + // Initialize the priority queue with customized comparator + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(node -> node.val)); + for (ListNode node : lists) { + if (node != null) queue.offer(node); + } + + // 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; + } +} + + + + +``` \ No newline at end of file diff --git a/Java/234. Palindrome Linked List.java b/Java/234. Palindrome Linked List.java new file mode 100755 index 0000000..316ca82 --- /dev/null +++ b/Java/234. Palindrome Linked List.java @@ -0,0 +1,108 @@ +E +tags: Linked List, Two Pointers +time: O(n) +space: O(1) + +#### Reverse Linked List +- Palindrome概念很简单: 两边回溯相等. However: + - 1) cannot random access index on linkded list + - 2) cannot reverse iterating linked list +- solution: reverse linked list: 遍历接开头 + - 1) 用快慢指正找到mid point + - 2) reverse 2nd half + - 3) compare leftList and rightList +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + +``` +/* +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; + } + //1) Find middle + ListNode mid = findMiddle(head); + //2) Reverse and return right side + ListNode rightNode = reverse(mid.next); + mid.next = null; // cut off the mid connection + ListNode leftNode = head; + // 3) compare + while (leftNode != null && rightNode != null) { + if (leftNode.val != rightNode.val) return false; + leftNode = leftNode.next; + rightNode = rightNode.next; + } + return true; + } + + // odd sequence: return mid node; even sequence: return ending node of 1st half + private ListNode findMiddle(ListNode head) { + ListNode slow = head, fast = head.next; + while (fast != null && fast.next != null) { + slow = slow.next; + fast = fast.next.next; + } + return slow; + } + + private ListNode reverse(ListNode node) { + ListNode newHead = new ListNode(0); + while (node != null) { + ListNode next = node.next; //1) store node.next for iteration usage + node.next = newHead.next; // buffer newHead.next because we need to insert node after newHead + newHead.next = node; // 2) insert node right after neaHead, at starting spot. + node = next; // 3) move to next node + } + return newHead.next; + } +} + + + + +``` \ No newline at end of file diff --git a/Java/235. Lowest Common Ancestor of a Binary Search Tree.java b/Java/235. Lowest Common Ancestor of a Binary Search Tree.java new file mode 100755 index 0000000..8c2275e --- /dev/null +++ b/Java/235. Lowest Common Ancestor of a Binary Search Tree.java @@ -0,0 +1,130 @@ +E +tags: Tree, DFS, BST +time: O(logn) +space: O(logn) + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的 path +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Beofre lasts common ancestor is found: p and q should follow same search pattern: + - compare with root, then dfs(left) or dfs(right) + - util p and q fall on two sides of root, then return root + - 非常巧妙, 但是也比较局限; 稍微变条件, 就很难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(logn) extra space: recursive stack 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. + +Example 1: + +Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8 +Output: 6 +Explanation: The LCA of nodes 2 and 8 is 6. +Example 2: + +Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4 +Output: 2 +Explanation: The LCA of nodes 2 and 4 is 2, since a node can be a descendant of itself according to the LCA definition. + + +Note: + +All of the nodes' values will be unique. +p and q are different and both values will exist in the BST. + + +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; + } + List listP = new ArrayList<>(); + List listQ = new ArrayList<>(); + findNode(root, p, listP); + findNode(root, q, listQ); + int size = Math.min(listP.size(), listQ.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 side 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) { // move right + return lowestCommonAncestor(root.right, p, q); + } else if (root.val > p.val && root.val > q.val) { // move left + return lowestCommonAncestor(root.left, p, q); + } + // root is between p and q. + return root; + } +} + +``` \ No newline at end of file diff --git a/Java/236. Lowest Common Ancestor of a Binary Tree.java b/Java/236. Lowest Common Ancestor of a Binary Tree.java new file mode 100755 index 0000000..d9d5699 --- /dev/null +++ b/Java/236. Lowest Common Ancestor of a Binary Tree.java @@ -0,0 +1,77 @@ +M +tags: Tree, DFS +time: O(n) +space: O(n) + +给一个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 + - Base Case: 当root == null, p or q is found (`root == p || root == q`),那么就return the root as LCA + - 三种情况: + - 1. leftLCA and rightLCA all found: `each path has found one of p and q node as LCA`. Therefore, curr root is the lowest ancestor + - 2. One of leftLCA and rightLCA is found: return whichever one found + - 3. both LCAs are null, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time O(n), stack space O(n) + +``` +/** +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 p and q as the lowest node in T that has both p and q as descendants (where we allow a node to be a descendant of itself).” + +Given the following binary tree: root = [3,5,1,6,2,0,8,null,null,7,4] + + _______3______ + / \ + ___5__ ___1__ + / \ / \ + 6 _2 0 8 + / \ + 7 4 +Example 1: + +Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 1 +Output: 3 +Explanation: The LCA of nodes 5 and 1 is 3. +Example 2: + +Input: root = [3,5,1,6,2,0,8,null,null,7,4], p = 5, q = 4 +Output: 5 +Explanation: The LCA of nodes 5 and 4 is 5, since a node can be a descendant of itself according to the LCA definition. + +*/ + +/* +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; // p or q is found, return itself as LCA + + TreeNode leftLCA = lowestCommonAncestor(root.left, p, q); + TreeNode rightLCA = lowestCommonAncestor(root.right, p, q); + + if (leftLCA != null && rightLCA != null) return root; + else if (leftLCA != null || rightLCA != null) // found a LCA of p and q + return leftLCA != null ? leftLCA : rightLCA; + return null; + } +} + + +/* +Alternative, build dfs list +1. run dfs to find list of nodes to reach p, and the list of nodes to reach q. boolean dfs(list, p) + - return true if found + - backtrack if not found +2. compare and find last common node +time: O(n) +space: O(n) +*/ + +``` diff --git a/Java/237. Delete Node in a Linked List.java b/Java/237. Delete Node in a Linked List.java new file mode 100755 index 0000000..938d8d8 --- /dev/null +++ b/Java/237. Delete Node in a Linked List.java @@ -0,0 +1,39 @@ +E +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/238. Product of Array Except Self.java b/Java/238. Product of Array Except Self.java new file mode 100755 index 0000000..e0b6e84 --- /dev/null +++ b/Java/238. Product of Array Except Self.java @@ -0,0 +1,59 @@ +M +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的感觉有点像) + - PreProduct[i] stores product from num[0] -> num[i-1] (skipping current num[i]) + - init preProduct[i] = 1, as base for product + - 错过一位操作: always `preProduct[i] *= carry;` and `carry *= nums[i]` +- 第二轮: 从右边乘起, 每次在index i, 收到的carry都是 `nums[i+1] *....* nums[end]` + - 第一轮的结果 * 第二轮的结果, 刚好在index i 缺少掉 nums[i]. 如题所愿. +- Time: O(n) + +``` +/* +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.) +*/ + +// 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/239. Sliding Window Maximum.java b/Java/239. Sliding Window Maximum.java new file mode 100755 index 0000000..e55fb0e --- /dev/null +++ b/Java/239. Sliding Window Maximum.java @@ -0,0 +1,151 @@ +H +tags: Sliding Window, Deque, Heap +time: O(n) +space: O(n) + +#### Method1: Deque, Monotonous queue +- 维持monotonuous queue: `front is always at max` and the `tail end is min`. Always need to return the max end of queue. +- when adding new elements x: + - 1) start from small-end of the queue + - 2) drop all smaller elements + - 3) append to the ending element that is larger than x. + - This is to maintain a front->tail decreasing queue +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`: better than using arraylist, since DeQueue(linked list) removes at O(1) cost +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +- Option1: sliding window template using right/left + while loop + - 1) tailing the new number to max queue, if applicable + - 2) process: record max + - 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] +- Option2: same concept, but use index `i` to mark right, and `i - k + 1` to mark left. +- time: O(n), one pass +- space: O(k), store the deque + + +#### Method2: Heap +- can always build a `class Node{index, val}`; and sort them with PQ of size k +- time: O(nlogK) +- space: O(k) +- this is not linear time, not as good as method1 + +``` +/* +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? + +*/ +// Method1: sliding window, option2: using sliding window template +class Solution { + public int[] maxSlidingWindow(int[] nums, int k) { + if (nums == null || nums.length < k || k <= 0) return new int[0]; + int left = 0, right = 0, n = nums.length; + + int[] rst = new int[n - k + 1]; + Deque deque = new ArrayDeque<>(); // ArrayDeque is faster than LinkedList, when not removing node from middle + while (right < k - 1) inQueue(deque, nums[right++]); // add first k - 1 items + + while(right < n) { // slide window on remain items + // 1) tailing the new number to max queue, if applicable + inQueue(deque, nums[right++]); + // 2) process: record max + rst[left] = deque.peekFirst(); + // 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] + outQueue(deque, nums[left++]); + } + return rst; + } + /* + Monotonous queue: front/top = max. + Remove all smaller items from end of queue. Therefore to maintain a decreasing queue from front/top -> tail + */ + 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(); + } +} + +/* +Method1: option2 +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++) inQueue(deque, nums[i]); // add first k - 1 items + + for (int i = k - 1; i < n; i++) { // slide window on remain items + // 1) tailing the new number to max queue, if applicable + inQueue(deque, nums[i]); + // 2) process: record max + rst[i - k + 1] = deque.peekFirst(); + // 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] + outQueue(deque, nums[i - k + 1]); + } + return rst; + } + /* + monotonous queue: front/top = max. + Remove all smaller items from end of queue. Therefore to maintain a decreasing queue from front/top -> tail + */ + 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(); + } +} + + +``` \ No newline at end of file diff --git a/Java/242. Valid Anagram.java b/Java/242. Valid Anagram.java new file mode 100755 index 0000000..ef248f5 --- /dev/null +++ b/Java/242. Valid Anagram.java @@ -0,0 +1,88 @@ +E +tags: Hash Table, Sort +time: O(n) +space: O(1), unique chars + +#### int[26] + +#### HashMap + +``` +/* +Given two strings s and t , write a function to determine if t is an anagram of s. + +Example 1: + +Input: s = "anagram", t = "nagaram" +Output: true +Example 2: + +Input: s = "rat", t = "car" +Output: 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? + + +*/ +/* +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 +*/ + +/* +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[] count = countChars(s, t); + for (int i = 0; i < chars.length; i++) { + if (chars[i] != 0) return false; + } + return true; + } + + private int[] countChars(String s, String t) { + int[] count = new int[26]; + for (int i = 0; i < s.length(); i++) { + count[s.charAt(i) - 'a'] += 1; + count[t.charAt(i) - 'a'] -= 1; + } + return count; + } +} + +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; + Map count = new HashMap<>(); + countChars(s, count, 1); + countChars(t, count, -1); + + for (Map.Entry entry: count.entrySet()) { + if (entry.getValue() != 0) return false; + } + return true; + } + + private void countChars(String s, Map count, int val) { + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + count.putIfAbsent(c, 0); + count.put(c, count.get(c) + val); + } + } +} + + + +``` \ No newline at end of file diff --git a/Java/243. Shortest Word Distance.java b/Java/243. Shortest Word Distance.java new file mode 100755 index 0000000..853ffff --- /dev/null +++ b/Java/243. Shortest Word Distance.java @@ -0,0 +1,57 @@ +E +tags: Array, Two Pointers +time: O(n) +space: O(1) + + +#### Two Pointers +- Use 2 pointers to record **most recent** pos of word1 and word2 + - move pointer i [0 ~ n) and keep refreshing pos1 and pos2 + - both pos1 and pos2 will be as adjacent as possible since they both moving towards same direction +- keep recalculating best distance when either word is matched +- 而同一时间,只需要计算一个最近的curr 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) +*/ + +/* +- Use 2 pointers to record pos of word1 and word2 +- keep recalculating best distance when either word is matched +*/ +class Solution { + public int shortestDistance(String[] words, String word1, String word2) { + Integer p1 = null, p2 = null; + int min = Integer.MAX_VALUE; + for (int i = 0; i < words.length; i++) { + if (words[i].equals(word1)) p1 = i; + if (words[i].equals(word2)) p2 = i; + if (p1 != null && p2 != null) min = Math.min(min, Math.abs(p1 - p2)); + } + return min; + } +} + +``` \ No newline at end of file diff --git a/Java/244. Shortest Word Distance II.java b/Java/244. Shortest Word Distance II.java new file mode 100755 index 0000000..78d556b --- /dev/null +++ b/Java/244. Shortest Word Distance II.java @@ -0,0 +1,61 @@ +M +tags: Array, Hash Table, Design, Two Pointers +time: O(n) to build map, O(a + b) to query +space: O(n) + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + +``` +/* +Design a class which receives a list of words in the constructor, and implements a method that takes two words word1 and word2 and return the shortest distance between these two words in the list. Your method will be called repeatedly many times with different parameters. + +Example: +Assume that words = ["practice", "makes", "perfect", "coding", "makes"]. + +Input: word1 = “coding”, word2 = “practice” +Output: 3 +Input: word1 = "makes", word2 = "coding" +Output: 1 +Note: +You may assume that word1 does not equal to word2, and word1 and word2 are both in the list. +*/ + +/* +1. HashMap stores the indexes to reduce the dataset +1. Use 2 pointers do regular wordDistance calculation to find answer +time: O(n) to build map, O(a + b) for function +space: O(n) +*/ +class WordDistance { + Map> map = new HashMap<>(); + public WordDistance(String[] words) { + for (int i = 0; i < words.length; i++) { + map.putIfAbsent(words[i], new ArrayList<>()); + map.get(words[i]).add(i); + } + } + + public int shortest(String word1, String word2) { + List list1 = map.get(word1), list2 = map.get(word2); + int min = Integer.MAX_VALUE; + int i = 0, j = 0; + while (i < list1.size() && j < list2.size()) { + int p1 = list1.get(i), p2 = list2.get(j); + min = Math.min(min, Math.abs(p1 - p2)); + if (p1 < p2) i++; // move 1 occurance at a time: trying to move 2 words closer + else j++; + } + return min; + } +} + +/** + * Your WordDistance object will be instantiated and called as such: + * WordDistance obj = new WordDistance(words); + * int param_1 = obj.shortest(word1,word2); + */ +``` \ No newline at end of file diff --git a/Java/245. Shortest Word Distance III.java b/Java/245. Shortest Word Distance III.java new file mode 100755 index 0000000..6251346 --- /dev/null +++ b/Java/245. Shortest Word Distance III.java @@ -0,0 +1,97 @@ +M +tags: Array, Hash Table, Design, Two Pointers +time: O(n) +space: O(1) + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + +``` + +/* +Given a list of words and two words word1 and word2, return the shortest distance between these two words in the list. + +word1 and word2 may be the same and they represent two individual words in the list. + +Example: +Assume that words = ["practice", "makes", "perfect", "coding", "makes"]. + +Input: word1 = “makes”, word2 = “coding” +Output: 1 +Input: word1 = "makes", word2 = "makes" +Output: 3 +Note: +You may assume word1 and word2 are both in the list. +*/ + +// Built based on shortest word distance. Handle word1 == word2 case. +/** +Method1: Two Pointers, process words in 1 pass. +Follow up of 243. Shortest Word Distance I +*/ +class Solution { + public int shortestWordDistance(String[] words, String word1, String word2) { + Integer p1 = null, p2 = null; + int distance = Integer.MAX_VALUE; + for (int i = 0; i < words.length; i++) { + String word = words[i]; + + // word1 == word2 on same index. Choose old p1 or p2 operator (old p1 == old p2 anyways) + if (word.equals(word1) && word.equals(word2)) { + if (p1 != null) distance = Math.min(distance, i - p1); // i = curr index of word1/word2; p1/p2 are last index of word1/word2 + p1 = i; // move p1 to new spot + continue; + } + if (words[i].equals(word1)) p1 = i; + if (words[i].equals(word2)) p2 = i; + if (p1 != null && p2 != null) distance = Math.min(distance, Math.abs(p1 - p2)); + }// end for + return distance; + } +} + + +/* +Method2: Hash Table to store >. Follow up of 244. Shortest Word Distance II +*/ +class Solution { + + public int shortestWordDistance(String[] words, String word1, String word2) { + Map> map = new HashMap<>(); + for (int i = 0; i < words.length; i++) { + map.putIfAbsent(words[i], new ArrayList<>()); + map.get(words[i]).add(i); + } + + List list1 = map.get(word1), list2 = map.get(word2); + int min = Integer.MAX_VALUE; + int i = 0, j = 0, m = list1.size(), n = list2.size(); + while (i < m && j < n) { + int p1 = list1.get(i), p2 = list2.get(j); + if (p1 == p2) { // on same index, move i or j forward + if (i + 1 < m) i++; + else if (j + 1 <= n) j++; // at the end, move j -> n to end processing + continue; + } + // p1 != p2 + min = Math.min(min, Math.abs(p1 - p2)); + if (p1 < p2) i++; // move 1 occurance at a time: trying to move 2 words closer + else j++; + } + return min; + } +} + +``` \ No newline at end of file diff --git a/Java/246. Strobogrammatic Number.java b/Java/246. Strobogrammatic Number.java new file mode 100755 index 0000000..4eae5b0 --- /dev/null +++ b/Java/246. Strobogrammatic Number.java @@ -0,0 +1,74 @@ +E +tags: Hash Table, Two Pointers, Math, Enumeration +time: O(n) +space: O(1) + +根据题意枚举, 再根据规则basic implementation + +#### HashTable + Two Pointer +- compare left/right + +#### Alter input +- flip number (6 and 9), and then reverse the string, see if the string is the same. +- takes more + + +``` +/* +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. +*/ +public class Solution { + public boolean isStrobogrammatic(String num) { + if (num == null || num.length() == 0) return true; + HashMap map = buildMap(); + int left = 0, right = num.length() - 1; + while (left <= right) { + char charL = num.charAt(left++), charR = num.charAt(right--); + if (!map.containsKey(charR) || (charL != map.get(charR))) { + return false; + } + } + return true; + } + + private HashMap buildMap() { + HashMap map = new HashMap<>(); + map.put('0','0'); + map.put('1','1'); + map.put('8','8'); + map.put('6','9'); + map.put('9','6'); + return map; + } +} + +//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 reversedNum = sb.reverse().toString(); + return num.equals(reversedNum); + } +} +``` diff --git a/Java/252. Meeting Rooms.java b/Java/252. Meeting Rooms.java new file mode 100755 index 0000000..efd140a --- /dev/null +++ b/Java/252. Meeting Rooms.java @@ -0,0 +1,99 @@ +E +tags: Sort, Sweep Line, PriorityQueue +time: O(nlogn) +space: O(1) + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### Method1: sort input and compare if curr.end & next.start overlaps +- sort: `Arrays.sort(intervals, Comparator.comparing(i -> i[0]))` +- time: O(nlogn), space: O(1) + +#### Method2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 +- time: O(nlogn), space O(n) +- Not necessary for this problem, since it requires extra space with pq. + +``` +/* +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. + +Example 1: + +Input: [[0,30],[5,10],[15,20]] +Output: false +Example 2: + +Input: [[7,10],[2,4]] +Output: true +*/ + +/* +Method1: +- sort by first digit O(nlogn) +- and compare if end time overlaps +*/ +class Solution { + public boolean canAttendMeetings(int[][] intervals) { + if (intervals == null) return true; + Arrays.sort(intervals, Comparator.comparing(i -> i[0])); + + // Compare tail to head + for (int i = 0; i < intervals.length - 1; i++) { + // overlap happens + if (intervals[i][1] > intervals[i + 1][0]) return false; + } + return true; + } +} + +/* +Method2: Sweep line +- store all time to point(val, flag) and sort by val +- check if at any given time, count flag is >=2, meaning 2 meetings are open +- extra space O(n) +*/ +public class Solution { + class Point { + int pos, flag; + public Point(int pos, int flag) { + this.pos = pos; + this.flag = flag; + } + } + + public boolean canAttendMeetings(int[][] intervals) { + if (intervals == null) return true; + + // Prepare sweep line points + PriorityQueue queue = buildQueue(intervals); + + 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 on point x), 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; + } + + // Prepare sweep line points + private PriorityQueue buildQueue(int[][] intervals) { + PriorityQueue queue = new PriorityQueue(Comparator.comparing(p -> p.pos)); + for (int i = 0; i < intervals.length; i++) { + queue.offer(new Point(intervals[i][0], 1)); + queue.offer(new Point(intervals[i][1], -1)); + } + return queue; + } +} +``` \ No newline at end of file diff --git a/Java/253. Meeting Rooms II.java b/Java/253. Meeting Rooms II.java new file mode 100755 index 0000000..9b42e53 --- /dev/null +++ b/Java/253. Meeting Rooms II.java @@ -0,0 +1,178 @@ +M +tags: Heap, Greedy, Sort, Sweep Line, PriorityQueue +time: O(nlogn) +space: O(n) + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个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. + +Example 1: + +Input: [[0, 30],[5, 10],[15, 20]] +Output: 2 +Example 2: + +Input: [[7,10],[2,4]] +Output: 1 +NOTE: input types have been changed on April 15, 2019. Please reset to default code definition to get new method signature. +*/ + + +class Solution { + public int minMeetingRooms(int[][] intervals) { + if (intervals == null) return 0; + + int[] starts = extract(intervals, 0); + int[] ends = extract(intervals, 1); + + int count = 0, endIndex = 0; + for (int i = 0; i < starts.length; i++) { + if (starts[i] < ends[endIndex]) count++; + else endIndex++; + } + + return count; + } + + private int[] extract(int[][] intervals, int index) { + int[] array = new int[intervals.length]; + for (int i = 0; i < intervals.length; i++) { + array[i] = intervals[i][index]; + } + Arrays.sort(array); + return array; + } +} + + +/* +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 +*/ +// find # of meethings open at same time, using sweep line to count +class Solution { + class Point { + int pos, flag; + public Point(int pos, int flag) { + this.pos = pos; + this.flag = flag; + } + } + public int minMeetingRooms(int[][] intervals) { + if (intervals == null) return 0; + + // init + int count = 0, max = 0; + PriorityQueue queue = buildQueue(intervals); + + // 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; + } + + // Prepare sweep line points + private PriorityQueue buildQueue(int[][] intervals) { + PriorityQueue queue = new PriorityQueue(Comparator.comparing(p -> p.pos)); + for (int i = 0; i < intervals.length; i++) { + queue.offer(new Point(intervals[i][0], 1)); + queue.offer(new Point(intervals[i][1], -1)); + } + return queue; + } +} + +/* +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/254. Factor Combinations.java b/Java/254. Factor Combinations.java new file mode 100755 index 0000000..cd457a6 --- /dev/null +++ b/Java/254. Factor Combinations.java @@ -0,0 +1,146 @@ +M +tags: DFS, BFS, Backtracking +time: O(x), x is the # of results +space: O(y), y is all ongoing candidates in queue + + +#### Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs + + +#### Method2: BFS +- Check if the number can be devided by [2, sqrt(n)], return a list of possible factors. Only check till `Math.sqrt(n)` + - build suffixes: use the factor to devide last element of list and replace last element + - build candidate: replace last element of the queue item with new list of suffixes; add to rst + - add success item back to queue: in case last element can be simplified +- **remove dupilcates**: since we start factor from [2, sqrt(n)], the final factor list should be **ascending**!! +- time: O(x), x is the # of results +- space: O(y), y is all ongoing candidates in queue + +``` +/* +Numbers can be regarded as product of its factors. For example, + +8 = 2 x 2 x 2; + = 2 x 4. +Write a function that takes an integer n and return all possible combinations of its factors. + +Note: + +You may assume that n is always positive. +Factors should be greater than 1 and less than n. +Example 1: + +Input: 1 +Output: [] +Example 2: + +Input: 37 +Output:[] +Example 3: + +Input: 12 +Output: +[ + [2, 6], + [2, 2, 3], + [3, 4] +] +Example 4: + +Input: 32 +Output: +[ + [2, 16], + [2, 2, 8], + [2, 2, 2, 4], + [2, 2, 2, 2, 2], + [2, 4, 4], + [4, 8] +] +*/ + +/* +Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs +*/ +class Solution { + public List> getFactors(int n) { + List> rst = new ArrayList>(); + if (n <= 3) return rst; + dfs(rst, new ArrayList<>(), n, null); + return rst; + } + + public void dfs(List> rst, List list, int num, Integer lastFactor) { + if (lastFactor != null) { // it only reaches here when there is a `num % low == 0` from last level + list.add(num); + rst.add(new ArrayList<>(list)); + list.remove(list.size() - 1); + } + int high = (int) Math.sqrt(num); + int low = lastFactor == null ? 2 : Math.max(2, lastFactor); + while (low <= high) { + if (num % low == 0) { + list.add(low); + dfs(rst, list, num / low, low); + list.remove(list.size() - 1); + } + low++; + } + } +} + + +/* +Method2: BFS: +- test candidate with all possible factors +- continue with factoring the last element, replace last element of candidate and add to rst +- end state: no way to break it down further, return +*/ +class Solution { + Map>> memo = new HashMap<>(); + public List> getFactors(int n) { + List> rst = new ArrayList<>(); + Queue> queue = new LinkedList>(calcFactors(2, n)); + + while (!queue.isEmpty()) { + List list = queue.poll(); + rst.add(new ArrayList<>(list)); + + int num = list.get(list.size() - 1); + int lastFactor = list.get(list.size() - 2); // remove duplicates + List> factorList = calcFactors(lastFactor, num); // [2, 8] + if (factorList.isEmpty()) continue; + + for (List factors : factorList) { + List candidate = new ArrayList<>(list); + candidate.remove(candidate.size() - 1); + candidate.addAll(factors); + queue.offer(candidate); + } + } + + return rst; + } + + /* + Calculate factor for tail element. + Reduce duplication: start factoring from last factor, so it does not create error case: [2,2,3], [2,3,2] + */ + private List> calcFactors(int lastFactor, int num) { + List> rst = new ArrayList<>(); + int sqrt = (int)Math.sqrt(num); + for (int i = lastFactor; i <= sqrt; i++) { // prevent reuse of elements + int remain = num / i; + if (i * remain == num) rst.add(Arrays.asList(i, remain)); + } + return rst; + } +} + +``` \ No newline at end of file diff --git a/Java/256. Paint House.java b/Java/256. Paint House.java new file mode 100755 index 0000000..0d0b17d --- /dev/null +++ b/Java/256. Paint House.java @@ -0,0 +1,144 @@ +E +tags: DP, Sequence DP, Status DP +time: O(nm), m = # of colors +space: O(nm), or O(1) with rolling array + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, depends on the color of dp[n-1] + - 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- Need to have status with dp array: int[index][color status] + - dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. + - dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- time: O(nm), m = # of colors +- space: O(nm) + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 +- space:O(1) + +``` +/* +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. +*/ + +/* +Method1: DP, TopDown + rolling array +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) + +top-down: +- dp[n][color]: cost to paint n houses + - depends on the color of dp[n-1][color] +- dp[0][color] = 0; + - dp[n][0] = max(dp[n-1][1], dp[n-1][2]) + cost[n-1][0] + - dp[n][1] = ... + - dp[n][2] = ... +- return max of dp[n][0,1,2] + +*/ +// Method1: simple way to write it for this problem. +class Solution { + int R = 0, B = 1, G = 2; + 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[][] dp = new int[n + 1][3]; + dp[0][R] = dp[0][B] = dp[0][G] = 0; // house no.0 + + for (int i = 1; i <= n; i++) { + dp[i][R] = Math.min(dp[i - 1][B], dp[i - 1][G]) + costs[i-1][R]; + dp[i][B] = Math.min(dp[i - 1][R], dp[i - 1][G]) + costs[i-1][B]; + dp[i][G] = Math.min(dp[i - 1][R], dp[i - 1][B]) + costs[i-1][G]; + } + + int min = Integer.MAX_VALUE; + for (int j = 0; j < 3; j++) min = Math.min(min, dp[n][j]); + return min; + } +} + +// Rolling array: +class Solution { + public int minCost(int[][] costs) { + int n = costs.length; + int[][] dp = new int[2][3]; // rolling array + + for (int i = 1; i <= n; i++) { + int last = (i - 1) % 2, curr = i % 2; // rolling array + for (int j = 0; j < 3; j++) { // simplify j + dp[curr][j] = Math.min(dp[last][(j + 1) % 3], dp[last][(j + 2) % 3]) + costs[i-1][j]; + } + } + + return Math.min(Math.min(dp[n%2][0], dp[n%2][1]), dp[n%2][2]); + } +} + +/* +Method2: make it generic, M colors +*/ +class Solution { + public int minCost(int[][] costs) { + if (costs == null || costs.length == 0) return 0; + int n = costs.length, 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++) {// Select color j at index i + dp[i][j] = Integer.MAX_VALUE; + for (int k = 0; k < m; k++) {// Select color k at index i-1 + if (j == k) continue; // avoid same color + 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) return 0; + int n = costs.length, 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++) { + int curr = i % 2, last = (i - 1) % 2; + for (int j = 0; j < m; j++) {// Select color j at index i + dp[curr][j] = Integer.MAX_VALUE; + for (int k = 0; k < m; k++) {// Select color k at index i-1 + if (j == k) continue; // avoid same color + dp[curr][j] = Math.min(dp[curr][j], dp[last][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; + } +} +``` \ No newline at end of file diff --git a/Java/257. Binary Tree Paths.java b/Java/257. Binary Tree Paths.java new file mode 100755 index 0000000..7daaff0 --- /dev/null +++ b/Java/257. Binary Tree Paths.java @@ -0,0 +1,159 @@ +E +tags: Binary Tree, DFS, Backtracking +time: O(n) +space: O(nlogn) + + +给一个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 +- time: visit all n nodes +- space: to hold all paths, O(nlogn) + - O((n-1)/2) = O(n) nodes at leaf + - O(logn) depth + +#### 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 + + +``` +/* +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 +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +class Solution { + public List binaryTreePaths(TreeNode root) { + List rst = new ArrayList<>(); + dfs(rst, new ArrayList<>(), root); + return rst; + } + + public void dfs(List rst, List path, TreeNode node) { + if (node == null) return; + + path.add(String.valueOf(node.val)); + + if (node.left == null && node.right == null) { // leaf + rst.add(convert(path)); + path.remove(path.size() - 1); + return; + } + + dfs(rst, path, node.left); + dfs(rst, path, node.right); + + path.remove(path.size() - 1); + } + + private String convert(List path) { + return String.join("->", path); + } +} + + +/* +Previous notes + 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 +*/ + +/* + 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; + } +} +``` diff --git a/Java/259. 3Sum Smaller.java b/Java/259. 3Sum Smaller.java new file mode 100755 index 0000000..b07f22e --- /dev/null +++ b/Java/259. 3Sum Smaller.java @@ -0,0 +1,93 @@ +M +1517465602 +tags: Two Pointers, Array, Sort + +1. Similar to 15. 3Sum, but simpler. +1. 只需要count triplet, 但是不需要save triplet, 而且还不需要handle duplicated triplets +1. 发现start, end满足条件时候,(end - start)就是所有 sum target, 那么就end-- +1. 两层循环, 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/26.Remove Duplicates from Sorted Array.java b/Java/26.Remove Duplicates from Sorted Array.java new file mode 100755 index 0000000..e4956cc --- /dev/null +++ b/Java/26.Remove Duplicates from Sorted Array.java @@ -0,0 +1,117 @@ +E +1526347200 +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 + +``` +/* +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] = nums[i]; + } + } + return index + 1; // length + } +} + + +// two pointers +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; + } +} + +``` \ No newline at end of file diff --git a/Java/261. Graph Valid Tree.java b/Java/261. Graph Valid Tree.java new file mode 100755 index 0000000..6b11c5f --- /dev/null +++ b/Java/261. Graph Valid Tree.java @@ -0,0 +1,246 @@ +M +tags: DFS, BFS, Union Find, Graph + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + +``` +/* +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. + +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. + + + */ + + +/* +Method1: Union Find +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. + +Note: need to count # of unions after merging. Count should be 1 for a tree. +*/ +// 简化版 unionfind +class Solution { + int[] father; + int count; + public boolean validTree(int n, int[][] edges) { + // 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], y = edges[i][1]; + if (find(x) == find(y)) return false; // ideally, this edges[i] should be the very first time x and y node connect; however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. + union(x, y); + } + return count == 1; // no other isolated sub-graph + } + + private void union(int x, int y) { + int rootX = find(x), 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]); + } +} + +/* +Method2: 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<>(); + + // 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) { // undirected, add edge to both nodes + 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 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; + } +} + + +/* +#### Method3: BFS on adjacency list graph +- validate cycle with set: if revisit same node + - avoid infinite loop: remove curr node from child node +- validate check set size for connected +*/ +class Solution { + public boolean validTree(int n, int[][] edges) { + Map> graph = buildGraph(n, edges); + Set visited = new HashSet<>(); + + Queue queue = new LinkedList<>(); + queue.offer(0); + visited.add(0); + + while (!queue.isEmpty()) { + int node = queue.poll(); + for (int child : graph.get(node)) { + if(!visited.add(child)) return false; // exist, cycle, return false; + queue.offer(child); + graph.get(child).remove(node); // remove backward pointer to avoid infinite loop + } + } + + // 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) { // undirected, add edge to both nodes + graph.get(edge[0]).add(edge[1]); + graph.get(edge[1]).add(edge[0]); + } + return graph; + } +} + +/**************** Full-length union-find, for reading purpose, not necessary to use. *********/ +/* +How to simplify `UnionFind`? +- think of what data structure is in union-find: `parnet[]` +- just impl `parent[]` and the `union(x,y)`, `find(x)` functions as private functions in class Solution +- Take 1 step back: `UnionFind` class is just a convinient wraped up data structure :) +*/ +class Solution { + public boolean validTree(int n, int[][] edges) { + // No node, false + if (n == 0) { + return false; + } + // 1 Node without edges, true + if (n == 1 && (edges == null || edges.length == 0)) { + 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; + } +} + + +``` \ No newline at end of file diff --git a/Java/265. Paint House II.java b/Java/265. Paint House II.java new file mode 100755 index 0000000..47a6054 --- /dev/null +++ b/Java/265. Paint House II.java @@ -0,0 +1,213 @@ +H +tags: DP, Sequence DP, Status DP +time: O(NK^2): +space: O(K) with rolling array + +一排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; + if (costs.length == 1 && costs[0].length == 1) return costs[0][0]; + + int n = costs.length, k = costs[0].length; + int[][] dp = new int[n + 1][k]; //dp[0][0] = dp[0][1] = dp[0][2] ... = 0; + + for (int i = 1; i <= n; i++) { + for (int j = 0; j < k; j++) {// Select color j at index i + dp[i][j] = Integer.MAX_VALUE; + for (int m = 0; m < k; m++) {// Select color k at index i-1 + if (j == m) continue; // avoid same color + dp[i][j] = Math.min(dp[i][j], dp[i - 1][m] + costs[i - 1][j]); + } + } + } + + int min = Integer.MAX_VALUE; + for (int j = 0; j < k; j++) min = Math.min(min, dp[n][j]); + return min; + } +} + +// space optimization, rolling array, O(k) space; but slow +class Solution { + public int minCostII(int[][] costs) { + if (costs == null || costs.length == 0) return 0; + if (costs.length == 1 && costs[0].length == 1) return costs[0][0]; + + int n = costs.length, k = costs[0].length; + int[][] dp = new int[2][k]; //dp[0][0] = dp[0][1] = dp[0][2] ... = 0; + + for (int i = 1; i <= n; i++) { + for (int j = 0; j < k; j++) {// Select color j at index i + dp[i % 2][j] = Integer.MAX_VALUE; + for (int m = 0; m < k; m++) {// Select color k at index i-1 + if (j == m) continue; // avoid same color + dp[i % 2][j] = Math.min(dp[i % 2][j], dp[(i - 1) % 2][m] + costs[i - 1][j]); + } + } + } + + int min = Integer.MAX_VALUE; + for (int j = 0; j < k; j++) min = Math.min(min, dp[n % 2][j]); + return min; + } +} + +/* +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/266. Palindrome Permutation.java b/Java/266. Palindrome Permutation.java new file mode 100755 index 0000000..6411655 --- /dev/null +++ b/Java/266. Palindrome Permutation.java @@ -0,0 +1,86 @@ +E +tags: Hash Table +time: O(n) +space: O(n) + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count char occurrance + - 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assume 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 + +*/ + +/* +- count char freq +- only have <1 odd char, and the rest are even freq +- LeetCode. Made assumption on ASCII code, so use int[256] +*/ +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; + } +} + + +``` \ No newline at end of file diff --git a/Java/269. Alien Dictionary.java b/Java/269. Alien Dictionary.java new file mode 100755 index 0000000..d80fde9 --- /dev/null +++ b/Java/269. Alien Dictionary.java @@ -0,0 +1,230 @@ +H +tags: Graph, Topological Sort, DFS, BFS, Backtracking +time: O(n), n = # of graph edges +space: O(n) + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟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/270. Closest Binary Search Tree Value.java b/Java/270. Closest Binary Search Tree Value.java new file mode 100755 index 0000000..1d90559 --- /dev/null +++ b/Java/270. Closest Binary Search Tree Value.java @@ -0,0 +1,74 @@ +E +tags: Binary Search, Tree, BST +time: O(logn) +space: O(1) + +给一个BST, 和一个double target, 走位找到最接近的number. + +Concept: Iterate over all logN nodes in the BST and record the closest. Rather than finding the value at +/- 0.5 precision + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, until null leaf +- time: O(logn), space O(1) since no extra space used + +#### DFS, Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return +- time: O(logn), space: O(logn) + + +``` +/* +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 + +*/ + + +/* +Binary search, maintain a closest value. +Note: initial closest in real case is just the root, since we start from the root +*/ +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; + 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; + } +} + +/* +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; + } +} + +``` \ No newline at end of file diff --git a/Java/272. Closest Binary Search Tree Value II.java b/Java/272. Closest Binary Search Tree Value II.java new file mode 100755 index 0000000..d4b0311 --- /dev/null +++ b/Java/272. Closest Binary Search Tree Value II.java @@ -0,0 +1,139 @@ +H +tags: Stack, Tree +time: O(n) +space: O(n) + +#### Method1: Stack, DFS, Inorder Traversal +- find successors and predecessors using BST (both list will be sorted); in the end, we can easily get top k from the two sorted list + - with BST: **inorder traversal gives us sorted predecessors + - with BST: **reversed-inorder traversal gives us sorted successors + - smallest on top of the stack +- time: O(n) visit all nodes, O(k) to output +- space overall: O(n) to store all nodes + +#### Method2: BFS, brutle force +- Itereate over all nodes and maintain pq (improvemenet point: how to avoid traversing entire tree?) +- prioritize nodes that are closer to target, so we may stop early when result reaches k candidates +- time: O(n*logn) +- kinds slow and not utilizing BST + +``` +/* +Given a non-empty binary search tree and a target value, +find k values in the BST that are closest to the target. + +Note: + +Given target value is a floating point. +You may assume k is always valid, that is: k ≤ total nodes. +You are guaranteed to have only one unique set of k values in the BST that are closest to the target. +Example: + +Input: root = [4,2,5,1,3], target = 3.714286, and k = 2 + + 4 + / \ + 2 5 + / \ +1 3 + +Output: [4,3] +Follow up: +Assume that the BST is balanced, could you solve it in less than O(n) runtime (where n = total nodes)? +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +Method1: DFS + Stack +- find successors and predecessors using BST (both list will be sorted); in the end, we can easily get top k from the two sorted list + - with BST: inorder traversal gives us sorted predecessors + - with BST: reversed-inorder traversal gives us sorted successors +- time: O(n) visit all nodes, O() +- space overall: O(n) to store all nodes +*/ +class Solution { + public List closestKValues(TreeNode root, double target, int k) { + Stack predecessors = new Stack<>(), successors = new Stack<>(); + inorder(predecessors, root, target); + reverseInorder(successors, root, target); + + List list = new ArrayList<>(); + while(k-- > 0) { + if (predecessors.isEmpty()) list.add(successors.pop()); + else if (successors.isEmpty()) list.add(predecessors.pop()); + else { + int valA = predecessors.peek(), valB = successors.peek(); + if(Math.abs(valA - target) < Math.abs(valB - target)) list.add(predecessors.pop()); + else list.add(successors.pop()); + } + } + return list; + } + + // produce sorted predecessors (smallest on top of stack) + private void inorder(Stack stack, TreeNode node, double target) { + if (node == null) return; + inorder(stack, node.left, target); + if (node.val > target) return; + stack.push(node.val); + inorder(stack, node.right, target); + } + + // produce sorted successors (smallest on top of stack) + private void reverseInorder(Stack stack, TreeNode node, double target) { + if (node == null) return; + reverseInorder(stack, node.right, target); + if (node.val <= target) return; + stack.push(node.val); + reverseInorder(stack, node.left, target); + } +} + +/* +Method2: BFS +- eventuall we have to itereate over all nodes and maintain pq of size k. +- can prioritize nodes that are closer to target, so we may stop early when result reaches k +- time: O(n*logn) +- kinds slow +*/ +class Solution { + public List closestKValues(TreeNode root, double target, int k) { + PriorityQueue queue = buildQueue(k, target); + PriorityQueue rst = buildQueue(k, target); + + queue.offer(root); + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + rst.offer(node); + + if (node.left != null) queue.offer(node.left); + if (node.right != null) queue.offer(node.right); + + while(rst.size() == k) break; + } + + List list = new ArrayList<>(); + while (list.size() != k) list.add(rst.poll().val); + + return list; + } + + + private PriorityQueue buildQueue(int k, double target) { + return new PriorityQueue(k, new Comparator() { + public int compare(TreeNode a, TreeNode b) { + if (Math.abs(a.val - target) - Math.abs(b.val - target) <= 0) return -1; + return 1; + } + }); + } +} +``` \ No newline at end of file diff --git a/Java/273. Integer to English Words.java b/Java/273. Integer to English Words.java new file mode 100755 index 0000000..cf73df0 --- /dev/null +++ b/Java/273. Integer to English Words.java @@ -0,0 +1,99 @@ +H +tags: Math, String, Enumeration +time: O(n) +space: O(1) + +给一个小于 Integer.MAX_VALUE (2^31 - 1) 的数字, 转换成英语. (不需要加 'and') + +#### String +- 基本implementation +- `分类讨论`: thounsand, million, billion. `3个数字一格`. +- 用array枚举 token +- 运用 % 和 / 来找到每个分段的英语翻译 +- 3-digit 的部分, 可以用一个helper funtion来找到结果, 每段的处理方法都是一样的 +- Note: + - StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 + - 注意加 " " 的时候, 如果多余, 要`trim()` + - 注意, `小于20的数字, 有自己的特殊写法, 需要额外handle` + - 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 +- Thinking process: + - `1 ~ 19`: [one, two ... nine, ten, eleven, ...., ninteen] + - `20 ~ x0`: [twenty, thirty, fourty, ... ninety] + - `x00`: hundred: 100 + - thousand: 10^3 + - million: 10^6 + - billion: 10^9 + - trillian: 10^12 way over 2^31, not needed +- plan: + - parse 3 digits at a time + - convert the 3 digit to [xx hundred xx-ty x] + - come up with a string[] + - insert the thousands/million/billion to the string[] + +``` +/* +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(); + } +} + +``` diff --git a/Java/274.H-Index.java b/Java/274.H-Index.java new file mode 100755 index 0000000..a66f3c6 --- /dev/null +++ b/Java/274.H-Index.java @@ -0,0 +1,190 @@ +M +tags: Hash Table, Sort, Bucket Sort +time: O(n) +space: O(n) + +找到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. + +#### Couting Sort / 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 + +#### More about Counting Sort +1. Application: for number/character range +1. Steps: + - Find range, define countArray + - Count element and record in the array + - PreSum the countArray + - Start from beginning of the array, `print & decrese count` to produce the sorted elements + + +``` +/* +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: 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. +*/ + +// O(n), feel likes hashtable, where the key = citation value. +// when citation value > n, then value must > h, so put the # into bucket[n] +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 all extras + //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]; // sum = # of citation canditate who has citationCount >= h + if (sum >= h) return h; // h-index definition + } + return 0; + } +} + + +// with while loop, start from beginning, 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); + int i = 0; + // by definition, break when the first citations[i] >= h, where h = n - i + while (i < n && citations[i] < n - i) { + i++; + } + return n - i; + } +} + +// with for loop, start from end, 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); + + // by definition, break when the first citations[i] < h, where h = n - i + // to start, h = length of citation + int index = n; + for (int i = n - 1; i >= 0; i--) { + if (citations[i] >= n - i) { + index = i; + continue; + } + break; + } + + return n - index; + } +} + + +/* + 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; + } +} + + +``` diff --git a/Java/275. H-Index II.java b/Java/275. H-Index II.java new file mode 100755 index 0000000..d412ac0 --- /dev/null +++ b/Java/275. H-Index II.java @@ -0,0 +1,65 @@ +M +tags: Binary Search +time: O(logN) +space: O(1) extra + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +Aim to find the lowest index mid, which maximize h = n - mid + +#### 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 + +*/ + +/* + 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) +*/ + + +// Binary Search, O(logn), +// Aim to find the lowest index mid, which maximize h = n - mid +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; + } + } + if (citations[start] >= n - start) return n - start; + if (citations[end] >= n - end) return n - end; + return 0; + } +} + +``` diff --git a/Java/277. Find the Celebrity.java b/Java/277. Find the Celebrity.java new file mode 100755 index 0000000..7d55232 --- /dev/null +++ b/Java/277. Find the Celebrity.java @@ -0,0 +1,85 @@ +M +tags: Array, Greedy, Pruning, Adjacency Matrix, Graph +time: O(n) +space: O(1) + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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). 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. + + + +Example 1: +Input: graph = [ + [1,1,0], + [0,1,0], + [1,1,1] +] +Output: 1 +Explanation: There are three persons labeled with 0, 1 and 2. graph[i][j] = 1 means person i knows person j, otherwise graph[i][j] = 0 means person i does not know person j. The celebrity is the person labeled as 1 because both 0 and 2 know him but 1 does not know anybody. +Example 2: + + +Input: graph = [ + [1,0,1], + [1,1,0], + [0,1,1] +] +Output: -1 +Explanation: There is no celebrity. + + +Note: + +The directed graph is represented as an adjacency matrix, which is an n x n matrix where a[i][j] = 1 means person i knows person j while a[i][j] = 0 means the contrary. +Remember that you won't have direct access to the adjacency matrix. + + +*/ +/* 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; + // first pass: find 1 possible candidate k, by pruning [0~k] candidates and (k+1~n) + for (int i = 0; i < n; i++) { + if (celeb != i && knows(celeb, i)) celeb = i; + } + + // second pass: simple validate if celeb is real + 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/278. First Bad Version.java b/Java/278. First Bad Version.java new file mode 100755 index 0000000..73516b7 --- /dev/null +++ b/Java/278. First Bad Version.java @@ -0,0 +1,64 @@ +E +tags: Binary Search +time: O(logN) +space: O(1) + +#### Method1: Check is-NOT-BadVersion +- simply binary Search: if not bad, assign `start = mid+1` + +#### Method2: Check ifBadVersion +- 根据isBadVersion的性质,判断还如何end=mid or start=mid. +- A bit more code to handle + +``` +/* +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. + +Example: + +Given n = 5, and version = 4 is the first bad version. + +call isBadVersion(3) -> false +call isBadVersion(5) -> true +call isBadVersion(4) -> true + +Then 4 is the first bad version. + +*/ +/* The isBadVersion API is defined in the parent class VersionControl. + boolean isBadVersion(int version); */ +// Check is-NOT-BadVersion +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + int start = 1, end = n; + while (start + 1 < end) { + int mid = start + (end - start) / 2; + if (!isBadVersion(mid)) start = mid; + else end = mid; + } + return isBadVersion(start) ? start : end; + } +} + +// Check isBadVersion. +public class Solution extends VersionControl { + public int firstBadVersion(int n) { + if (n <= 0) return n; + int start = 0, end = n; + while (start + 1 < end) { + int mid = start + (end - start) / 2; + boolean isBad = isBadVersion(mid); + if (isBad) { + if (mid - 1 >= 1 && !isBadVersion(mid - 1)) return mid; + end = mid; + } else start = mid; + } + return isBadVersion(start) ? start : end; + } +} + +``` \ No newline at end of file diff --git a/Java/28. Implement strStr().java b/Java/28. Implement strStr().java new file mode 100755 index 0000000..c19332a --- /dev/null +++ b/Java/28. Implement strStr().java @@ -0,0 +1,58 @@ +E +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++) { // n(n) + if (n - i < m) { + return -1; + } + if (haystack.charAt(i) != needle.charAt(0)) { + continue; + } + + if (haystack.substring(i, i + m).equals(needle)) { // O(m) + return i; + } + } + return -1; + } +} + +``` \ No newline at end of file diff --git a/Java/283. Move Zeroes.java b/Java/283. Move Zeroes.java new file mode 100755 index 0000000..0891e07 --- /dev/null +++ b/Java/283. Move Zeroes.java @@ -0,0 +1,43 @@ +E +tags: Array, Two Pointers +time: O(n) +space: O(1) + +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. +*/ +/* +Pick non-zero and assign to front. Use a pointer to track +- Use pointer to move all elements to non-zero index. +- set remaining list -> 0 +*/ +class Solution { + public void moveZeroes(int[] nums) { + if (nums == null) return; + int index = 0, n = nums.length; + for (int i = 0; i < n; i++) { + if (nums[i] != 0) nums[index++] = nums[i]; + } + for (int i = index; i < n; i++) { + nums[i] = 0; + } + } +} +``` \ No newline at end of file diff --git a/Java/287. Find the Duplicate Number.java b/Java/287. Find the Duplicate Number.java new file mode 100755 index 0000000..1cb3aa9 --- /dev/null +++ b/Java/287. Find the Duplicate Number.java @@ -0,0 +1,107 @@ +M +tags: Array, Two Pointers, Binary Search, Binary Search on Value, Slow Fast Pointer, Cycle Detection +time: O(n) +space: O(1) + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + +``` +/* + +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. + +Example 1: + +Input: [1,3,4,2,2] +Output: 2 +Example 2: + +Input: [3,1,3,4,2] +Output: 3 +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. +*/ + + +/* +Method1: Use LinkedList Concept, utilize the cycle +*/ +class Solution { + public int findDuplicate(int[] nums) { + int slow = nums[0], fast = nums[nums[0]]; // starts slow=nums[0], because there is no 0 value. + while (slow != fast) { + slow = nums[slow]; + fast = nums[nums[fast]]; + } + int finder = 0; + while (slow != finder) { + slow = nums[slow]; + finder = nums[finder]; + } + return slow; + } +} + + +/* +#### Method2: Binary Search on value +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, start = 1, end = n; // as given, the [start, end] is in [1, 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/293. Flip Game.java b/Java/293. Flip Game.java new file mode 100755 index 0000000..6f1561b --- /dev/null +++ b/Java/293. Flip Game.java @@ -0,0 +1,110 @@ +E +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/295. Find Median from Data Stream.java b/Java/295. Find Median from Data Stream.java new file mode 100755 index 0000000..c9b056b --- /dev/null +++ b/Java/295. Find Median from Data Stream.java @@ -0,0 +1,77 @@ +H +tags: Heap, Design, MinHeap, MaxHeap +time: O(1) get, O(logn) addNum +space: O(n) + +#### MaxHeap/MinHeap +- 把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 + */ +/* +- use maxHeap, minHeap. +- normalize the heap size + - always fix maxHeah with the additional item +- calc: if size of 2 heaps are the same, do average. +*/ +class MedianFinder { + PriorityQueue maxHeap, minHeap; + /** initialize your data structure here. */ + public MedianFinder() { + minHeap = new PriorityQueue<>(); + maxHeap = new PriorityQueue<>(Comparator.reverseOrder()); + } + + public void addNum(int num) { + if (maxHeap.isEmpty()) maxHeap.offer(num); + else if (num <= maxHeap.peek()) maxHeap.offer(num); + else minHeap.offer(num); + // adjust: + if (maxHeap.size() > minHeap.size() + 1) { + minHeap.offer(maxHeap.poll()); + } else if (minHeap.size() > maxHeap.size()) { + maxHeap.offer(minHeap.poll()); + } + } + + public double findMedian() { + if (maxHeap.size() == minHeap.size()) { + return (maxHeap.peek() + minHeap.peek()) / 2.0; + } + 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(); + */ + +``` \ No newline at end of file diff --git a/Java/297. Serialize and Deserialize Binary Tree.java b/Java/297. Serialize and Deserialize Binary Tree.java new file mode 100755 index 0000000..8add021 --- /dev/null +++ b/Java/297. Serialize and Deserialize Binary Tree.java @@ -0,0 +1,255 @@ +H +tags: Tree, Design, DFS, BFS, Divide and Conquer, Deque +time: O(n) +space: O(n) + +Serialize and Deserialize Binary Tree + +#### DFS, Divide and Conquer, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + +``` +/** +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. + +*/ + +/** + * 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(); + appendString(root, sb); + return sb.toString(); + } + private void appendString(TreeNode node, StringBuffer sb) { + if (node == null) { + sb.append(NULL).append(DELI); + } else { + sb.append(node.val).append(DELI); + appendString(node.left, sb); + appendString(node.right, sb); + } + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + Queue queue = new LinkedList<>(Arrays.asList(data.split(DELI))); + return buildTree(queue); + } + + private TreeNode buildTree(Queue queue) { + String val = queue.poll(); + if (val.equals(NULL)) return null; + TreeNode node = new TreeNode(Integer.parseInt(val)); + node.left = buildTree(queue); + node.right = buildTree(queue); + return node; + } +} + +//BFS +public class Codec { + private final String DELI = ","; + private final String NULL = "#"; + + public String serialize(TreeNode root) { + if (root == null) return ""; + StringBuffer sb = new StringBuffer(); + Queue queue = new LinkedList<>(); + queue.offer(root); + while (!queue.isEmpty()) { + TreeNode node = queue.poll(); + if (node == null) { + sb.append(NULL).append(DELI); + continue; + } + sb.append(node.val).append(DELI); + queue.offer(node.left); + queue.offer(node.right); + } + return sb.toString(); + } + + public TreeNode deserialize(String data) { + if (data == null || data.length() == 0) return null; + Queue queue = new LinkedList<>(); + String[] strs = data.split(DELI); + TreeNode root = new TreeNode(Integer.parseInt(strs[0])); + queue.offer(root); + int i = 1; + while (i < strs.length) { + TreeNode node = queue.poll(); + String s = strs[i++]; + if (!s.equals(NULL)) { + node.left = new TreeNode(Integer.parseInt(s)); + queue.offer(node.left); + } + s = strs[i++]; + if (!s.equals(NULL)) { + node.right = new TreeNode(Integer.parseInt(s)); + queue.offer(node.right); + } + } + return root; + } +} + + + + +// DFS: Use StringBuffer instead of queue. +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); + return mid + left + right; + } + + // 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); + } +} +// Previous notes: +/** +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/299. Bulls and Cows.java b/Java/299. Bulls and Cows.java new file mode 100755 index 0000000..547bf88 --- /dev/null +++ b/Java/299. Bulls and Cows.java @@ -0,0 +1,112 @@ +M +tags: Hash Table +time: O(n) +space: O(n) + +#### Solution1: use int[10] to count frequency +1. check match chars +1. check unmatched chars by counting and offset their frequency + - count++ on secret chars: if secretCount is ever < 0 => `char g` has match, then cows++ + - count-- on guess chars: if guessCount is ever >0 => `char s` has match, then cows++ + +#### Solution2: Use hashmap to count +- Improvement: since all digit, use int[10] to count + +``` +/** +You are playing the following Bulls and Cows game with your friend: +You write down a number and ask your friend to guess what the number is. +Each time your friend makes a guess, you provide a hint that indicates how many digits +in said guess match your secret number exactly in both digit and position (called "bulls") +and how many digits match the secret number but locate in the wrong position (called "cows"). +Your friend will use successive guesses and hints to eventually derive the secret number. + +Write a function to return a hint according to the secret number and friend's guess, +use A to indicate the bulls and B to indicate the cows. + +Please note that both secret number and friend's guess may contain duplicate digits. + +Example 1: + +Input: secret = "1807", guess = "7810" + +Output: "1A3B" + +Explanation: 1 bull and 3 cows. The bull is 8, the cows are 0, 1 and 7. +Example 2: + +Input: secret = "1123", guess = "0111" + +Output: "1A1B" + +Explanation: The 1st 1 in friend's guess is a bull, the 2nd or 3rd 1 is a cow. +Note: You may assume that the secret number and your friend's guess only contain digits, and their lengths are always equal. + +*/ +// solution1 +class Solution { + public String getHint(String secret, String guess) { + if (secret == null || guess == null || secret.length() == 0 || guess.length() == 0) { + return "0A0B"; + } + + int[] nums = new int[10]; // 0 - 9 + int bulls = 0, cows = 0; + + for (int i = 0; i < secret.length(); i++) { + int s = Character.getNumericValue(secret.charAt(i)); + int g = Character.getNumericValue(guess.charAt(i)); + if (s == g) { + bulls++; + } else { + if (nums[s] < 0) cows++; + if (nums[g] > 0) cows++; + nums[s]++; + nums[g]--; + } + } + + return String.format("%sA%sB", bulls, cows); + } +} + +//solution2: Use HashMap to count +class Solution { + public String getHint(String secret, String guess) { + if (secret == null || guess == null || secret.length() == 0 || guess.length() == 0) { + return "0A0B"; + } + + char[] secretArr = secret.toCharArray(); + char[] guessArr = guess.toCharArray(); + + Map secretMap = new HashMap<>(); + Map guessMap = new HashMap<>(); + + // count matches + int bulls = 0; + for (int i = 0; i < secretArr.length; i++) { + char a = secretArr[i], b = guessArr[i]; + if (a == b) { + bulls++; + } else { + secretMap.putIfAbsent(a, 0); + secretMap.put(a, secretMap.get(a) + 1); + guessMap.putIfAbsent(b, 0); + guessMap.put(b, guessMap.get(b) + 1); + } + } + + // count miss positioned chars + int cows = 0; + for (Map.Entry entry : secretMap.entrySet()) { + char key = entry.getKey(); + if (guessMap.containsKey(key)) { + cows += Math.min(entry.getValue(), guessMap.get(key)); + } + } + + return String.format("%sA%sB", bulls, cows); + } +} +``` \ 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/301. Remove Invalid Parentheses.java b/Java/301. Remove Invalid Parentheses.java new file mode 100755 index 0000000..4f8d658 --- /dev/null +++ b/Java/301. Remove Invalid Parentheses.java @@ -0,0 +1,194 @@ +H +tags: DFS, BFS, DP + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- Goal: identify invalid parentheses and remove (minimum removals) +- Step: + - Detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` + - When invalid occurs: + - chance for correction. Remove the incorrect parentheses, one at a time + - dfs on the rest of the s that has not been tested yet: start index from index i + - Core edge cases: + - Do not correct twice of the same parenthesis by checking [j-1] pos + - Make sure to attempt correction of all possible parenthesis within tested range: because it outputs all results at the same level + - return/finish once correction done +- Success case: + - a string s passed test: make sure it passes REVERSED string test! + - Core Concept: `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**. +- How does it guarantee minimum removals? + - When seeing a chance to correct, it jumps into a for loop of DFS. It `return` after the for loop. This stops additional testing + - When invalid occurs, correct it right away: minimum correction +- Complexity: + - O(nk), k being the # of recursive calls. It takes n calls to finish a full string case. + +#### BFS +- Similar to DFS, we wnat to test: 1) test input s valid, 2) remove 1 invalid parenthesis at a time, 3) process substring +- instead of testing all substrings (timeout), we want to establish rules to improve reprocess: + - Test1: skip regular char. No need to test it. + - Test2: if redundant paren, do 1 is enough. skip adjacent ones. + - Test3: if last removed extra paren is '(', the next ')' must be a valid pair. LastRemoved char: pecial handling by using a struct: `class Node {String s, int index, char lastRemoved}` +- How to end tests? When there is data in rst, stop adding to queue. + +``` +/* +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 ). + +Example 1: + +Input: "()())()" +Output: ["()()()", "(())()"] +Example 2: + +Input: "(a)())()" +Output: ["(a)()()", "(a())()"] +Example 3: + +Input: ")(" +Output: [""] +*/ +// DFS, slight simplification +class Solution { + public List removeInvalidParentheses(String s) { + List rst = new ArrayList<>(); + if (s == null) return rst; + dfs(rst, s, 0, 0, '(', ')'); + return rst; + } + + private void dfs(List rst, String s, int i, int j, char open, char close) { + for (int count = 0; i < s.length(); i++) { + count += s.charAt(i) == open ? 1 : 0; + count -= s.charAt(i) == close ? 1 : 0; + if (count < 0) { + int init = j; + for (; j <= i; j++) { + if (s.charAt(j) == close && (j == init || s.charAt(j) != s.charAt(j - 1))) { + dfs(rst, s.substring(0, j) + s.substring(j + 1), i, j, open, close); + } + } + return; + } + } + + // s passed test + String reversed = new StringBuffer(s).reverse().toString(); + if (open == '(') { + dfs(rst, reversed, 0, 0, close, open); + } else { + rst.add(reversed); + } + } +} + + +// Original DFS +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); + } + } +} + + + +// Inspired from: https://leetcode.com/problems/remove-invalid-parentheses/discuss/75041/Java-BFS-solution-16ms-avoid-generating-duplicate-strings + +class Solution { + + private class Node { + String s; + int index; + char lastRemoved; // Smart way to maintain relationship with last recorded node + public Node(String s, int index, char lastRemoved) { + this.s = s; + this.index = index; + this.lastRemoved = lastRemoved; + } + } + + public List removeInvalidParentheses(String s) { + List rst = new ArrayList<>(); + if (validate(s)) { + rst.add(s); + return rst; + }; + + Queue q = new LinkedList<>(); + q.offer(new Node(s, 0, ')')); + + while(!q.isEmpty()) { + Node node = q.poll(); + String ss = node.s; + // Attempt to remove 1 char after failing 3 tests + for (int i = node.index; i < ss.length(); i++) { + char c = ss.charAt(i); + // Test1: skip regular char + if (c != '(' && c != ')') continue; + // Test2: if redundant paren, do 1 is enough. skip adjacent ones. + if (i != node.index && ss.charAt(i - 1) == c) continue; + // Test3: if last removed extra paren is '(', the next ')' must be a valid pair + if (node.lastRemoved == '(' && c == ')') continue; + // Generate sub and test it + String sub = ss.substring(0, i) + ss.substring(i + 1); + if (validate(sub)) rst.add(sub); + else if (rst.isEmpty()) q.offer(new Node(sub, i, c)); // only add to queue if rst empty + } + } + + return rst; + } + + // validate by testing open and close parens + private boolean validate(String s) { + int count = 0; + for (char c : s.toCharArray()) { + count += c == '(' ? 1 : 0; + count -= c == ')' ? 1 : 0; + if (count < 0) return false; + } + return count == 0; + } +} + +``` \ No newline at end of file diff --git a/Java/303. Range Sum Query - Immutable.java b/Java/303. Range Sum Query - Immutable.java new file mode 100755 index 0000000..3af2824 --- /dev/null +++ b/Java/303. Range Sum Query - Immutable.java @@ -0,0 +1,50 @@ +E +tags: DP, PreSum +time: O(1) query, O(n) setup +space: O(n) + +给一串数字, 求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 (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/304. Range Sum Query 2D - Immutable.java b/Java/304. Range Sum Query 2D - Immutable.java new file mode 100755 index 0000000..7465f9c --- /dev/null +++ b/Java/304. Range Sum Query 2D - Immutable.java @@ -0,0 +1,99 @@ +M +tags: DP, PreSum +time: O(mn) build, O(1) query +space: O(mn) + +#### Method1: rangeSum/caching +- build rangeSum[i][j]: square range sum from (0,0) to (i,j), O(mn) to init +- query: time O(1) + +#### Method2: presum/caching +- build rowPreSum[i][j]: row i sum from [0 ~ j], O(mn) to init +- callign takes O(m); space O(mn) + + +``` +/* +Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). + +Range Sum Query 2D +The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, col2) = (4, 3), which contains sum = 8. + +Example: +Given matrix = [ + [3, 0, 1, 4, 2], + [5, 6, 3, 2, 1], + [1, 2, 0, 1, 5], + [4, 1, 0, 1, 7], + [1, 0, 3, 0, 5] +] + +sumRegion(2, 1, 4, 3) -> 8 +sumRegion(1, 1, 2, 2) -> 11 +sumRegion(1, 2, 2, 4) -> 12 +Note: +You may assume that the matrix does not change. +There are many calls to sumRegion function. +You may assume that row1 ≤ row2 and col1 ≤ col2. +*/ + +/* +- Method1: rangeSum/caching +- build rangeSum[i][j]: range sum from (0,0) to (i,j), O(mn) to init +- query: timeO(1), +*/ +class NumMatrix { + int[][] rangeSum; + public NumMatrix(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) return; + int m = matrix.length, n = matrix[0].length; + rangeSum = new int[m + 1][n + 1]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + rangeSum[i + 1][j + 1] = rangeSum[i + 1][j] + rangeSum[i][j+1] + matrix[i][j] - rangeSum[i][j]; + } + } + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + int sum = 0; + sum += rangeSum[row2+1][col2+1] - rangeSum[row1][col2+1] - rangeSum[row2+1][col1] + + rangeSum[row1][col1]; + return sum; + } +} + + +/* +- Method2: presum/caching +- build rowPreSum[i][j]: row i sum from [0 ~ j], O(mn) to init +- callign takes O(m); space O(mn) +*/ +class NumMatrix { + int[][] preSum; + public NumMatrix(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) return; + int m = matrix.length, n = matrix[0].length; + preSum = new int[m][n + 1]; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + preSum[i][j + 1] = preSum[i][j] + matrix[i][j]; + } + } + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + int sum = 0; + for (int i = row1; i <= row2; i++) { + sum += preSum[i][col2 + 1] - preSum[i][col1]; + } + return sum; + } +} + +/** + * Your NumMatrix object will be instantiated and called as such: + * NumMatrix obj = new NumMatrix(matrix); + * int param_1 = obj.sumRegion(row1,col1,row2,col2); + */ +``` \ No newline at end of file diff --git a/Java/305. Number of Islands II.java b/Java/305. Number of Islands II.java new file mode 100755 index 0000000..02ee9ef --- /dev/null +++ b/Java/305. Number of Islands II.java @@ -0,0 +1,313 @@ +H +tags: Union Find +time: O(k * log(mn)) +space: O(mn) + +给一个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] + +*/ + +/* +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], y = positions[i][1]; + if (grid[x][y] == 0) { // no need to fill + grid[x][y] = 1; + unionFind.increaseCount(); + for (int j = 0; j < dx.length; j++) { + int movedX = x + dx[j], 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[] rank; + int count; + + public UnionFind(int x) { + father = new int[x]; + rank = 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), rootY = find(y); + if (rootX != rootY) { + if (rank[rootX] > rank[rootY]) father[rootY] = rootX; + else if (rank[rootX] < rank[rootY]) father[rootX] = rootY; + else { + father[rootY] = rootX; + rank[rootX]++; + } + 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; + } + +} + +/* +DFS times out +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/307. Range Sum Query - Mutable.java b/Java/307. Range Sum Query - Mutable.java new file mode 100755 index 0000000..59f6f19 --- /dev/null +++ b/Java/307. Range Sum Query - Mutable.java @@ -0,0 +1,101 @@ +M +tags: Segment Tree, Binary Indexed Tree +time: build O(n), query (logn +k), update O(logn) +space: O(n) + +#### Segment Tree, devide and conquer +- sample problem for segment tree +- build(), update(), rangeQuery() + - build and update are standard + - rangeQuery: handle the range split check +- Null leaf node handling: NO, ideally it will not encounter null leaf. + - in update/rangeQuery: when final state (`start==end`) is reached, the recursive call ends + - there is no way for any node to dive futher into null child. +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. + +``` + +/* +Given an integer array nums, find the sum of the elements between indices i and j (i ≤ j), inclusive. + +The update(i, val) function modifies nums by updating the element at index i to val. + +Example: + +Given nums = [1, 3, 5] + +sumRange(0, 2) -> 9 +update(1, 2) +sumRange(0, 2) -> 8 +Note: + +The array is only modifiable by the update function. +You may assume the number of calls to update and sumRange function is distributed evenly. + +*/ + +class NumArray { + SegTreeNode segTree; + public NumArray(int[] nums) { + if (nums == null || nums.length == 0) return; + segTree = build(nums, 0, nums.length - 1); + } + + public void update(int i, int val) { + updateNode(segTree, i, val); + } + + public int sumRange(int i, int j) { + return rangeQuery(segTree, i, j); + } + + class SegTreeNode { + int start, end, sum; + SegTreeNode left, right; + public SegTreeNode(int start, int end, int sum, SegTreeNode left, SegTreeNode right) { + this.start = start; + this.end = end; + this.sum = sum; + this.left = left; + this.right = right; + } + } + + private SegTreeNode build(int[] nums, int start, int end) { + if (start == end) return new SegTreeNode(start, end, nums[start], null, null); + int mid = start + (end - start) / 2; + SegTreeNode left = build(nums, start, mid); + SegTreeNode right = build(nums, mid + 1, end); + return new SegTreeNode(start, end, left.sum + right.sum, left, right); + } + + private void updateNode(SegTreeNode node, int i, int val) { + if (node.start == i && node.end == i) { + node.sum = val; + return; + } + if (i <= node.left.end) updateNode(node.left, i, val); + else updateNode(node.right, i, val); + node.sum = node.left.sum + node.right.sum; + } + + private int rangeQuery(SegTreeNode node, int i, int j) { + if (node.start == i && node.end == j) return node.sum; + if (j <= node.left.end) return rangeQuery(node.left, i, j); + else if (i >= node.right.start) return rangeQuery(node.right, i, j); + int mid = node.start + (node.end - node.start) / 2; + return rangeQuery(node.left, i, mid) + rangeQuery(node.right, mid + 1, j); + } + +} + +/** + * Your NumArray object will be instantiated and called as such: + * NumArray obj = new NumArray(nums); + * obj.update(i,val); + * int param_2 = obj.sumRange(i,j); + */ +``` \ No newline at end of file diff --git a/Java/308. Range Sum Query 2D - Mutable.java b/Java/308. Range Sum Query 2D - Mutable.java new file mode 100755 index 0000000..32f77c7 --- /dev/null +++ b/Java/308. Range Sum Query 2D - Mutable.java @@ -0,0 +1,143 @@ +H +tags: Segment Tree, Binary Indexed Tree +time: build(n), update(logn), rangeRuery(logn + k) +space: O(n) + +#### Segment Tree +- Same concept as turning an array into a binary segment tree, + - HOWEVER, this is a 4-nary segmenet tree +- Reference. 307 Range Sum Query +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. +- Handling end stage, there are two approaches: + - ApproachA: check at beginning of recursive call (i.e in `build()`, `updateNode()`, `rangeQuery()`). + - pro: calling recursive function blindly; code is easy. + - con: be really clear about termination state, and catch it. + - ApproachB: check & come up with correct query condition before recursive call + - pro: input to recursive function is assumed to be correct + - con: sometimes really hard to write the conditions before recursive call; code is hard. + +``` + +/* + +Given a 2D matrix matrix, find the sum of the elements inside the rectangle defined by its upper left corner (row1, col1) and lower right corner (row2, col2). + +Range Sum Query 2D +The above rectangle (with the red border) is defined by (row1, col1) = (2, 1) and (row2, col2) = (4, 3), which contains sum = 8. + +Example: +Given matrix = [ + [3, 0, 1, 4, 2], + [5, 6, 3, 2, 1], + [1, 2, 0, 1, 5], + [4, 1, 0, 1, 7], + [1, 0, 3, 0, 5] +] + +sumRegion(2, 1, 4, 3) -> 8 +update(3, 2, 2) +sumRegion(2, 1, 4, 3) -> 10 +Note: +The matrix is only modifiable by the update function. +You may assume the number of calls to update and sumRegion function is distributed evenly. +You may assume that row1 ≤ row2 and col1 ≤ col2. + +*/ + +class NumMatrix { + class SegTreeNode { + int row1, row2, col1, col2, sum; + SegTreeNode topL, topR, downL, downR; + public SegTreeNode(int row1, int col1, int row2, int col2, int sum) { + this.row1 = row1; + this.col1 = col1; + this.row2 = row2; + this.col2 = col2; + this.sum = sum; + } + } + // nice wrapper class to simplify query. + class QueryNode { + int row1, row2, col1, col2; + public QueryNode(int row1, int col1, int row2, int col2) { + this.row1 = row1; + this.col1 = col1; + this.row2 = row2; + this.col2 = col2; + } + } + + SegTreeNode root; + public NumMatrix(int[][] matrix) { + if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) return; + root = build(matrix, 0, 0, matrix.length - 1 , matrix[0].length - 1); + } + + public void update(int row, int col, int val) { + updateNode(root, row, col, val); + } + + public int sumRegion(int row1, int col1, int row2, int col2) { + return rangeQuery(root, new QueryNode(row1, col1, row2, col2)); + } + + private SegTreeNode build(int[][] matrix, int row1, int col1, int row2, int col2) { + if (row2 < row1 || col2 < col1) return null; + if (row1 == row2 && col1 == col2) return new SegTreeNode(row1, col1, row2, col2, matrix[row1][col1]); + int rowMid = row1 + (row2 - row1) / 2, colMid = col1 + (col2 - col1) / 2; + SegTreeNode node = new SegTreeNode(row1, col1, row2, col2, 0); + node.topL = build(matrix, row1, col1, rowMid, colMid); + node.topR = build(matrix, row1, colMid + 1, rowMid, col2); + node.downL = build(matrix, rowMid + 1, col1, row2, colMid); + node.downR = build(matrix, rowMid + 1, colMid + 1, row2, col2); + node.sum = sum(node); + return node; + } + + private void updateNode(SegTreeNode node, int row, int col, int val) { + if (node == null) return; + if (row < node.row1 || row > node.row2 || col < node.col1 || col > node.col2) return; // target (row, col) fall out of range; end call + if (node.row1 == row && node.row2 == row && node.col1 == col && node.col2 == col) { // direct match + node.sum = val; + return; + } + updateNode(node.topL, row, col, val); + updateNode(node.topR, row, col, val); + updateNode(node.downL, row, col, val); + updateNode(node.downR, row, col, val); + node.sum = sum(node); + } + + private int rangeQuery(SegTreeNode node, QueryNode queryNode) { + if (node == null) return 0; + if (node.row2 < queryNode.row1 || node.row1 > queryNode.row2 + || node.col2 < queryNode.col1 || node.col1 > queryNode.col2) // segment tree node falls out of target range; end call + return 0; + if (node.row1 >= queryNode.row1 && node.row2 <= queryNode.row2 + && node.col1 >= queryNode.col1 && node.col2 <= queryNode.col2) // segment tree node is surrounded by range, return the node.sum + return node.sum; + return rangeQuery(node.topL, queryNode) + rangeQuery(node.topR, queryNode) + + rangeQuery(node.downL, queryNode) + rangeQuery(node.downR, queryNode); + + } + + private int sum(SegTreeNode node) { + int sum = 0; + sum += node.topL == null ? 0 : node.topL.sum; + sum += node.topR == null ? 0 : node.topR.sum; + sum += node.downL == null ? 0 : node.downL.sum; + sum += node.downR == null ? 0 : node.downR.sum; + return sum; + } +} + +/** + * Your NumMatrix object will be instantiated and called as such: + * NumMatrix obj = new NumMatrix(matrix); + * obj.update(row,col,val); + * int param_2 = obj.sumRegion(row1,col1,row2,col2); + */ +``` \ No newline at end of file diff --git a/Java/31. Next Permutation.java b/Java/31. Next Permutation.java new file mode 100755 index 0000000..70d5ea4 --- /dev/null +++ b/Java/31. Next Permutation.java @@ -0,0 +1,83 @@ +M +tags: Array, Permutation +time: O(n) +space: O(1) + +#### Permutation Behavior +- Great write up: https://leetcode.com/problems/next-permutation/solution/ +- next lexicographically permutation: `smallest` but `larger than curr` permutation: + - find first low point from right [low] + - find the slight larger [high] to swap with [low] + - make sure right side of low is eventually the smallest +- Analyze the use cases, to find next low permutation, 2 major steps: + - 1) Find `first low/drop candidate` from right + - 2) Find `first high where nums[high] > nums[low]` from right + - 3) swap(low, high). + - By now, [low, n-1] forms a greater permutation + - but it is not the smallest, because right side [low + 1, n - 1] is descending + - 4) reverse(low + 1, n-1) to create ascending slopt on right of low (smallest next lexicographically permutation) +- Corner case: if input array is decending (1st low not found), reverse it all together O(n) +- time: O(n) visit all indexes +- space: O(1) not using additional +- Similar question: `1053. Previous Permutation With One Swap` + + +``` +/* +Implement next permutation, which rearranges numbers into the lexicographically next greater permutation of numbers. + +If such arrangement is not possible, it must rearrange it as the lowest possible order (ie, sorted in ascending order). + +The replacement must be in-place and use only constant extra memory. + +Here are some examples. Inputs are in the left-hand column and its corresponding outputs are in the right-hand column. + +1,2,3 → 1,3,2 +3,2,1 → 1,2,3 +1,1,5 → 1,5,1 +*/ +class Solution { + public void nextPermutation(int[] nums) { + if (nums == null || nums.length <= 1) return; + int n = nums.length; + int low = findLow(nums); + if (low == -1) reverse(nums, 0 , n - 1); // descending array, reverse all + else { + int high = findHigh(nums, low); + swap(nums, low, high); + reverse(nums, low + 1, n - 1); // reverse just right side to create min slop + } + } + + private int findLow(int[] nums) { + int end = nums.length - 1; + while (end > 0) { + if (nums[end - 1] < nums[end]) return end - 1; + end--; + } + return -1; + } + + private int findHigh(int[] nums, int low) { + int end = nums.length - 1; + while (end > low) { + if (nums[end] > nums[low]) return end; + end--; + } + return -1; + } + + private void reverse(int[] nums, int start, int end) { + while (start < end) { + swap(nums, start++, end--); + } + } + + private void swap(int[] nums, int a, int b) { + int temp = nums[a]; + nums[a] = nums[b]; + nums[b] = temp; + } +} + +``` \ No newline at end of file diff --git a/Java/311. Sparse Matrix Multiplication.java b/Java/311. Sparse Matrix Multiplication.java new file mode 100755 index 0000000..594c529 --- /dev/null +++ b/Java/311. Sparse Matrix Multiplication.java @@ -0,0 +1,114 @@ +M +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 +class Solution { + public int[][] multiply(int[][] A, int[][] B) { + if (validate(A, B)) return new int[][]{}; + + // iterate over A, B + int aRow = A.length, bCol = B[0].length, bRow = B.length; + int[][] rst = new int[aRow][bCol]; + + // base loop, reduce + for (int i = 0; i < aRow; i++) { + for (int j = 0; j < bCol; j++) { + rst[i][j] += multiple(A, B, i, j); + } + } + + return rst; + } + + private int multiple(int[][] A, int[][] B, int row, int col) { + int sum = 0; + for (int i = 0; i < B.length; i++) sum += A[row][i] * B[i][col]; + return sum; + } + + 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/314. Binary Tree Vertical Order Traversal.java b/Java/314. Binary Tree Vertical Order Traversal.java new file mode 100755 index 0000000..159e80c --- /dev/null +++ b/Java/314. Binary Tree Vertical Order Traversal.java @@ -0,0 +1,224 @@ +M +tags: BFS, DFS, Hash Table, Tree +time: O(n) +space: O(n) + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + +``` +/* +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 offset; + TreeNode treeNode; + public Node (int offset, TreeNode treeNode) { + this.offset = offset; + 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(); + int offset = node.offset; + map.putIfAbsent(offset, new ArrayList<>()); + map.get(offset).add(node.treeNode.val); + + if (node.treeNode.left != null) queue.offer(new Node(offset - 1, node.treeNode.left)); + if (node.treeNode.right != null) queue.offer(new Node(offset + 1, node.treeNode.right)); + + min = Math.min(min, offset); + max = Math.max(max, offset); + } + } + + for (int offset = min; offset <= max; offset++) { + if (map.containsKey(offset)) rst.add(map.get(offset)); + } + + 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 +*/ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +// DFS and add offset to a map, record global min,max, pick list in order (small -> large offset) +// DFS and add offset to a map, record global min,max, pick list in order (small -> large offset) +class Solution { + class Node { + int val, level; + public Node (int val, int level) { + this.val = val; + this.level = level; + } + } + int min = 0, max = 0; + public List> verticalOrder(TreeNode root) { + Map> map = new HashMap<>(); + dfs(map, root, 0, 0); + + List> rst = new ArrayList<>(); + for (int offset = min; offset <= max; offset++) { + if (map.containsKey(offset)) { + List list = map.get(offset); + list.sort(Comparator.comparing(a -> a.level)); + rst.add(output(list)); + } + } + return rst; + } + + public void dfs(Map> map, TreeNode node, int offset, int level) { + if (node == null) return; + map.putIfAbsent(offset, new ArrayList<>()); + map.get(offset).add(new Node(node.val, level)); + + max = Math.max(max, offset); + min = Math.min(min, offset); + + dfs(map, node.left, offset - 1, level + 1); + dfs(map, node.right, offset + 1, level + 1); + } + + private List output(List list) { + List rst = new ArrayList<>(); + for (Node node : list) { + rst.add(node.val); + } + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/315. Count of Smaller Numbers After Self.java b/Java/315. Count of Smaller Numbers After Self.java new file mode 100755 index 0000000..51d2010 --- /dev/null +++ b/Java/315. Count of Smaller Numbers After Self.java @@ -0,0 +1,228 @@ +H +tags: BST, Binary Search, Divide and Conquer, Binary Indexed Tree, Segment Tree +time: O(nlogn) +space: O(n) + +给一串数字nums[], 求一个新数组result, where result[i] = # of smaller items on right of nums[i] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + +``` +/* +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 +*/ + +/* +- Method1: binary Search: + - process from rightside: find the first item nums[i] > target: i should be the # to record + - and add to sorted position: O(nlogn) +*/ +class Solution { + public List countSmaller(int[] nums) { + List rst = new ArrayList<>(); + List list = new ArrayList<>(); + if (nums == null || nums.length == 0) return rst; + + // 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 biggerIndex = search(list, nums[i]); + rst.add(0, biggerIndex); + list.add(biggerIndex, nums[i]); + } + return rst; + } + + // find firt index i, where num[i] > target; that's the place to insert nums[i] in sorted list + private int search(List list, int target) { + if (list.size() == 0) return 0; + int start = 0, end = list.size() - 1; + if (target <= list.get(start)) return start; + if (target > list.get(end)) return end + 1; + while (start + 1 < end) { + int mid = start + (end - start) / 2; + int num = list.get(mid); + if (num < target) start = mid; + else end = mid; // num >= end + } + if (target <= list.get(start)) return start; + return end; + } +} + +// Method2: SegmentTree +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; + // Find range + int[] range = findRange(nums); + int n = nums.length, min = range[0], max = range[1], offset = range[2]; + + // build tree + root = build(min, max); + for (int num : nums) modify(root, num + offset, 1); + + // remove curr element, and query for all remaining elements + for (int num : nums) { + modify(root, num + offset, -1); + result.add(query(root, min, num + offset - 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); + } + + private int[] findRange(int[] nums) { + int diff = 0, min = Integer.MAX_VALUE, max = Integer.MIN_VALUE; + 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; + } + return new int[]{min, max, diff}; + } +} + + +// Method3: Binary Search 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 node.val == val + return node.left == null ? 0 : node.left.smallerCount; + } +} + + + + +``` \ No newline at end of file diff --git a/Java/319. Bulb Switcher.java b/Java/319. Bulb Switcher.java new file mode 100755 index 0000000..986115e --- /dev/null +++ b/Java/319. Bulb Switcher.java @@ -0,0 +1,41 @@ +M +tags: Math, Brainteaser +time: O(1) +space: O(1) + +#### Brainteaser +- https://leetcode.com/problems/bulb-switcher/discuss/77104/Math-solution.. + +#### Brutle: +- if just impl, it take O(n^2): +- repating: some pos are toggled mutiple times: if we know total times, easy to determin each pos. +- loop over [2, n], count times on each index + +``` + +/* + +There are n bulbs that are initially off. You first turn on all the bulbs. Then, you turn off every second bulb. On the third round, you toggle every third bulb (turning on if it's off or turning off if it's on). For the i-th round, you toggle every i bulb. For the n-th round, you only toggle the last bulb. Find how many bulbs are on after n rounds. + +Example: + +Input: 3 +Output: 1 +Explanation: +At first, the three bulbs are [off, off, off]. +After first round, the three bulbs are [on, on, on]. +After second round, the three bulbs are [on, off, on]. +After third round, the three bulbs are [on, off, off]. + +So you should return 1, because there is only one bulb is on. +*/ + +/* +Brain teaser +*/ +class Solution { + public int bulbSwitch(int n) { + return (int)Math.sqrt(n); + } +} +``` \ No newline at end of file diff --git a/Java/322. Coin Change.java b/Java/322. Coin Change.java new file mode 100755 index 0000000..f73e568 --- /dev/null +++ b/Java/322. Coin Change.java @@ -0,0 +1,97 @@ +M +tags: DP, Backpack DP, Memoization, DFS +time: O(n * S) +space: O(S) + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + +``` +/* +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. + +*/ + +/* +Method1: Bottom-Up DP +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) { + 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 (coin > i || dp[i - coin] == Integer.MAX_VALUE) continue; // coin too big, or last spot was invalid + dp[i] = Math.min(dp[i], dp[i - coin] + 1); + } + } + return dp[amount] == Integer.MAX_VALUE ? -1 : dp[amount]; + } +} + +/* +Method2: memoization, DFS +Pick one coin => amount - coin => coinChange(coins, amount - coin). +- memo[i]: min # for amount i. +- create memo[amount + 1] +- make memo global and memorize results +*/ +class Solution { + int[] memo; + public int coinChange(int[] coins, int amount) { + if (coins == null || coins.length == 0) return - 1; + memo = new int[amount + 1]; + for (int i = 1; i <= amount; i++) memo[i] = Integer.MAX_VALUE; + return dfs(coins, amount); + } + + private int dfs(int[] coins, int amount) { + if (amount < 0) return -1; + if (memo[amount] != Integer.MAX_VALUE) return memo[amount]; + + for (int coin : coins) { + int count = dfs(coins, amount - coin); + if (count >= 0) memo[amount] = Math.min(memo[amount], count + 1); + } + return memo[amount] = memo[amount] == Integer.MAX_VALUE ? -1 : memo[amount]; + } +} + +``` \ No newline at end of file diff --git a/Java/327. Count of Range Sum.java b/Java/327. Count of Range Sum.java new file mode 100755 index 0000000..52ba1e9 --- /dev/null +++ b/Java/327. Count of Range Sum.java @@ -0,0 +1,154 @@ +H +tags: Divide and Conquer, Merge Sort, BIT, PreSum, Segment Tree +time: O(nlogn) +space: O(n) + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + +``` +/* +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) { + if (nums == null || nums.length <= 0) return 0; + 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, count = 0; + + // sort two sides + count += mergeSort(preSum, lower, upper, start, mid); + count += mergeSort(preSum, lower, upper, mid, end); + + // find the lower/upper bound index range. we know range[start, mid] and [mid, end] has been sorted earlier + int lo = mid, hi = mid; + for (int i = start; i < mid; i++) { + while (lo < end && preSum[lo] - preSum[i] < lower) lo++; + while (hi < end && preSum[hi] - preSum[i] <= upper) hi++; + count += hi - lo; + } + + // 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; + } +} + + + +// Same impl, but wtih a customized merge function. Reference. [tool].MergeSort.java +class Solution { + long[] cache; + public int countRangeSum(int[] nums, int lower, int upper) { + if (nums == null || nums.length <= 0) return 0; + cache = new long[nums.length + 1]; + 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, count = 0; + + // sort two sides + count += mergeSort(preSum, lower, upper, start, mid); + count += mergeSort(preSum, lower, upper, mid, end); + + // find the lower/upper bound index range. we know range[start, mid] and [mid, end] has been sorted earlier + int lo = mid, hi = mid; + for (int i = start; i < mid; i++) { + while (lo < end && preSum[lo] - preSum[i] < lower) lo++; + while (hi < end && preSum[hi] - preSum[i] <= upper) hi++; + count += hi - lo; + } + + // merge two list for range [start, end) + merge(preSum, mid - 1, start, 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; + } + + private void merge(long[] nums, int mid, int start, int end) { + int i = start, j = mid + 1, index = start; + + while (i <= mid && j <= end) { + if (nums[i] <= nums[j]) cache[index++] = nums[i++]; + else cache[index++] = nums[j++]; + } + + // append the remaining array. + // arraycopy: copy from array[i] to cache[index] for (x) items + System.arraycopy(nums, i, cache, index, mid - i + 1); // copy remaining of left segment (in case it didn't reach end) + System.arraycopy(nums, j, cache, index, end - j + 1); // copy remaining of right segment (in case it didn't reach end) + System.arraycopy(cache, start, nums, start, end - start + 1); // copy whole cache[start,end] to original + } +} +``` \ No newline at end of file diff --git a/Java/33. Search in Rotated Sorted Array.java b/Java/33. Search in Rotated Sorted Array.java new file mode 100755 index 0000000..b3e024d --- /dev/null +++ b/Java/33. Search in Rotated Sorted Array.java @@ -0,0 +1,141 @@ +M +tags: Array, Binary Search +time: O(logn) +space: O(1) + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 + - 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` + - 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- Binary search template: + - 1) `start + 1 < end` (adjacent indexes) + - 2) start/end = mid, + - 3) compare start and end individually + +#### 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]) end = mid; + else start = mid; + } else { //Land in 2nd continous section + if (nums[mid] <= target && target <= nums[end]) 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/332. Reconstruct Itinerary.java b/Java/332. Reconstruct Itinerary.java new file mode 100755 index 0000000..f101eba --- /dev/null +++ b/Java/332. Reconstruct Itinerary.java @@ -0,0 +1,101 @@ +M +tags: Graph, DFS, Backtracking +time: O(n^n) +space: O(m) + +#### DFS with backtcking +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +- time: O(n^n). n = # of cities. worst case, each city has (n-1) edges and need to try all combinations +- space: O(n^2), there can at most be n * (n - 1) edges + +``` +/* +Given a list of airline tickets represented by pairs of departure and arrival airports [from, to], reconstruct the itinerary in order. All of the tickets belong to a man who departs from JFK. Thus, the itinerary must begin with JFK. + +Note: + +If there are multiple valid itineraries, you should return the itinerary that has the smallest lexical order when read as a single string. For example, the itinerary ["JFK", "LGA"] has a smaller lexical order than ["JFK", "LGB"]. +All airports are represented by three capital letters (IATA code). +You may assume all tickets form at least one valid itinerary. +Example 1: + +Input: [["MUC", "LHR"], ["JFK", "MUC"], ["SFO", "SJC"], ["LHR", "SFO"]] +Output: ["JFK", "MUC", "LHR", "SFO", "SJC"] +Example 2: + +Input: [["JFK","SFO"],["JFK","ATL"],["SFO","ATL"],["ATL","JFK"],["ATL","SFO"]] +Output: ["JFK","ATL","JFK","SFO","ATL","SFO"] +Explanation: Another possible reconstruction is ["JFK","SFO","ATL","JFK","ATL","SFO"]. + But it is larger in lexical order. +*/ + +/* +- Construct map> based on input, and DFS +- Order the items by name; +- remove edge after use; dfs and backtrack +*/ +/* +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +*/ +class Solution { + int n = 0; + public List findItinerary(List> tickets) { + + List rst = new LinkedList<>(); + Set citySet = new HashSet<>(); + Map> map = buildMap(tickets); + n = tickets.size(); + rst.add("JFK"); + dfs(rst, map, "JFK"); + return rst; + } + + private boolean dfs(List list, Map> map, String curr) { + if (list.size() == n + 1) return true; + if (!map.containsKey(curr)) return false; + List destinations = map.get(curr); + for (int i = 0; i < destinations.size(); i++) { + String next = destinations.get(i); + destinations.remove(i); + list.add(next); + + if (dfs(list, map, next)) return true; + + // backtrack + destinations.add(i, next); + list.remove(list.size() - 1); + } + return false; + } + + private Map> buildMap(List> tickets) { + Map> map = new HashMap<>(); + for (List ticket : tickets) { + map.putIfAbsent(ticket.get(0), new LinkedList<>()); + map.get(ticket.get(0)).add(ticket.get(1)); + } + for (String key : map.keySet()) Collections.sort(map.get(key)); + return map; + } +} +``` \ No newline at end of file diff --git a/Java/339. Nested List Weight Sum.java b/Java/339. Nested List Weight Sum.java new file mode 100755 index 0000000..a4b93aa --- /dev/null +++ b/Java/339. Nested List Weight Sum.java @@ -0,0 +1,115 @@ +E +tags: DFS, BFS, NestedInteger +time: O(n) +space: O(h), h = levels + +给一串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. + - 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) + +#### BFS +- bfs, queue, 处理queue.size() for a level +- use a level variable to track levels +- slower since it uses extra space, worst case O(n) of all items + +``` +/* +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]] + 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; + 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]] + Queue queue = new LinkedList<>(); + addToQueue(queue, nestedList); + + int level = 1, 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 addToQueue(queue, childInt.getList()); + } + level++; + } + return sum; + } + + private void addToQueue(Queue queue, List nestedList) { + for (NestedInteger childInt: nestedList) { + queue.offer(childInt); + } + } +} +``` diff --git a/Java/34. Find First and Last Position of Element in Sorted Array.java b/Java/34. Find First and Last Position of Element in Sorted Array.java new file mode 100755 index 0000000..0aa004e --- /dev/null +++ b/Java/34. Find First and Last Position of Element in Sorted Array.java @@ -0,0 +1,86 @@ +M +tags: Array, Binary Search +time: O(logn) +space: O(1) + +#### Binary Search +- need search left bound & right bound. +- use input parameter `direction` to binary search function +- Option0: simplification, inspired by `278. First Bad Version - Method1: Check is-NOT-BadVersion` + - 1) if found match, but NOT sure it is desired boundary, just leave it and keep going + - 2) check the final results after `binary search while loop` completes + - WHY? code is easier to read in this way. + +``` +/* +Given an array of integers nums sorted in ascending order, 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 1: + +Input: nums = [5,7,7,8,8,10], target = 8 +Output: [3,4] +Example 2: + +Input: nums = [5,7,7,8,8,10], target = 6 +Output: [-1,-1] +*/ + +// Option0: Simplification +class Solution { + public int[] searchRange(int[] nums, int target) { + if (nums == null || nums.length == 0) return new int[]{-1, -1}; + return new int[]{ + binarySearch(nums, target, true), + binarySearch(nums, target, false) + }; + } + + private int binarySearch(int[] nums, int target, boolean isLeft) { + int n = nums.length, start = 0, end = n - 1; + while (start + 1 < end) { + int mid = start + (end - start) / 2, val = nums[mid]; + if (val == target) { + if (isLeft) end = mid; + else start = mid; + } else if (val < target) start = mid; + else end = mid; + } + if (nums[start] == target && nums[end] == target) return isLeft ? start : end; + if (nums[start] == target) return start; + if (nums[end] == target) return end; + return -1; + } +} + +// Option1: original, check end state in middle +class Solution { + public int[] searchRange(int[] nums, int target) { + if (nums == null || nums.length == 0) return new int[]{-1, -1}; + return new int[]{ + binarySearch(nums, target, true), + binarySearch(nums, target, false) + }; + } + + private int binarySearch(int[] nums, int target, boolean isLeft) { + int n = nums.length, start = 0, end = n - 1; + while (start + 1 < end) { + int mid = start + (end - start) / 2, val = nums[mid]; + if (val == target) { + if (isLeft && mid - 1 >= 0 && nums[mid - 1] == target) end = mid; + else if (!isLeft && mid + 1 <= n - 1 && nums[mid + 1] == target) start = mid; + else return mid; + } else if (val < target) start = mid; + else end = mid; + } + if (nums[start] == target && nums[end] == target) return isLeft ? start : end; + if (nums[start] == target) return start; + if (nums[end] == target) return end; + return -1; + } +} +``` \ No newline at end of file diff --git a/Java/340. Longest Substring with At Most K Distinct Characters.java b/Java/340. Longest Substring with At Most K Distinct Characters.java new file mode 100755 index 0000000..ad9caf1 --- /dev/null +++ b/Java/340. Longest Substring with At Most K Distinct Characters.java @@ -0,0 +1,143 @@ +H +tags: Hash Table, String, Sliding Window, Two Pointers, LinkedHashMap +time: O(n) +space: O(k) + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + +``` +/* +Given a string s, find the length of the longest substring T that contains at most k distinct characters. + +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 + +*/ + +// Method1: Slinding window +class Solution { + public int lengthOfLongestSubstringKDistinct(String s, int k) { + if (s == null) return 0; + int left = 0, right = 0, max = 0, n = s.length(); + Map freq = new HashMap<>(); + + while (right < n) { + // 1) expand right + char head = s.charAt(right++); + freq.put(head, freq.getOrDefault(head, 0) + 1); + + // 2) process when window is reached + if (freq.size() <= k) max = Math.max(max, right - left); + + // 3) contract left + if (freq.size() > k) { + char tail = s.charAt(left++); + freq.put(tail, freq.get(tail) - 1); + if (freq.get(tail) == 0) freq.remove(tail); + } + } + + return max; + } +} + +// Method2: Sliding window, store last occurance of char, and truncate +class Solution { + public int lengthOfLongestSubstringKDistinct(String s, int k) { + 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() <= k) { // add new char + lastOccurMap.put(s.charAt(right), right++); + } + if (lastOccurMap.size() > k) { // 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; + } +} + + +// Method3: based on method2, update the map with LinkedHashMap +class Solution { + public int lengthOfLongestSubstringKDistinct(String s, int k) { + if (s == null || s.length() == 0) return 0; + int n = s.length(); + Map lastOccurMap = new LinkedHashMap<>(k + 1); + int left = 0, right = 0, max = 0; + + while (right < n) { + // add new char + char c = s.charAt(right); + if (lastOccurMap.containsKey(c)) lastOccurMap.remove(c); + lastOccurMap.put(c, right++); + + if (lastOccurMap.size() > k) { // clean up left-most char + Map.Entry leftMost = lastOccurMap.entrySet().iterator().next(); + lastOccurMap.remove(leftMost.getKey()); + left = leftMost.getValue() + 1; + } + max = Math.max(max, right - left); + } + + return max; + } +} + + + +``` \ No newline at end of file diff --git a/Java/341. Flatten Nested List Iterator.java b/Java/341. Flatten Nested List Iterator.java new file mode 100755 index 0000000..363ea64 --- /dev/null +++ b/Java/341. Flatten Nested List Iterator.java @@ -0,0 +1,174 @@ +M +tags: Stack, Design, NestedInteger +time: O(n) +space: O(n) + +#### Method1: Stack holds items from back of the list +- Option1: always set integer on top of the stack everywhere + - if not, poping stack until the top is integer + - code is easy +- Option2: in hasNext(), faltten the list in stack + +#### Method2: DFS preprocessing. +- 用queue to store all items. Kinda hack. Defeat the purpose of the problem. +- Super fast to query next(), however, needs to holds everything in memory +- O(n) + +``` +/* +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(); + * } + */ +/* +Method1, Stack, Option1: +- Stack holds items from back of the list +- when popping, keep going until: 1: the processing list is exhausted, 2) the stack.peek() is a integer => return +*/ +public class NestedIterator implements Iterator { + Stack stack = new Stack<>(); + public NestedIterator(List nestedList) { + addListToStack(nestedList); + flattenStackTop(); + } + + // Stack store list (from back to front) + private void addListToStack(List nestedList) { + for (int i = nestedList.size() - 1; i >= 0; i--) { + stack.push(nestedList.get(i)); + } + } + + // Exhaust stack untill there is no list on top; it can reduce stack to empty + private void flattenStackTop() { + while (!stack.isEmpty() && !stack.peek().isInteger()) { + addListToStack(stack.pop().getList()); + } + } + + @Override + public Integer next() { + Integer num = stack.pop().getInteger(); + flattenStackTop(); // clean up stack to make sure top is integer + return num; + } + + @Override + public boolean hasNext() { + return !stack.isEmpty(); + } +} + + /* +Method1, Stack, Option2: +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) { + addListToStack(nestedList) + } + + // Stack store list (from back to front) + private void addListToStack(List nestedList) { + 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()) { // keep working on stack until the top is integer + NestedInteger nestedInteger = stack.peek(); + if (nestedInteger.isInteger()) return true; + // nestedInteger is list + stack.pop(); + // flatten the list and add back to stack + addListToStack(nestedInteger.getList()) + } + return false; + } +} + + + +/* +Method2. Flatten out first, kinda hack, but fast when next(), hasNext(). +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 { + 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()); + dfs(nestedInteger.getList()); + } + } + + @Override + public Integer next() { + return !queue.isEmpty() ? queue.poll(): 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/345. Reverse Vowels of a String.java b/Java/345. Reverse Vowels of a String.java new file mode 100755 index 0000000..3622d8e --- /dev/null +++ b/Java/345. Reverse Vowels of a String.java @@ -0,0 +1,96 @@ +E +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'); + 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; + } + List vowels = Arrays.asList('a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'); + List matches = new ArrayList<>(); + for (int i = 0; i < s.length(); i++) { + if (vowels.contains(s.charAt(i))) { + matches.add(s.charAt(i)); + } + } + 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/346. Moving Average from Data Stream.java b/Java/346. Moving Average from Data Stream.java new file mode 100755 index 0000000..32bb61b --- /dev/null +++ b/Java/346. Moving Average from Data Stream.java @@ -0,0 +1,54 @@ +E +tags: Design, Queue, Sliding Window +time: O(1) for `next()` +space: O(size) for fixed storage + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison +- Note: if we it is calculate moving-window-product, better to use deque :) +- Sliding window? + - It has the spirit of slinding window: 1) maintain a range; 2) check range size `if (queue.size() > size)` + - Though, the solution must use a data structure to store data; it is not the traditional sliding window type of `left/right` pointer problem + +``` +/** +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/347. Top K Frequent Elements.java b/Java/347. Top K Frequent Elements.java new file mode 100755 index 0000000..e1adc49 --- /dev/null +++ b/Java/347. Top K Frequent Elements.java @@ -0,0 +1,193 @@ +M +tags: Hash Table, Heap, PriorityQueue, MinHeap, MaxHeap +time: O(n) +space: O(n) + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + +``` +/** +Given a non-empty array of integers, return the k most frequent elements. + +Example 1: + +Input: nums = [1,1,1,2,2,3], k = 2 +Output: [1,2] +Example 2: + +Input: nums = [1], k = 1 +Output: [1] +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. + +*/ + +// Method1: bucket sort. Hashmap with count List[] +class Solution { + public List topKFrequent(int[] nums, int k) { + List rst = new ArrayList<>(); + if (nums == null) return rst; + + int n = nums.length; + Map map = buildFreqMap(nums); + + 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()); + } + // Start from largest bucket + for(int i = n; i >= 0 && rst.size() < k; i--) { + if(bucket[i] != null) rst.addAll(bucket[i]); + } + return rst; + } + + private Map buildFreqMap(int[] nums) { // init hashmap. O(n) + Map map = new HashMap<>(); + for (int num : nums) map.put(num, map.getOrDefault(num, 0) + 1); + return map; + } +} + + +//Method2: Min Heap. O(n) space, O(nlogk) time. +//Option1: Use just map +class Solution { + public List topKFrequent(int[] nums, int k) { + List result = new ArrayList<>(); + if (nums == null) return result; + + // init hashmap. O(n) + Map map = buildFreqMap(nums); + + // Prepare PriorityQueue + PriorityQueue> queue = new PriorityQueue<>(k, Comparator.comparing(r -> r.getValue())); + + for (Map.Entry entity: map.entrySet()) { + queue.offer(entity); + if (queue.size() > k) { + queue.poll(); + } + } + + while (!queue.isEmpty()) result.add(0, queue.poll().getKey()); + return result; + } + + private Map buildFreqMap(int[] nums) { // init hashmap. O(n) + Map map = new HashMap<>(); + for (int num : nums) map.put(num, map.getOrDefault(num, 0) + 1); + return map; + } +} +// Method2, option2: use object Record. +class Solution { + class Record { + int value, freq = 0; + public Record(int value) { + this.value = value; + } + } + public List topKFrequent(int[] nums, int k) { + List result = new ArrayList<>(); + if (nums == null) return result; + + // init hashmap. O(n) + Map map = buildFreqMap(nums); + + // Prepare PriorityQueue + PriorityQueue queue = new PriorityQueue<>(k, Comparator.comparing(r -> r.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; + } + + private Map buildFreqMap(int[] nums) { // init hashmap. O(n) + Map map = new HashMap<>(); + for (int num : nums) { + map.putIfAbsent(num, new Record(num)); + map.get(num).freq += 1; + } + return map; + } +} + + + +/* +Attempt with MaxHeap: INCORRECT +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 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/349. Intersection of Two Arrays.java b/Java/349. Intersection of Two Arrays.java new file mode 100755 index 0000000..55ed075 --- /dev/null +++ b/Java/349. Intersection of Two Arrays.java @@ -0,0 +1,85 @@ +E +tags: Hash Table, Two Pointers, Binary Search, Sort +time: O(m + n) +space: O(m + n) + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用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) { + Set set = new HashSet<>(); + for (int num : nums1) set.add(num); + Set rst = new HashSet<>(); + for (int num: nums2) { + if (set.contains(num)) rst.add(num); + } + int i = 0; + int[] result = new int[rst.size()]; + for (int num : rst) 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) { + Arrays.sort(nums1);// nLog(n) + Set resultSet = new HashSet<>(); + for (int num: nums2) { // nLog(m) + if(binarySearch(nums1, num)) { + resultSet.add(num); + } + } + int i = 0; + int[] result = new int[resultSet.size()]; + for (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/350. Intersection of Two Arrays II.java b/Java/350. Intersection of Two Arrays II.java new file mode 100755 index 0000000..5ae2764 --- /dev/null +++ b/Java/350. Intersection of Two Arrays II.java @@ -0,0 +1,73 @@ +E +tags: Hash Table, Two Pointers, Binary Search, Sort +time:O(n) +space:O(n) + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + +``` +/* +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) { + List result = new ArrayList<>(); + Map map = buildCountMap(nums1); + + // 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); + } + } + + return convertToArray(result); + } + + // fill nums into map + private Map buildCountMap(int[] nums) { + Map map = new HashMap<>(); + for (int num : nums) { + map.putIfAbsent(num, 0); + map.put(num, map.get(num) + 1); + } + return map; + } + + private int[] convertToArray(List list) { + int[] rst = new int[list.size()]; + for (int i = 0; i < list.size(); i++) { + rst[i] = list.get(i); + } + return rst; + } +} + + +``` \ No newline at end of file diff --git a/Java/359. Logger Rate Limiter.java b/Java/359. Logger Rate Limiter.java new file mode 100755 index 0000000..15cfcbb --- /dev/null +++ b/Java/359. Logger Rate Limiter.java @@ -0,0 +1,113 @@ +E +tags: Hash Table, Design +time: O(1) +space: O(n) + +#### HashMap +- map: avoid duplicate message, records timestamp for validation +- time: O(1) +- space: O(n) + +#### Queue + Set +- 1) keep a trimmed queue and set (all tasks to be within 10 secs); +- 2) use set to O(1) check if incoming message exists. +- time: O(x), trimQueue() +- space: O(n) + +``` + +/* +Design a logger system that receive stream of messages along with its timestamps, each message should be printed if and only if it is not printed in the last 10 seconds. + +Given a message and a timestamp (in seconds granularity), return true if the message should be printed in the given timestamp, otherwise returns false. + +It is possible that several messages arrive roughly at the same time. + +Example: + +Logger logger = new Logger(); + +// logging string "foo" at timestamp 1 +logger.shouldPrintMessage(1, "foo"); returns true; + +// logging string "bar" at timestamp 2 +logger.shouldPrintMessage(2,"bar"); returns true; + +// logging string "foo" at timestamp 3 +logger.shouldPrintMessage(3,"foo"); returns false; + +// logging string "bar" at timestamp 8 +logger.shouldPrintMessage(8,"bar"); returns false; + +// logging string "foo" at timestamp 10 +logger.shouldPrintMessage(10,"foo"); returns false; + +// logging string "foo" at timestamp 11 +logger.shouldPrintMessage(11,"foo"); returns true; +*/ + +class Logger { + + Map map; + /** Initialize your data structure here. */ + public Logger() { + map = new HashMap(); + } + + /** Returns true if the message should be printed in the given timestamp, otherwise returns false. + If this method returns false, the message will not be printed. + The timestamp is in seconds granularity. */ + public boolean shouldPrintMessage(int timestamp, String message) { + if (!map.containsKey(message)) { + map.put(message, timestamp); + return true; + } else if (timestamp - map.get(message) >= 10) { + map.put(message, timestamp); + return true; + } + return false; + } +} + + +#### Queue + Set +class Logger { + + Queue> queue; + Set set; + /** Initialize your data structure here. */ + public Logger() { + set = new HashSet(); + queue = new LinkedList(); + } + + /** Returns true if the message should be printed in the given timestamp, otherwise returns false. + If this method returns false, the message will not be printed. + The timestamp is in seconds granularity. */ + public boolean shouldPrintMessage(int timestamp, String message) { + trimQueue(timestamp); + if (!set.contains(message)) { + set.add(message); + queue.offer(new Pair<>(message, timestamp)); + return true; + } + return false; + } + + private void trimQueue(int timestamp) { + while (!queue.isEmpty()) { + Pair task = queue.peek(); + if (timestamp - task.getValue() < 10) break; + queue.poll(); + set.remove(task.getKey()); + } + } +} + +/** + * Your Logger object will be instantiated and called as such: + * Logger obj = new Logger(); + * boolean param_1 = obj.shouldPrintMessage(timestamp,message); + */ + +``` \ No newline at end of file diff --git a/Java/36. Valid Sudoku.java b/Java/36. Valid Sudoku.java new file mode 100755 index 0000000..bda5faa --- /dev/null +++ b/Java/36. Valid Sudoku.java @@ -0,0 +1,97 @@ +E +tags: Hash Table, Enumeration +time: (mn) +space: (mn) + +#### Hash Set +- 用HashSet存visited row/col/block. + - 在nest for loop里面validate row,col,and block. + - Special: validate block要利用i 和 j 增长的规律 +- i, j are [0~n) can build block boundary in a 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 if a 9x9 Sudoku board is valid. Only the filled cells need to be validated according to the following rules: + +Each row must contain the digits 1-9 without repetition. +Each column must contain the digits 1-9 without repetition. +Each of the 9 3x3 sub-boxes of the grid must contain the digits 1-9 without repetition. + +The Sudoku board could be partially filled, where empty cells are filled with the character '.'. + +Example 1: + +Input: +[ + ["5","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"] +] +Output: true +Example 2: + +Input: +[ + ["8","3",".",".","7",".",".",".","."], + ["6",".",".","1","9","5",".",".","."], + [".","9","8",".",".",".",".","6","."], + ["8",".",".",".","6",".",".",".","3"], + ["4",".",".","8",".","3",".",".","1"], + ["7",".",".",".","2",".",".",".","6"], + [".","6",".",".",".",".","2","8","."], + [".",".",".","4","1","9",".",".","5"], + [".",".",".",".","8",".",".","7","9"] +] +Output: false +Explanation: Same as Example 1, except with the 5 in the top left corner being + modified to 8. Since there are two 8's in the top left 3x3 sub-box, it is invalid. +Note: + +A Sudoku board (partially filled) could be valid but is not necessarily solvable. +Only the filled cells need to be validated according to the mentioned rules. +The given board contain only digits 1-9 and the character '.'. +The given board size is always 9x9. +*/ +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; + } +}; + +``` \ No newline at end of file diff --git a/Java/360. Sort Transformed Array.java b/Java/360. Sort Transformed Array.java new file mode 100755 index 0000000..096f29b --- /dev/null +++ b/Java/360. Sort Transformed Array.java @@ -0,0 +1,63 @@ +M +tags: Math, Two Pointers +time: O(n) +space: O(n) store result + +#### Two Pointers, Math +- Being able to analys the a*x^2 + b*x graph and find the `peak` or `valley` +- Math basics: x^2 dominates the overall curve so it is up to a to determine: + - `valley`: if a < 0, both sides will be small and center will be large. Prioritize larger value. + - `peak`: if a > 0, center will be small and both sides will be large. Prioritize smaller value. + - starting index being 0 or n-1, is driven by `a` + +``` +/* +Given a sorted array of integers nums and integer values a, b and c. Apply a quadratic function of the form f(x) = ax2 + bx + c to each element x in the array. + +The returned array must be in sorted order. + +Expected time complexity: O(n) + +Example 1: + +Input: nums = [-4,-2,2,4], a = 1, b = 3, c = 5 +Output: [3,9,15,33] +Example 2: + +Input: nums = [-4,-2,2,4], a = -1, b = 3, c = 5 +Output: [-23,-5,1,7] +*/ + +/* +- Math basics: x^2 dominates the overall curve so it is up to a to determine: + - if a < 0, both sides will be small and center will be large. Prioritize larger value. + - if a > 0, center will be small and both sides will be large. Prioritize smaller value. + - starting index being 0 or n-1, is driven by a +*/ +class Solution { + int a, b, c; + public int[] sortTransformedArray(int[] nums, int a, int b, int c) { + this.a = a; + this.b = b; + this.c = c; + int n = nums.length, i = 0, j = n - 1; + int[] rst = new int[n]; + int index = a >= 0 ? n - 1 : 0; + + while (i <= j) { + int left = calc(nums[i]), right = calc(nums[j]); + if (a >= 0) { + rst[index--] = left >= right ? calc(nums[i++]) : calc(nums[j--]); + } else { // a < 0 + rst[index++] = left >= right ? calc(nums[j--]) : calc(nums[i++]); + } + } + + return rst; + } + + private int calc(int x) { + return a * x * x + b * x + c; + } +} +``` \ No newline at end of file diff --git a/Java/361. Bomb Enemy.java b/Java/361. Bomb Enemy.java new file mode 100755 index 0000000..10dc112 --- /dev/null +++ b/Java/361. Bomb Enemy.java @@ -0,0 +1,181 @@ +M +tags: DP, Coordinate DP +time: O(mn) +space: O(n) by calculating column sum + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Method1: 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 的变换. + +#### Method2: Analyze, col count array: +- Inspired by: https://leetcode.com/problems/bomb-enemy/discuss/83387/Short-O(mn)-time-O(n)-space-solution +- Analyize the problem: need to add up 2 directions at any given point. + - Notice 1: if I traverse row by row, each colSum at a specific col j is likely to be the same + - Unless there is a Wall in last row, so we have to calclate the col sum starting from current row, below the Wall + - Notice 2: for row it is the same: + - If I in a new spot row[i][j], where (i-1) is Wall, i need to sum row from 0 +- we will process each point: + - process row by row and add up row sum + - buffer col[j] in an array vertically so we can resue + - make sure to recalculate row sum or col sum if last row index or last col index is Wall +- time: O(mn) traversal +- space: O(n) only use a column array + +``` +/* +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) +*/ + + +/* +Method1: +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; + } +} + + +/* +Method2: +- Inspired by: https://leetcode.com/problems/bomb-enemy/discuss/83387/Short-O(mn)-time-O(n)-space-solution +- Analyize the problem: need to add up 2 directions at any given point. + - Notice 1: if I traverse row by row, each colSum at a specific col j is likely to be the same + - Unless there is a Wall in last row, so we have to calclate the col sum starting from current row, below the Wall + - Notice 2: for row it is the same: + - If I in a new spot row[i][j], where (i-1) is Wall, i need to sum row from 0 +- we will process each point: + - process row by row and add up row sum + - buffer col[j] in an array vertically so we can resue + - make sure to recalculate row sum or col sum if last row index or last col index is Wall +*/ +class Solution { + public int maxKilledEnemies(char[][] grid) { + if (grid == null || grid.length == 0 || grid[0].length == 0) return 0; + int m = grid.length, n = grid[0].length; + int[] colSum = new int[n]; + int max = 0; + int row = 0; + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (j == 0 || grid[i][j - 1] == 'W') { + row = 0; + for (int k = j; k < n && grid[i][k] != 'W'; k++) { + row += grid[i][k] == 'E' ? 1 : 0; + } + } + + if (i == 0 || grid[i - 1][j] == 'W') { + colSum[j] = 0; + for (int k = i; k < m && grid[k][j] != 'W'; k++) { + colSum[j] += grid[k][j] == 'E' ? 1 : 0; + } + } + if (grid[i][j] == '0') max = Math.max(max, colSum[j] + row); + } + } + return max; + } +} + + + + +``` \ No newline at end of file diff --git a/Java/364. Nested List Weight Sum II.java b/Java/364. Nested List Weight Sum II.java new file mode 100755 index 0000000..3f8c6a1 --- /dev/null +++ b/Java/364. Nested List Weight Sum II.java @@ -0,0 +1,132 @@ +M +tags: DFS, NestedInteger +time: O(n), visit all nodes +space: O(h), depth + +#### Method1: DFS +- Build a list of NestedInt +- DFS: + - sum up integers in the list are integers + - dfs on nested list + - overallSum = sum * (depth+1) + - End state: if no nested list (no more child dfs), return depth 1 +- Parent level: sum up all ints and times the (depth+1) + + +#### Method2: BFS +- Using stack to flatten all nestedList, and process in the end +- Can actually use list, does not need to be stack. +- uses more memory + +``` +/* +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. + +Different from the previous question where weight is increasing from root to leaf, now the weight is defined from bottom up. i.e., the leaf level integers have weight 1, and the root level integers have the largest weight. + +Example 1: + +Input: [[1,1],2,[1,1]] +Output: 8 +Explanation: Four 1's at depth 1, one 2 at depth 2. +Example 2: + +Input: [1,[4,[6]]] +Output: 17 +Explanation: One 1 at depth 3, one 4 at depth 2, and one 6 at depth 1; 1*3 + 4*2 + 6*1 = 17. +*/ + + +/* +- Build a list of NestedInt +- End state: sum up only when all items in the list are integers (no more child dfs), return depth 1 +- Parent level: sum up all ints and times the (depth+1) +*/ +class Solution { + int overallSum = 0; + public int depthSumInverse(List nestedList) { + dfs(nestedList); + return overallSum; + } + + public int dfs(List nestedList) { + List list = new ArrayList<>(); + int sum = 0; + for (NestedInteger nestedInt : nestedList) { + if (nestedInt.isInteger()) sum += nestedInt.getInteger(); + else list.addAll(nestedInt.getList()); + } + if (list.isEmpty()) { + overallSum += sum; + return 1; + }; + + int depth = dfs(list) + 1; + overallSum += sum * depth; + return depth; + } +} + +// BFS, using stack +/* +- Use stack to flatten entire nested structure: build higher stack based on NestedIntegerList from bottom level +- When process: only process the ones that are integer (skip list since they are processed) +*/ +class Solution { + public int depthSumInverse(List nestedList) { + Stack> stack = new Stack<>(); + stack.push(nestedList); + while (true) { + List list = stack.peek(); + List newList = new ArrayList<>(); + for (NestedInteger item : list) { + if (!item.isInteger()) newList.addAll(item.getList()); + } + if (newList.isEmpty()) break; + stack.push(newList); + } + + // process + int sum = 0, level = 1; + while (!stack.isEmpty()) { + List list = stack.pop(); + for (NestedInteger item : list) { + if (item.isInteger()) sum += level * item.getInteger(); + } + level++; + } + return sum; + } +} + +/** + * // 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(); + * } + */ +``` \ No newline at end of file diff --git a/Java/366. Find Leaves of Binary Tree.java b/Java/366. Find Leaves of Binary Tree.java new file mode 100755 index 0000000..edf36bb --- /dev/null +++ b/Java/366. Find Leaves of Binary Tree.java @@ -0,0 +1,84 @@ +M +tags: Tree, DFS +time: O(n) +space: O(h) + +#### DFS: store nodes at the same depth +- the leaves are at depth 0 and the root is at highest depth +- dfs: the depth = index of the rst, start from depth = 0 at leaf +- end state: leaf node, add to rst, and return depth + + +``` + +/* +Given a binary tree, collect a tree's nodes as if you were doing this: Collect and remove all leaves, repeat until the tree is empty. + + + +Example: + +Input: [1,2,3,4,5] + + 1 + / \ + 2 3 + / \ + 4 5 + +Output: [[4,5,3],[2],[1]] + + +Explanation: + +1. Removing the leaves [4,5,3] would result in this tree: + + 1 + / + 2 + + +2. Now removing the leaf [2] would result in this tree: + + 1 + + +3. Now removing the leaf [1] would result in the empty tree: + + [] +*/ + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ +/* +- init rst +- dfs: the depth = index of the rst, start from depth = 0 at leaf +- end state: leaf node, add to rst, and return depth +*/ +class Solution { + public List> findLeaves(TreeNode root) { + List> rst = new ArrayList<>(); + dfs(rst, root); + return rst; + } + + private int dfs(List> rst, TreeNode node) { + if (node == null) return 0; + + int leftDepth = dfs(rst, node.left); + int rightDepth = dfs(rst, node.right); + int currDepth = Math.max(leftDepth, rightDepth); + if(rst.size() <= currDepth) rst.add(new ArrayList<>()); + rst.get(currDepth).add(node.val); + + return currDepth + 1; + } +} +``` \ No newline at end of file diff --git a/Java/367. Valid Perfect Square.java b/Java/367. Valid Perfect Square.java new file mode 100755 index 0000000..3645ce1 --- /dev/null +++ b/Java/367. Valid Perfect Square.java @@ -0,0 +1,49 @@ +E +tags: Math, Binary Search +time: O(logN) +space: O(1) + +#### Binary找sqrt +- binary search template: mid+1, mid-1, `start <= end` +- 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] +*/ +/* +- Find a number n, that n*n == num +- Make it faster, use binary search +- End state: (n-1)^2 < num, and n^2 > num +*/ +class Solution { + public boolean isPerfectSquare(int num) { + long start = 0, end = num; + while (start <= end) { + long mid = start + (end - start) / 2; + long sqrt = mid * mid; + if (sqrt == num) return true; + else if (sqrt < num) start = mid + 1; + else end = mid - 1; + } + return false; + } +} +``` \ No newline at end of file diff --git a/Java/369. Plus One Linked List.java b/Java/369. Plus One Linked List.java new file mode 100755 index 0000000..a9c1850 --- /dev/null +++ b/Java/369. Plus One Linked List.java @@ -0,0 +1,76 @@ +M +tags: Linked List +time: O(n) +space: O(1) + +#### Reverse to make significant digit at tail +- Need add from the back and calculate carry +- Reverse list, so insignificant digit at head; calculate carry +- Reverse back when output + +``` +/* +Given a non-negative integer represented as non-empty a singly linked list of digits, plus one to the integer. + +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. + +Example : + +Input: [1,2,3] +Output: [1,2,4] +*/ + +/** + * Definition for singly-linked list. + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { val = x; } + * } + */ +/* +Head: 1, 2, 3 +- Need add from the back and calculate carry +- Reverse list, so head: 3,2,1; add from head and carry down +- Reverse back when output + +*/ +class Solution { + public ListNode plusOne(ListNode head) { + ListNode reversedHead = reverse(head); + + int carry = 1; + ListNode node = reversedHead; + while (node != null && carry != 0) { + int sum = node.val + carry; + node.val = sum % 10; + carry = sum / 10; + + node = node.next; + } + + node = reverse(reversedHead); + if (carry > 0) { + ListNode temp = node; + node = new ListNode(carry); + node.next = temp; + } + + return node; + } + + private ListNode reverse(ListNode node) { + ListNode dummy = new ListNode(0); + + while(node != null) { + ListNode temp = node.next; + node.next = dummy.next; + dummy.next = node; + node = temp;// move pointer + } + return dummy.next; + } +} +``` \ No newline at end of file diff --git a/Java/373. Find K Pairs with Smallest Sums.java b/Java/373. Find K Pairs with Smallest Sums.java new file mode 100755 index 0000000..82afb66 --- /dev/null +++ b/Java/373. Find K Pairs with Smallest Sums.java @@ -0,0 +1,121 @@ +M +tags: MinHeap, MaxHeap, Heap +time: O(klogk) +space: O(k) + +#### Method1: MinHeap wiht k size +- This approach follows the pattern of finding min pair: + - 1) only need to store k pairs + - 2) always start from min of A list and min of B list + - 3) pre-build k pairs honoring A list, and then pick the min pair, and start swapping with min of list B +- First attemp all first k pairs from nums1[i] against nums2[0] <=k : O(k) +- Use queue to pull min node and save results +- Use the nums1 val from the min node, pair up with nums2[j], add back to queue to sort +- overall runtime: O(klogk) +- space: O(k) + +#### Method2: MaxHeap with k size +- Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +- overall time: O(mnLogK) +- space: O(k) + +``` +/* + +You are given two integer arrays nums1 and nums2 sorted in ascending order and an integer k. + +Define a pair (u,v) which consists of one element from the first array and one element from the second array. + +Find the k pairs (u1,v1),(u2,v2) ...(uk,vk) with the smallest sums. + +Example 1: + +Input: nums1 = [1,7,11], nums2 = [2,4,6], k = 3 +Output: [[1,2],[1,4],[1,6]] +Explanation: The first 3 pairs are returned from the sequence: + [1,2],[1,4],[1,6],[7,2],[7,4],[11,2],[7,6],[11,4],[11,6] +Example 2: + +Input: nums1 = [1,1,2], nums2 = [1,2,3], k = 2 +Output: [1,1],[1,1] +Explanation: The first 2 pairs are returned from the sequence: + [1,1],[1,1],[1,2],[2,1],[1,2],[2,2],[1,3],[1,3],[2,3] +Example 3: + +Input: nums1 = [1,2], nums2 = [3], k = 3 +Output: [1,3],[2,3] +Explanation: All possible pairs are returned from the sequence: [1,3],[2,3] +*/ + +/* +Method1: MinHeap +*/ +class Solution { + public List> kSmallestPairs(int[] nums1, int[] nums2, int k) { + List> rst = new ArrayList<>(); + if (k <= 0 || nums1.length == 0 || nums2.length == 0) return rst; + + Queue> queue = initQueue(k); + int m = nums1.length, n = nums2.length; + for (int i = 0; i < m && i <= k; i++) { + queue.offer(Arrays.asList(nums1[i], nums2[0], 0)); + } + + while(!queue.isEmpty() && k-- != 0) { + List node = queue.poll(); + rst.add(Arrays.asList(node.get(0), node.get(1))); + int tailIndex = node.get(2) + 1; + if (tailIndex >= n) continue; + queue.offer(Arrays.asList(node.get(0), nums2[tailIndex], tailIndex)); + } + + return rst; + } + + private PriorityQueue> initQueue(int k) { + return new PriorityQueue<>(k, new Comparator>() { + public int compare(List a, List b) { + return a.get(0) + a.get(1) - b.get(0) - b.get(1); + } + }); + } +} + +/* +Method2: +Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +overall time: O(mnLogK) +space: O(k) +*/ +class Solution { + public List> kSmallestPairs(int[] nums1, int[] nums2, int k) { + List> rst = new ArrayList<>(); + if (k <= 0) return rst; + + Queue> queue = initQueue(k); + int m = nums1.length, n = nums2.length; + for (int a : nums1) { + for (int b : nums2) { + queue.offer(Arrays.asList(a, b)); + if (queue.size() > k) queue.poll(); + } + } + + while(!queue.isEmpty()) { + rst.add(0, queue.poll()); + } + + return rst; + } + + private PriorityQueue> initQueue(int k) { + return new PriorityQueue<>(k, new Comparator>() { + public int compare(List a, List b) { + return b.get(0) + b.get(1) - a.get(0) - a.get(1); + } + }); + } +} + + +``` \ No newline at end of file diff --git a/Java/380. Insert Delete GetRandom O(1).java b/Java/380. Insert Delete GetRandom O(1).java new file mode 100755 index 0000000..28c6e2c --- /dev/null +++ b/Java/380. Insert Delete GetRandom O(1).java @@ -0,0 +1,101 @@ +M +tags: Design, Array, Hash Table +time: O(1) avg +space: O(n) + +#### Hash Table, Swap when removing +- Map: `map, Lis: tracks `index->value` +- list maintain 用来 insert/remove/random operations. +- Remove: swap input valueIndex & tialIndex = list.size() -1. + - list.remove(object) 应该是要O(logn) 做一个search的. + - list.remove(list.size() - 1) is cheapter + +``` + +/* +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 { + List list = new LinkedList<>(); + Map map = new HashMap<>(); + Random rnd = new Random(); + /** Initialize your data structure here. */ + public RandomizedSet() { } + + /** 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)) { + list.add(val); + map.put(val, list.size() - 1); + 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)) return false; + int index = map.get(val); + swap(index, list.size() - 1); + map.put(list.get(index), index); + map.remove(val); + list.remove(list.size() - 1); + return true; + } + + /** Get a random element from the set. */ + public int getRandom() { + int index = rnd.nextInt(list.size()); + return list.get(index); + } + + private void swap(int a, int b) { + int temp = list.get(a); + list.set(a, list.get(b)); + list.set(b, temp); + } +} + +/** + * 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/383. Ransom Note.java b/Java/383. Ransom Note.java new file mode 100755 index 0000000..6e62fe9 --- /dev/null +++ b/Java/383. Ransom Note.java @@ -0,0 +1,47 @@ +E +1558026139 +tags:String, Basic Implementation + +count chars in int[256] + +``` +/* +Given an arbitrary ransom note string and another string containing letters from all the magazines, write a function that will return true if the ransom note can be constructed from the magazines ; otherwise, it will return false. + +Each letter in the magazine string can only be used once in your ransom note. + +Note: +You may assume that both strings contain only lowercase letters. + +canConstruct("a", "b") -> false +canConstruct("aa", "ab") -> false +canConstruct("aa", "aab") -> true +*/ + +/* +count ransom note letter occurrence, and count magazine letters +compare occurrence +edge: null ransom -> true; null or shorter magazine, false +*/ +class Solution { + public boolean canConstruct(String ransomNote, String magazine) { + if (ransomNote == null) return true; + if (magazine == null || magazine.length() < ransomNote.length()) return false; + + int[] count = new int[256]; + for (char c : magazine.toCharArray()) { + count[c]++; + } + + for (char c : ransomNote.toCharArray()) { + count[c]--; + if (count[c] < 0) { + return false; + } + } + + return true; + } +} + +``` \ No newline at end of file diff --git a/Java/387. First Unique Character in a String.java b/Java/387. First Unique Character in a String.java new file mode 100755 index 0000000..c36472a --- /dev/null +++ b/Java/387. First Unique Character in a String.java @@ -0,0 +1,78 @@ +E +tags: Hash Table, String +time: O(n) +space: O(256) = O(1) + +#### Count appearance with int[256] +- 按照题意, 找到第一个 first index == last index的字母. + +#### Count appearance with hashmap (more scalable) +- 用hashmap存字母的index, 有些重复字母的index就会是个list. +- 找到单一index, 结合成list, sort, return list.get(0) +- slow due + +``` +/* +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 + // slow + */ +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; + Map map = new HashMap<>(); + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + map.putIfAbsent(c, 0); + map.put(c, map.get(c) + 1); + } + + for (int i = 0; i < s.length(); i++) { + char c = s.charAt(i); + if (map.get(c) == 1) return i; + } + return -1; + } +} + +``` diff --git a/Java/39. Combination Sum.java b/Java/39. Combination Sum.java new file mode 100755 index 0000000..1b56d43 --- /dev/null +++ b/Java/39. Combination Sum.java @@ -0,0 +1,122 @@ +M +tags: Array, DFS, Backtracking, Combination +time: O(k * 2^n), k = avg rst length +space: O(k) stack depth, if not counting result size + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + +``` +/** +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] +] + + */ + +/* +- 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 + +*/ +// Method1: DFS, build result as end state + +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) { + if (target == 0) { + result.add(new ArrayList<>(list)); + return; + } + for (int i = index; i < candidates.length; i++) { + int value = candidates[i]; + list.add(value); + 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; + } +} + + +// Method2: DFS, build result in for loop +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; + } +} + + +``` \ No newline at end of file diff --git a/Java/398. Random Pick Index.java b/Java/398. Random Pick Index.java new file mode 100755 index 0000000..dd08d4b --- /dev/null +++ b/Java/398. Random Pick Index.java @@ -0,0 +1,66 @@ +M +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. +- probability theory: + - 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; + } +} +``` \ No newline at end of file diff --git a/Java/399. Evaluate Division.java b/Java/399. Evaluate Division.java new file mode 100755 index 0000000..601428f --- /dev/null +++ b/Java/399. Evaluate Division.java @@ -0,0 +1,84 @@ +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(List> equations, double[] values, List> queries) { + + buildGraph(equations, values); + // dfs on each item + int n = queries.size(); + double[] result = new double[n]; + for (int i = 0; i < n; i++) { + List query = queries.get(i); + result[i] = dfs(new HashSet<>(), query.get(0), query.get(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 void buildGraph(List> equations, double[] values) { + for (int i = 0; i < values.length; i++) { + String x = equations.get(i).get(0), y = equations.get(i).get(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); + } + } + + private String getKey(String a, String b) { + return String.format("%s->%s", a, b); + } +} +``` \ 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/40. Combination Sum II.java b/Java/40. Combination Sum II.java new file mode 100755 index 0000000..a76ad02 --- /dev/null +++ b/Java/40. Combination Sum II.java @@ -0,0 +1,135 @@ +M +tags: Array, DFS, Backtracking, Combination +time: O(k * 2^n), k = avg rst length +space: O(n) stack depth, if not counting result size + +给一串数字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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + +``` +/* +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) { + if (target == 0) { + result.add(new ArrayList<>(list)); + return; + } + // 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 >= 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; + } +} + + + +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; + } +} + + + + +``` \ No newline at end of file diff --git a/Java/402. Remove K Digits.java b/Java/402. Remove K Digits.java new file mode 100755 index 0000000..897eff8 --- /dev/null +++ b/Java/402. Remove K Digits.java @@ -0,0 +1,73 @@ +M +tags: Greedy, Stack, Monotonous Stack +time: O(n) +space: O(n) + +#### Monotonous Stack (Increasing) +- Greedy: Remove 1) earlier digits(数位靠前权值大), 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. + + +``` +/* +Given a non-negative integer num represented as a string, remove k digits from the number so that the new number is the smallest possible. + +Note: +The length of num is less than 10002 and will be ≥ k. +The given num does not contain any leading zero. +Example 1: + +Input: num = "1432219", k = 3 +Output: "1219" +Explanation: Remove the three digits 4, 3, and 2 to form the new number 1219 which is the smallest. +Example 2: + +Input: num = "10200", k = 1 +Output: "200" +Explanation: Remove the leading 1 and the number is 200. Note that the output must not contain leading zeroes. +Example 3: + +Input: num = "10", k = 2 +Output: "0" +Explanation: Remove all the digits from the number and it is left with nothing which is 0. +*/ +/* +Monotonous Stack (Increasing): +- Remove 1) early, 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. +*/ +class Solution { + public String removeKdigits(String num, int k) { + Stack stack = new Stack<>(); + + // Monotonous Stack + int i = 0, n = num.length(); + while (i < n) { + char c = num.charAt(i++); + while (k > 0 && !stack.isEmpty() && stack.peek() > c) { + stack.pop(); + k--; + } + stack.push(c); + } + + // handle coner case when all digits are equal: 33333, that k never decreases + while (k-- > 0) stack.pop(); + + // Output: + StringBuffer sb = new StringBuffer(); + while(!stack.isEmpty()) sb.append(stack.pop()); + sb.reverse(); + while(sb.length() > 0 && sb.charAt(0) == '0') sb.deleteCharAt(0); + + return sb.length() == 0 ? "0" : sb.toString(); + } +} + +``` \ No newline at end of file diff --git a/Java/405. Convert a Number to Hexadecimal.java b/Java/405. Convert a Number to Hexadecimal.java new file mode 100755 index 0000000..23f9a2a --- /dev/null +++ b/Java/405. Convert a Number to Hexadecimal.java @@ -0,0 +1,61 @@ +E +tags: Bit Manipulation + +#### Unsigned Shift, Mask +- Move pointer: move digit after process 4 bits. + - `>>>` Unsigned right shift + - always fills 0 irrespective of the sign of the number +- Mas: `num & 0xf` = `num & 15` + +``` +/* +Given an integer, write an algorithm to convert it to hexadecimal. For negative integer, two’s complement method is used. + +Note: + +All letters in hexadecimal (a-f) must be in lowercase. +The hexadecimal string must not contain extra leading 0s. If the number is zero, it is represented by a single zero character '0'; otherwise, the first character in the hexadecimal string will not be the zero character. +The given number is guaranteed to fit within the range of a 32-bit signed integer. +You must not use any method provided by the library which converts/formats the number to hex directly. +Example 1: + +Input: +26 + +Output: +"1a" +Example 2: + +Input: +-1 + +Output: +"ffffffff" +*/ + +/* + +each time we take a look at the last four digits of + binary verion of the input, and maps that to a hex char + shift the input to the right by 4 bits, do it again + until input becomes 0. +*/ +class Solution { + public String toHex(int num) { + if (num == 0) return "0"; + + StringBuffer sb = new StringBuffer(); + while (num != 0) { + int digit = num & 0xf; // same as num & 15 + num >>>= 4; + sb.insert(0, convert(digit)); + } + return sb.toString(); + } + + private String convert(int digit) { + if (digit <= 9) return String.valueOf((char)(digit + '0')); + return String.valueOf((char)(digit - 10 + 'a')); + } +} +``` \ No newline at end of file diff --git a/Java/408. Valid Word Abbreviation.java b/Java/408. Valid Word Abbreviation.java new file mode 100755 index 0000000..62e4a4c --- /dev/null +++ b/Java/408. Valid Word Abbreviation.java @@ -0,0 +1,68 @@ +E +1557989899 +tags:String, Basic Implementation + +tricky: find integer within a string +edge case: leading '0' should not be allow in such abbr. + +``` +/* +Given a non-empty string s and an abbreviation abbr, return whether the string matches with the given abbreviation. + +A string such as "word" contains only the following valid abbreviations: + +["word", "1ord", "w1rd", "wo1d", "wor1", "2rd", "w2d", "wo2", "1o1d", "1or1", "w1r1", "1o2", "2r1", "3d", "w3", "4"] +Notice that only the above abbreviations are valid abbreviations of the string "word". Any other string is not a valid abbreviation of "word". + +Note: +Assume s contains only lowercase letters and abbr contains only lowercase letters and digits. + +Example 1: +Given s = "internationalization", abbr = "i12iz4n": + +Return true. +Example 2: +Given s = "apple", abbr = "a2e": + +Return false. +*/ + +/* +abbr uses int to replace letters. +walk through abbr, if non-int letter from abbr all matches original word, and travese completes, it's a true. +*/ +class Solution { + public boolean validWordAbbreviation(String word, String abbr) { + if (abbr == null || abbr.length() > word.length()) { + return false; + } + int index = 0; // index tracker on word + int i = 0; // index tracker for abbr + int m = word.length(), n = abbr.length(); + while (i < n && index < m) { + if (word.charAt(index) == abbr.charAt(i)) { + index++; + i++; + continue; + } + + // edge case: no integer, or: leading '0' cannot be used for abbr + if (!isInt(abbr.charAt(i)) || abbr.charAt(i) == '0') { + return false; + } + // obtain the int + StringBuffer sb = new StringBuffer(); + while (i < n && isInt(abbr.charAt(i))) { + sb.append(abbr.charAt(i)); + i++; + } + index += Integer.parseInt(sb.toString()); + } + return index == m && i == n; + } + + private boolean isInt(char c) { + return c >= '0' && c <= '9'; + } +} +``` \ No newline at end of file diff --git a/Java/41. First Missing Positive.java b/Java/41. First Missing Positive.java new file mode 100755 index 0000000..c30cfcb --- /dev/null +++ b/Java/41. First Missing Positive.java @@ -0,0 +1,122 @@ +H +tags: Array, Analysis, Edge Case +time: O(n) +space: O(1) + +给一串无序数字, 有负数: 找这个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 +1. 如果nums==null, 其实missing positive integer 自然而然是 1 +1. 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) + - 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +1. 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + +``` +/* +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 + +*/ + + +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 + +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 { + 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/Java/410. Split Array Largest Sum.java b/Java/410. Split Array Largest Sum.java new file mode 100755 index 0000000..00d9df7 --- /dev/null +++ b/Java/410. Split Array Largest Sum.java @@ -0,0 +1,22 @@ +/* +Given an array which consists of non-negative integers and an integer m, you can split the array into m non-empty continuous subarrays. Write an algorithm to minimize the largest sum among these m subarrays. + +Note: +If n is the length of array, assume the following constraints are satisfied: + +1 ≤ n ≤ 1000 +1 ≤ m ≤ min(50, n) +Examples: + +Input: +nums = [7,2,5,10,8] +m = 2 + +Output: +18 + +Explanation: +There are four ways to split nums into two subarrays. +The best way is to split it into [7,2,5] and [10,8], +where the largest sum among the two subarrays is only 18. +*/ \ No newline at end of file diff --git a/Java/414. Third Maximum Number.java b/Java/414. Third Maximum Number.java new file mode 100755 index 0000000..27ec78a --- /dev/null +++ b/Java/414. Third Maximum Number.java @@ -0,0 +1,50 @@ +E +tags: Array, PriorityQueue + +#### pq +- 注意special case: `when less than 3 items, return maximum` +- PQ是natural order: remain peek() will be the 3rd maximum + + +``` +/* +Given a non-empty array of integers, return the third maximum number in this array. If it does not exist, return the maximum number. The time complexity must be in O(n). + +Example 1: +Input: [3, 2, 1] + +Output: 1 + +Explanation: The third maximum is 1. +Example 2: +Input: [1, 2] + +Output: 2 + +Explanation: The third maximum does not exist, so the maximum (2) is returned instead. +Example 3: +Input: [2, 2, 3, 1] + +Output: 1 + +Explanation: Note that the third maximum here means the third maximum distinct number. +Both numbers with value 2 are both considered as second maximum. +*/ + +class Solution { + public int thirdMax(int[] nums) { + PriorityQueue pq = new PriorityQueue<>(); + for (int num : nums) { + if (!pq.contains(num)) { + pq.offer(num); + if (pq.size() > 3) pq.poll(); + } + } + // if no third maximum, return max + if (pq.size() < 3) { + while (pq.size() > 1) pq.poll(); + } + return pq.peek(); + } +} +``` \ No newline at end of file diff --git a/Java/415. Add Strings.java b/Java/415. Add Strings.java new file mode 100755 index 0000000..b0a48d5 --- /dev/null +++ b/Java/415. Add Strings.java @@ -0,0 +1,99 @@ +E +tags: String, Basic Implementation, Math +time: O(n) +space: O(n) + +#### Two Pointer +- Use i, j to process from end of 2 strings +- handle edge case for i, j + - if i < 0, its num = 0 (since we are doing sum, blindly setting 0 is okay) +- Note: `sb.insert(0, x)` is much slower than doing a final `sb.reverse()` + +#### If manually convertin to int[] +1. when converting to int[], remember to reverse string. +1. when converting to int[], remember to reserve extra space for carry + +``` +/** +Given two non-negative integers num1 and num2 represented as string, return the sum of num1 and num2. + +Note: + +The length of both num1 and num2 is < 5100. +Both num1 and num2 contains only digits 0-9. +Both num1 and num2 does not contain any leading zero. +You must not use any built-in BigInteger library or convert the inputs to integer directly. +*/ +class Solution { + public String addStrings(String num1, String num2) { + StringBuffer sb = new StringBuffer(); + int carry = 0; + int i = num1.length() - 1, j = num2.length() - 1; + + // carefully handle index i/j pointer. + for (; i >= 0 || j >= 0 || carry == 1; i--, j--) { // can switch to a while loop + int x = i < 0 ? 0 : (num1.charAt(i) - '0'); + int y = j < 0 ? 0 : (num2.charAt(j) - '0'); + int sum = x + y + carry; + sb.append(sum % 10); + carry = sum / 10; + } + + return sb.reverse().toString(); + } +} + +/* +- turn into char, and to int, then add from end +- turn final result of int[] into string +*/ +class Solution { + public String addStrings(String num1, String num2) { + int[] maxNums, minNums; + if (num1.length() <= num2.length()) { + minNums = convertToInt(num1); + maxNums = convertToInt(num2); + } else { + minNums = convertToInt(num2); + maxNums = convertToInt(num1); + } + + int carry = 0; + for (int i = 0; i < minNums.length; i++) { + int sum = minNums[i] + maxNums[i] + carry; + maxNums[i] = sum % 10; + carry = sum / 10; + } + for (int i = minNums.length; i < maxNums.length; i++) { + int sum = maxNums[i] + carry; + maxNums[i] = sum % 10; + carry = sum / 10; + } + return convertToString(maxNums); + } + + // reverse string gand convert to int[], leave 1 open spot + private int[] convertToInt(String num) { + if (num == null || num.length() == 0) { + return new int[1]; + } + int n = num.length(); + int[] nums = new int[n + 1]; + for (int i = 0; i < n; i++) { + nums[i] = num.charAt(n - i - 1) - '0'; + } + return nums; + } + + // cut the one possible leading zero, and reverse the string + private String convertToString(int[] result) { + int n = result[result.length - 1] == 0 ? result.length - 1: result.length; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < n; i++) { + char c = (char)('0' + result[n - i - 1]); + sb.append(c); + } + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/416. Partition Equal Subset Sum.java b/Java/416. Partition Equal Subset Sum.java new file mode 100644 index 0000000..a120c4a --- /dev/null +++ b/Java/416. Partition Equal Subset Sum.java @@ -0,0 +1,122 @@ +M +tags: DP, Backpack + +#### Backpack DP +- the problem turns into: can we find a subset of items that sum up to target sum? +- create `boolean dp[j]` to represent if we can sum up to j, where j = sum value + - want to try out all items in num, + +#### DFS +- use dfs to find a subset of items that sum up to target sum? + +``` +/* +Given a non-empty array containing only positive integers, find if the array can be partitioned into two subsets such that the sum of elements in both subsets is equal. + +Note: + +Each of the array element will not exceed 100. +The array size will not exceed 200. + + +Example 1: + +Input: [1, 5, 11, 5] + +Output: true + +Explanation: The array can be partitioned as [1, 5, 5] and [11]. + + +Example 2: + +Input: [1, 2, 3, 5] + +Output: false + +Explanation: The array cannot be partitioned into equal sum subsets. + +*/ + +class Solution { + public boolean canPartition(int[] nums) { + if (nums == null || nums.length < 2) return false; + int sum = 0; + for (int num : nums) sum += num; + if (sum % 2 != 0) return false; + sum /= 2; + + // dp + boolean[] dp = new boolean[sum + 1]; // dp[j] = false by default + dp[0] = true; + for (int i = 0; i < nums.length; i++) { + int num = nums[i]; + for (int j = sum; j >= num; j--) { + dp[j] = dp[j] || dp[j - num]; + } + } + return dp[sum]; + } +} + +// DFS correct solution, but times out when [1, 1, 1, ,,,,,,,1, 100] case. +class Solution { + public boolean canPartition(int[] nums) { + if (nums == null || nums.length < 2) return false; + int sum = 0; + for (int num : nums) sum += num; + if (sum % 2 != 0) return false; + return dfs(nums, 0, sum / 2); + } + + public boolean dfs(int[] nums, int index, int target) { + if (target < 0) return false; + if (target == 0) return true; + for (int i = index; i < nums.length; i++) { + if (dfs(nums, i + 1, target - nums[i])) return true; + } + return false; + } +} +// Still times out +class Solution { + public boolean canPartition(int[] nums) { + if (nums == null || nums.length < 2) return false; + int sum = 0; + for (int num : nums) sum += num; + if (sum % 2 != 0) return false; + sum /= 2; + Arrays.sort(nums); + return dfs(nums, 0, sum); + } + + public boolean dfs(int[] nums, int index, int target) { + if (index == nums.length) return false; + if (target == nums[index]) return true; + if (target < nums[index]) return false; + return dfs(nums, index + 1, target - nums[index]) + || dfs(nums, index + 1, target); + } +} + +// However C++ solution passes!! +class Solution { +public: + bool canPartition(vector& nums) { + int sum = 0; + for(int i =0;i& nums, int sum, int index){ + if(index == nums.size()) return false; + if(sum == nums[index]) return true; + if(sum < nums[index]) return false; + return helper(nums,sum-nums[index],index+1) || helper(nums,sum,index+1); + } +}; +``` \ No newline at end of file diff --git a/Java/42. Trapping Rain Water.java b/Java/42. Trapping Rain Water.java new file mode 100755 index 0000000..215862f --- /dev/null +++ b/Java/42. Trapping Rain Water.java @@ -0,0 +1,222 @@ +H +tags: Array, Two Pointers, Stack +time: O(n) +space: O(1) + +这道题目的方法比较多. + +#### Method1: Max wall from both sides +- Array: Left Max Wall vs Right Max Wall. +- 对于每个index而言, vertically 能存放的最大水柱, 就是靠 左 右 最高墙决定的: + - min(leftHighestWall, rightHighestWall) - currHeight. +- time: O(n) +- space: O(n) + +#### Method2: Two Pointers +- Optimization from Method1: two pointer, 还是找左边最高和右边最高. O(1) space. +- 利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +- always limited by the shorter wall: 左边按照maxLeft来计算, 右边按照maxRight来计算. +- time: O(n) +- space: O(1) + +#### Method3: 2 Pointers, start from 2 sides +- 1. 找中间最高bar的index +- 2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条 +- 3. 每次还要减去block自身的height +- time: O(n) +- space: O(1) + +#### Method4: Stack +- 主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +- 最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +- 用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. +- time: O(n) +- space: O(n) + + +``` +/* +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 + + +*/ +/* +Method1: +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], 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]); + + // Add water and remove wall height + int total = 0; + for (int i = 0; i < n; i++) { + int waterDepth = Math.min(maxLeft[i], maxRight[i]); + total += height[i] < waterDepth ? waterDepth - height[i] : 0; + } + return total; + } +} + + +/* +Method2: 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, left = 0, right = n - 1; + int maxLeft = height[0], 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; + } +} + + + +/* +Method3: find max first, then calculate from 2 sides +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) + +*/ +class Solution { + public int trap(int[] heights) { + if (heights == null || heights.length == 0) return 0; + + int maxIndex = 0; + for (int i = 0; i < heights.length; i++) { + if (heights[i] > heights[maxIndex]) maxIndex = i; + } + + int sum = 0, prev = 0; + //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; + } +} + +/* +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 stack = new Stack<>(); + int total = 0; + stack.push(0); + + for (int i = 1; i < height.length; i++) { + int curr = height[i]; + if (curr >= height[stack.peek()]) {// if ascending + int bottom = height[stack.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 (!stack.isEmpty() && curr >= height[stack.peek()]) { + int leftBound = stack.pop(); // pop(): shift left bound + total += (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 (!stack.isEmpty() && curr < height[stack.peek()]) { + total += (curr - bottom) * (i - stack.peek() - 1); + } + } + stack.push(i); + } + return total; + } +} + + +``` \ No newline at end of file diff --git a/Java/426. Convert Binary Search Tree to Sorted Doubly Linked List.java b/Java/426. Convert Binary Search Tree to Sorted Doubly Linked List.java new file mode 100755 index 0000000..ba431a6 --- /dev/null +++ b/Java/426. Convert Binary Search Tree to Sorted Doubly Linked List.java @@ -0,0 +1,107 @@ +M +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, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + +``` +/* +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); + Node rightHead = treeToDoublyList(root.right); + 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/427. Construct Quad Tree.java b/Java/427. Construct Quad Tree.java new file mode 100755 index 0000000..83331e4 --- /dev/null +++ b/Java/427. Construct Quad Tree.java @@ -0,0 +1,85 @@ +M +tags: Tree +time: O(n^2) +space: O(n^2) + +#### Basic Impl +- build tree recursively by definition +- O(n^2) time and space due to single visit to all nodes +``` +/* +We want to use quad trees to store an N x N boolean grid. Each cell in the grid can only be true or false. The root node represents the whole grid. For each node, it will be subdivided into four children nodes until the values in the region it represents are all the same. + +Each node has another two boolean attributes : isLeaf and val. isLeaf is true if and only if the node is a leaf node. The val attribute for a leaf node contains the value of the region it represents. + +Your task is to use a quad tree to represent a given grid. The following example may help you understand the problem better: + +Given the 8 x 8 grid below, we want to construct the corresponding quad tree: + +It can be divided according to the definition above: + + +The corresponding quad tree should be as following, where each node is represented as a (isLeaf, val) pair. + +For the non-leaf nodes, val can be arbitrary, so it is represented as *. + + + +Note: + +N is less than 1000 and guaranteened to be a power of 2. +If you want to know more about the quad tree, you can refer to its wiki. +*/ + +/* +// Definition for a QuadTree node. +class Node { + public boolean val; + public boolean isLeaf; + public Node topLeft; + public Node topRight; + public Node bottomLeft; + public Node bottomRight; + + public Node() {} + + public Node(boolean _val,boolean _isLeaf,Node _topLeft,Node _topRight,Node _bottomLeft,Node _bottomRight) { + val = _val; + isLeaf = _isLeaf; + topLeft = _topLeft; + topRight = _topRight; + bottomLeft = _bottomLeft; + bottomRight = _bottomRight; + } +}; +*/ +class Solution { + public Node construct(int[][] grid) { + if (grid == null || grid.length == 0) return null; + return dfs(grid, 0, 0, grid.length); + } + + private Node dfs(int[][] grid, int i, int j, int n) { + boolean val = grid[i][j] == 1; + if (n == 1) return buildLeaf(val); + + int len = n / 2; + Node topL = dfs(grid, i, j, len); + Node topR = dfs(grid, i, j + len, len); + Node botL = dfs(grid, i + len, j, len); + Node botR = dfs(grid, i + len, j + len, len); + if (topL.isLeaf && topR.isLeaf && botL.isLeaf && botR.isLeaf + && topL.val == topR.val && botL.val == botR.val && topL.val == botL.val) { + return buildLeaf(val); + } + return new Node(val, false, topL, topR, botL, botR); + } + + private Node buildLeaf(boolean val) { + Node node = new Node(); + node.val = val; + node.isLeaf = true; + return node; + } +} +``` \ No newline at end of file diff --git a/Java/429. N-ary Tree Level Order Traversal.java b/Java/429. N-ary Tree Level Order Traversal.java new file mode 100755 index 0000000..7d1361f --- /dev/null +++ b/Java/429. N-ary Tree Level Order Traversal.java @@ -0,0 +1,81 @@ +M +tags: BFS, Tree +time: O(n) +space: O(n) + +#### BFS +- use queue to hold each level. O(n) + +``` +/* +Given an n-ary tree, return the level order traversal of its nodes' values. + +Nary-Tree input serialization is represented in their level order traversal, each group of children is separated by the null value (See examples). + + + +Example 1: + + + +Input: root = [1,null,3,2,4,null,5,6] +Output: [[1],[3,2,4],[5,6]] +Example 2: + + + +Input: root = [1,null,2,3,4,5,null,null,6,7,null,8,null,9,10,null,null,11,null,12,null,13,null,null,14] +Output: [[1],[2,3,4,5],[6,7,8,9,10],[11,12,13],[14]] + + +Constraints: + +The height of the n-ary tree is less than or equal to 1000 +The total number of nodes is between [0, 10^4] + +*/ + +/* +- use queue to hold each level. O(n) +*/ +class Solution { + public List> levelOrder(Node 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) { + Node node = queue.poll(); + list.add(node.val); + if (node.children != null) { + for (Node child : node.children) queue.offer(child); + } + } + rst.add(list); + } + return rst; + } +} +/* +// Definition for a Node. +class Node { + public int val; + public List children; + + public Node() {} + + public Node(int _val) { + val = _val; + } + + public Node(int _val, List _children) { + val = _val; + children = _children; + } +}; +*/ +``` \ No newline at end of file diff --git a/Java/43. Multiply Strings.java b/Java/43. Multiply Strings.java new file mode 100755 index 0000000..67043a8 --- /dev/null +++ b/Java/43. Multiply Strings.java @@ -0,0 +1,206 @@ +M +tags: Math, String +time: O(mn) +space: O(mn) + +给两个integer String, 求乘积 + +#### String calculation, basic implementation +- let num1 = multipier, num2 = base. mutiply and save into int[m + n]. + - Loop over num1, each row num1[x] * num2, save to correct index (i + j + 1) + - Note: skip leading '0' during output, but do not delete string "0" + - time,space O(mn) +- Option1: Calculate carry on the fly + - index `curr = i + j + 1`, left index `left = curr - 1`, since we start calculation from end of the array. + - **we only touch right side of the array once**, so we can move the carry off from it, and carry to left index + - code is concise +- Option2: save product first without calculating carry + - save product in each int index + - calculate carry on rst[] and `sb.insert(0, c)` (since we start from end of rst) + - this is actaully faster than Option1, somehow. + +#### Reverse, calculate from index 0, and reverse back +- 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. +*/ + +// Same concept, slight concise +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; + + int m = num1.length(), n = num2.length(); + int[] rst = new int[m + n]; + + // multiply without carry handling, save from the end index + for (int i = m - 1; i >= 0; i--) { + for (int j = n - 1; j >= 0; j--) { + int curr = i + j + 1, left = curr - 1; + rst[curr] += toInt(num1, i) * toInt(num2, j); + rst[left] += rst[curr] / 10; + rst[curr] %= 10; + } + } + + // calc carry from end of rst + StringBuffer sb = new StringBuffer(); + for (int num : rst) { + if (!(sb.length() == 0 && num == 0)) { // skip front '0' + sb.append(toChar(num)); + } + } + + return sb.length() == 0 ? "0" : sb.toString(); + } + + private char toChar(int num) { + return (char)(num + '0'); + } + private int toInt(String s, int x) { + return s.charAt(x) - '0'; + } +} + +/* +num 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 w/o carry handling, save from the end index + 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 from end of rst + 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, toChar(rst[i])); + } + sb.insert(0, toChar(rst[0])); + + while (sb.length() > 1) { + if (sb.charAt(0) != '0') break; + sb.deleteCharAt(0); + } + + return sb.toString(); + } + + private char toChar(int num) { + return (char)(num + '0'); + } + 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/430. Flatten a Multilevel Doubly Linked List.java b/Java/430. Flatten a Multilevel Doubly Linked List.java new file mode 100755 index 0000000..1f9036d --- /dev/null +++ b/Java/430. Flatten a Multilevel Doubly Linked List.java @@ -0,0 +1,117 @@ +M +tags: DFS, Linked List +time: O(n) +space: O(1) + +#### DFS +- Depth-first: + - 1) process curr.child, return tailChild + - 2) connect tailChild.next = curr.next +- function: link(Node a, Node b); + +``` + +/* +You are given a doubly linked list which in addition to the next and previous pointers, it could have a child pointer, which may or may not point to a separate doubly linked list. These child lists may have one or more children of their own, and so on, to produce a multilevel data structure, as shown in the example below. + +Flatten the list so that all the nodes appear in a single-level, doubly linked list. You are given the head of the first level of the list. + + + +Example 1: + +Input: head = [1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12] +Output: [1,2,3,7,8,11,12,9,10,4,5,6] +Explanation: + +The multilevel linked list in the input is as follows: + + + +After flattening the multilevel linked list it becomes: + + +Example 2: + +Input: head = [1,2,null,3] +Output: [1,3,2] +Explanation: + +The input multilevel linked list is as follows: + + 1---2---NULL + | + 3---NULL +Example 3: + +Input: head = [] +Output: [] + + +How multilevel linked list is represented in test case: + +We use the multilevel linked list from Example 1 above: + + 1---2---3---4---5---6--NULL + | + 7---8---9---10--NULL + | + 11--12--NULL +The serialization of each level is as follows: + +[1,2,3,4,5,6,null] +[7,8,9,10,null] +[11,12,null] +To serialize all levels together we will add nulls in each level to signify no node connects to the upper node of the previous level. The serialization becomes: + +[1,2,3,4,5,6,null] +[null,null,7,8,9,10,null] +[null,11,12,null] +Merging the serialization of each level and removing trailing nulls we obtain: + +[1,2,3,4,5,6,null,null,null,7,8,9,10,null,null,11,12] + +*/ + +/* +- Depth-first: process curr.child, return tailChild, connect tailChild.next = curr.next +- function: link(Node a, Node b); +*/ +class Solution { + public Node flatten(Node head) { + + Node node = head; + while (node != null) { + if (node.child != null) { + Node next = node.next; + Node child = node.child; + Node childTail = dfs(child); + node.child = null; + link(node, child); + link(childTail, next); + node = next; + } else { + node = node.next; + } + } + + return head; + } + + private void link(Node a, Node b) { + if (a != null) a.next = b; + if (b != null) b.prev = a; + } + + private Node dfs(Node head) { + Node node = flatten(head); // flatten returns head + while (node.next != null) { + node = node.next; + } + return node; // return tail; + } +} + + + +``` \ No newline at end of file diff --git a/Java/432. All One Data Structure.java b/Java/432. All One Data Structure.java new file mode 100755 index 0000000..23b227a --- /dev/null +++ b/Java/432. All One Data Structure.java @@ -0,0 +1,123 @@ +H +tags: Design, Doubly Linked List +time: O(1) +space: O(n) + +#### Doubly Linked List +- IMPORTANT: the problem aims to put keys of same frequency in same node! This affects the design of node +- Main a class `Node {keySet, count, last/next pointers}` +- Each operation: + - 1) finds target node and extract the key + - 2) calculate: count +/- 1 + - 3) find new spot to store the key (prior positions or later positions) +- Be careful when handling the cases in inc() and dec() + +``` +/* +Implement a data structure supporting the following operations: + +Inc(Key) - Inserts a new key with value 1. Or increments an existing key by 1. Key is guaranteed to be a non-empty string. +Dec(Key) - If Key's value is 1, remove it from the data structure. Otherwise decrements an existing key by 1. If the key does not exist, this function does nothing. Key is guaranteed to be a non-empty string. +GetMaxKey() - Returns one of the keys with maximal value. If no element exists, return an empty string "". +GetMinKey() - Returns one of the keys with minimal value. If no element exists, return an empty string "". + +*/ + +class AllOne { + class Node { + Node last, next; + Set keys = new HashSet<>(); + int count; + public Node(int count, String key) { + this.count = count; + this.keys.add(key); + } + } + Map map; + Node head, tail; // Doubly Linked List + /** Initialize your data structure here. */ + public AllOne() { + map = new HashMap<>(); + head = new Node(-1, ""); + tail = new Node(-1, ""); + head.next = tail; + tail.last = head; + } + + /** Inserts a new key with value 1. Or increments an existing key by 1. */ + public void inc(String key) { + // Find node to process; use head if not exist + Node node = map.getOrDefault(key, head); + int count = removeKey(key) + 1; + + // Insert new node or populate existing node + if (node.next.count != count) insert(node, node.next, new Node(count, key)); + else node.next.keys.add(key); + map.put(key, node.next); + + // Clean up + if (node.keys.isEmpty()) remove(node); + } + + + /** Decrements an existing key by 1. If Key's value is 1, remove it from the data structure. */ + public void dec(String key) { + if (!map.containsKey(key)) return; + + Node node = map.get(key); + int count = removeKey(key) - 1; + if (count == 0) { // reduce to 0, delete + map.remove(key); + } else { // Insert new node or populate existing node + if (node.last.count != count) insert(node.last, node, new Node(count, key)); + else node.last.keys.add(key); + map.put(key, node.last); + } + + if (node.keys.isEmpty()) remove(node); + } + + /** Returns one of the keys with maximal value. */ + public String getMaxKey() { + if (map.isEmpty()) return ""; + return tail.last.keys.iterator().next(); + } + + /** Returns one of the keys with Minimal value. */ + public String getMinKey() { + if (map.isEmpty()) return ""; + return head.next.keys.iterator().next(); + } + + + // Helper Functions + + private void remove(Node node) { + node.last.next = node.next; + node.next.last = node.last; + } + + private int removeKey(String key) { + if (!map.containsKey(key)) return 0; + Node node = map.get(key); + node.keys.remove(key); + return node.count; + } + + private void insert(Node last, Node next, Node newNode) { + newNode.next = next; + next.last = newNode; + last.next = newNode; + newNode.last = last; + } +} + +/** + * Your AllOne object will be instantiated and called as such: + * AllOne obj = new AllOne(); + * obj.inc(key); + * obj.dec(key); + * String param_3 = obj.getMaxKey(); + * String param_4 = obj.getMinKey(); + */ +``` \ No newline at end of file diff --git a/Java/438. Find All Anagrams in a String.java b/Java/438. Find All Anagrams in a String.java new file mode 100755 index 0000000..7f29c57 --- /dev/null +++ b/Java/438. Find All Anagrams in a String.java @@ -0,0 +1,199 @@ +M +tags: Hash Table, Sliding Window, Two Pointers +time: O(n) +space: O(1) + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### Method1: Sliding Window, Hash Table. +- A creative way of using anagram char count `hash[c] >= 0` to determine if the curr c is a target char of the deesired anagram. + - because we always reduce hash[c]-- for all characters + - so only the anagram chars would be `hash[c] >= 0` after reducing. +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +- Slinding window always has left/right pointer: + - 1) at any given time move 1 index at a time: expand right window, process rsult, shrink left window + - 2) one of the basic goal is to maintain fixed window size +- algo: + - calc char freq of the target p, and store in a hash[256]; it will be used to distinguish anagram chars: `hash[c] >= 0` indicates a anagram char + - expand right window: move right to expand the window; ONLY when meeting a anagram char, count-- + - process result: if count reduces to 0, one anagram is found + - shrink left window: if (right - left) == p.length(), drop curr left char, and move forward +- how could we rely on only just `count == 0`? + - the hidden pre-condition is `right - left must already be p.length()`, which is validaterd in prev iteration +- time: O(n) +- space: O(1) + +#### Method2: HashTable +- count character apperance -> hash table, here just a int[26] + - use a window to record count++ and count--, in order to compare with countP +- prep the countP takes O(m) time +- time: O(n) + O(m) +- space: 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". +*/ + + +// Method1: Slide Window +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 = s.length(), m = p.length(); + + int[] hash = new int[256]; // prep p hash, tracks char # + for (char c : p.toCharArray()) hash[c]++; + + int left = 0, right = 0; + int count = m; // count of anagram chars. after reducing == 0, found 1 anagram + + while (right < n) { + // move right side of the window, if c exist in window, decrease count + char c = s.charAt(right++); + if (hash[c] > 0) count--; + hash[c]--; // if not exist in anagram, it'll drop to negative + + // if anagram char count reduced to 0, an anagram in s is found, mark start pos + if (count == 0) rst.add(left); + + // full range reached, so need to slide the window: + // 1. get val on left pos + // 2. add count back (when it is a anagram candidate) + // 3. + count back to hash + if (right - left == m) { + c = s.charAt(left++); // pick up the left char that will be dropped in next step + if (hash[c] >= 0) count++; // anagram candidate can be == 0 when evenly used; but can also be > 0 when there are extra + hash[c]++; + } + } + return rst; + } +} + +/* +// Method2: HashTable +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) +*/ +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 = s.length(), m = p.length(); + + // prep the count and window + int[] window = new int[26], countP = new int[26]; + for (int i = 0; i < m; i++) { + window[s.charAt(i) - 'a']++; + countP[p.charAt(i) - 'a']++; + } + + // compare at index = 0 + if (compare(window, countP)) rst.add(0); + + for (int i = m; i < n; i++) { + window[s.charAt(i) - 'a']++; + window[s.charAt(i - m) - 'a']--; + if (compare(window, countP)) rst.add(i - m + 1); + } + + return rst; + } + + private boolean compare(int[] window, int[] countP) { + for (int i = 0; i < 26; i++) { + if (window[i] != countP[i]) return false; + } + return true; + } +} + + + +/* +NOT recommend: +1. Two pointers with range of p.length(). O(n) +2. isAnagram(a, b). O(n) +=> Overall O(nm), bad solution +*/ +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/443. String Compression.java b/Java/443. String Compression.java new file mode 100755 index 0000000..c14b3de --- /dev/null +++ b/Java/443. String Compression.java @@ -0,0 +1,116 @@ +E +1557851233 +tags:String, Basic Implementation + +``` +/* +Given an array of characters, compress it in-place. + +The length after compression must always be smaller than or equal to the original array. + +Every element of the array should be a character (not int) of length 1. + +After you are done modifying the input array in-place, return the new length of the array. + + +Follow up: +Could you solve it using only O(1) extra space? + + +Example 1: + +Input: +["a","a","b","b","c","c","c"] + +Output: +Return 6, and the first 6 characters of the input array should be: ["a","2","b","2","c","3"] + +Explanation: +"aa" is replaced by "a2". "bb" is replaced by "b2". "ccc" is replaced by "c3". + + +Example 2: + +Input: +["a"] + +Output: +Return 1, and the first 1 characters of the input array should be: ["a"] + +Explanation: +Nothing is replaced. + + +Example 3: + +Input: +["a","b","b","b","b","b","b","b","b","b","b","b","b"] + +Output: +Return 4, and the first 4 characters of the input array should be: ["a","b","1","2"]. + +Explanation: +Since the character "a" does not repeat, it is not compressed. "bbbbbbbbbbbb" is replaced by "b12". +Notice each digit has it's own entry in the array. + + +Note: + +All characters have an ASCII value in [35, 126]. +1 <= len(chars) <= 1000. +*/ + +/* +- for loop, track index, compare [i, i+1], count occurance +- no need to clena up rest +- return final last index +- measure number over 10 + +edge case: +1 char: show itself +no char or null: return +*/ +class Solution { + public int compress(char[] chars) { + if (chars.length <= 1) { + return chars.length; + } + int index = 1, count = 1; + char c = chars[0]; + + // skip end case + for (int i = 0; i < chars.length - 1; i++) { + if (c == chars[i + 1]) { + count++; + continue; + } + + // next char + c = chars[i + 1]; + + // closure of existing section + if (count != 1) { + index = assignChar(chars, index, count); + } + chars[index++] = c; + count = 1; + } + + // end case + if (count != 1) { + index = assignChar(chars, index, count); + } + + // index is increased by 1 after use, which just equals the actual length + return index; + } + + private int assignChar(char[] chars, int index, int count) { + String s = String.valueOf(count); + for (char c : s.toCharArray()) { + chars[index++] = c; + } + return index; + } +} +``` \ No newline at end of file diff --git a/Java/448. Find All Numbers Disappeared in an Array.java b/Java/448. Find All Numbers Disappeared in an Array.java new file mode 100755 index 0000000..9091323 --- /dev/null +++ b/Java/448. Find All Numbers Disappeared in an Array.java @@ -0,0 +1,98 @@ +E +tags: Array, Bucket Sort +time: O(n) +space: O(1) + +#### Method1: Bucket Sort concept, set val to its correct position +- Given: values are [1,n], so val can represent index. Therefore, set val to its correct position +- 小心handle i: + - value是 1-based + - 每次换位, 需要`i--`, 重新省察`nums[i]` + +#### Method2: 做标记 (negative number, or super large number) +- Option1: use negative number to mark visited: + - 很巧妙地运用了标记的方法, 标记成负数,证明visit过。 + - Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! +- Option2: use large number (larger than n) + - 跟方法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] +*/ + +/* +Method1: +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 rst = new ArrayList(); + if (nums == null || nums.length == 0) return rst; + + for (int i = 0; i < nums.length; i++) { + int desiredIndex = nums[i] - 1; + if (nums[desiredIndex] != nums[i]) swap(nums, desiredIndex, i--); // i-- to revisit the new nums[i] + } + + // find missing elements + for (int i = 0; i < nums.length; i++) { + if (nums[i] != i + 1) rst.add(i + 1); + } + return rst; + } + + private void swap(int[] nums, int a, int b) { + int temp = nums[a]; + nums[a] = nums[b]; + nums[b] = temp; + } +} + + +/* +Method2: Marking it with negative number +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 rst = new ArrayList(); + if (nums == null || nums.length == 0) return rst; + + for (int i = 0; i < nums.length; i++) { + int desiredIndex = Math.abs(nums[i]) - 1; + nums[desiredIndex] = -Math.abs(nums[desiredIndex]); // mark negative + } + + for (int i = 0; i < nums.length; i++) { + if (nums[i] > 0) rst.add(i + 1); + } + + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/449. Serialize and Deserialize BST.java b/Java/449. Serialize and Deserialize BST.java new file mode 100755 index 0000000..5b5add0 --- /dev/null +++ b/Java/449. Serialize and Deserialize BST.java @@ -0,0 +1,102 @@ +M +tags: Tree +time: O(n) +space: O(n) + +#### DFS, Divide and Conquer, Preorder (utilizing BST) +- with BST, we can: + - skip adding the null nodes into the serialized string: `String NULL = "#"` + - In deserialization: use min/max boundary to check if queue.peek() can be added: + - if not meeting BST condition, skip this dfs and let other call to consume the queue +- Faster because it shortens the serialized string + + +#### DFS, Divide and Conquer, Preorder (w/o using BST) +- Take reference in Serialize and Deserialize Binary Tree +- The approach works but does not utilize Binary Search Tree properties + +``` + +/* +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 search tree. There is no restriction on how your serialization/deserialization algorithm should work. You just need to ensure that a binary search tree can be serialized to a string and this string can be deserialized to the original tree structure. + +The encoded string should be as compact as possible. + +Note: Do not use class member/global/static variables to store states. Your serialize and deserialize algorithms should be stateless. +*/ +// DFS utilizing binary search tree +public class Codec { + private final String DELI = ","; + + // Encodes a tree to a single string. + public String serialize(TreeNode root) { + StringBuffer sb = new StringBuffer(); + appendString(root, sb); + return sb.toString(); + } + private void appendString(TreeNode node, StringBuffer sb) { + if (node == null) return; + sb.append(node.val).append(DELI); + appendString(node.left, sb); + appendString(node.right, sb); + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + if (data.length() == 0) return null; + Queue queue = new LinkedList<>(Arrays.asList(data.split(DELI))); + return buildTree(queue, Integer.MIN_VALUE, Integer.MAX_VALUE); + } + + private TreeNode buildTree(Queue queue, int min, int max) { + if (queue.isEmpty()) return null; + String s = queue.peek(); + int currVal = Integer.parseInt(queue.peek()); + if (currVal < min || currVal > max) return null; + queue.poll(); + TreeNode node = new TreeNode(currVal); + node.left = buildTree(queue, min, currVal); + node.right = buildTree(queue, currVal, max); + return node; + } +} + +// DFS w/o utilizing Binary Search Tree +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(); + appendString(root, sb); + return sb.toString(); + } + private void appendString(TreeNode node, StringBuffer sb) { + if (node == null) { + sb.append(NULL).append(DELI); + } else { + sb.append(node.val).append(DELI); + appendString(node.left, sb); + appendString(node.right, sb); + } + } + + // Decodes your encoded data to tree. + public TreeNode deserialize(String data) { + Queue queue = new LinkedList<>(Arrays.asList(data.split(DELI))); + return buildTree(queue); + } + + private TreeNode buildTree(Queue queue) { + String val = queue.poll(); + if (val.equals(NULL)) return null; + TreeNode node = new TreeNode(Integer.parseInt(val)); + node.left = buildTree(queue); + node.right = buildTree(queue); + return node; + } +} +``` \ No newline at end of file diff --git a/Java/46. Permutations.java b/Java/46. Permutations.java new file mode 100755 index 0000000..6a3b840 --- /dev/null +++ b/Java/46. Permutations.java @@ -0,0 +1,272 @@ +M +tags: Backtracking, DFS, BFS, Permutation +time: O(n!) +space: O(n!) + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + +``` +/* +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 +Do it without recursion + +Tags Expand +Recursion Search + +*/ + + +/** +Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +Use queue in dfs: true T(n) = n * T(n-1), each time have 1 less item to check +*/ +class Solution { + public List> permute(int[] nums) { + List> rst = new ArrayList<>(); + if (nums == null || nums.length == 0) return rst; + + Queue queue = new LinkedList<>(); + for (int num : nums) queue.offer(num); // [1, 2, 3] + + dfs(rst, new ArrayList<>(), queue); + return rst; + } + + private void dfs(List> rst, List list, Queue queue) { + if (queue.isEmpty()) { + rst.add(new ArrayList<>(list)); + return; + } + int size = queue.size(); + while (size-- > 0) { + int val = queue.poll(); // queue maintains the remaining list in processing order: simulates pick/not-pick + list.add(val); + + dfs(rst, list, queue); + + // backtracking + list.remove(list.size() - 1); + queue.offer(val); + } + } +} + + +/** +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- Bad DFS Option, though still fast enough +- Use list.contains() to check. Technically, it is O(n^n), plus the `contains` is nlogn time + +*/ +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 list, int[] nums) { + if (list.size() == nums.length) { + result.add(new ArrayList<>(list)); + return; + } + for (int i = 0; i < nums.length; i++) { + if (list.contains(nums[i])) continue; + list.add(nums[i]); + dfs(result, list, nums); + list.remove(list.size() - 1); + } + } +} + +// Method1-Option3: Use visited[] to track, still causes for(over n items), not efficient +class Solution { + public List> permute(int[] nums) { + List> result = new ArrayList>(); + if (nums == null || nums.length == 0) return result; + + dfs(result, new ArrayList<>(), nums, new boolean[nums.length]); + return result; + } + + private void dfs(List> result, List list, int[] nums, boolean[] visited) { + if (list.size() == nums.length) { + result.add(new ArrayList<>(list)); + return; + } + for (int i = 0; i < nums.length; i++) { // still loop over O(n) + if (visited[i]) continue; + list.add(nums[i]); + visited[i] = true; + + dfs(result, list, nums, visited); + + list.remove(list.size() - 1); + visited[i] = false; + } + } +} + + +/* +#### Method2: Iterative, Option1: 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 rstSize = rst.size(); + for (int j = 0; j < rstSize; j++) { // pick base list from rst list + List baselist = rst.get(j); + // Pick slot to insert element at [n, baseList.size()) + for (int index = 0; index < baselist.size(); index++) { + baselist.add(index, nums[i]); + rst.add(new ArrayList<>(baselist)); // add new candidate back to rst + baselist.remove(index); + } + baselist.add(nums[i]); // add picked num to tail of base + } + } + return rst; + } +} + +/* +#### Method2: Iterative, Option2: use Queue to hold candidate list +- produce a list of candidate and put them into queue +- pull them out and try to expand: each execution generate a new candidate +- when candidate size == n, skip adding to queue, and just add to rst +- when processing queue item: for(n items) and do candidate.contains(nums[i]). + - this is not as efficient. +- to improve time: want to know the remaining items at each level +- maintain another large list of list, this wastes space + +*/ +class Solution { + public List> permute(int[] nums) { + List> rst = new ArrayList<>(); + if (nums == null || nums.length == 0) return rst; + + Queue> queue = new LinkedList<>(); + queue.offer(new ArrayList<>()); + + while (!queue.isEmpty()) { + int size = queue.size(); + while(size-- > 0) { + List list = queue.poll(); + if (list.size() == nums.length) { + rst.add(new ArrayList<>(list)); + continue; + } + populateQueue(queue, list, nums); + } + } + return rst; + } + + private void populateQueue(Queue> queue, List list, int[] nums) { + for (int num : nums) { + if (list.contains(num)) continue; // slow + list.add(num); + queue.offer(new ArrayList<>(list)); + list.remove(list.size() - 1); + } + } +} + +/* + #### Method2: Iterative, Option3: use queue, and calculate remain list on the fly + 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 { + public List> permute(int[] nums) { + List> rst = new ArrayList<>(); + if (nums == null || nums.length == 0) return rst; + + List numList = new ArrayList<>(); + for (int num : nums) numList.add(num); + + int n = nums.length; + Queue> queue = new LinkedList<>(); + queue.offer(new ArrayList<>()); + + while (!queue.isEmpty()) { + List list = queue.poll(); + if (list.size() == n) { + rst.add(new ArrayList<>(list)); + continue; + } + List candidates = new ArrayList<>(numList); + candidates.removeAll(list);// the remaining items to insert + for (int num : candidates) { + list.add(num); + queue.offer(new ArrayList<>(list)); + list.remove(list.size() - 1); + } + } + + return rst; + } +} + + +``` \ No newline at end of file diff --git a/Java/463. Island Perimeter.java b/Java/463. Island Perimeter.java new file mode 100755 index 0000000..301ea8b --- /dev/null +++ b/Java/463. Island Perimeter.java @@ -0,0 +1,120 @@ +E +tags: Hash Table +time: O(n) + +#### Brutle, Count Blocks and Walls +- 每个格子 +4 个墙; +- 每个shared的墙要减去: 从每个island走去另外一个, 都-1 (最终没面墙, -2) + +#### Hash Table +- 把每个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) return 0; + int[] dx = {1, -1, 0, 0}; + 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) continue; + count += 4; + for (int k = 0; k < dx.length; k++) { + int mX = i + dx[k], mY = j + dy[k]; + if (validate(grid, mX, mY)) count -= 1; + } + } + } + return count; + } + + private boolean validate(int[][] grid, int x, int y) { + return x >= 0 && y >= 0 && x < grid.length && y < grid[0].length && grid[x][y] == 1; + } +} + +// incomplete version using DFS. The code became complicated and not necessary. +/* +class Solution { + private int[] dx = {1, -1, 0, 0}; + private 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; + } + 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/47. Permutations II.java b/Java/47. Permutations II.java new file mode 100755 index 0000000..76732df --- /dev/null +++ b/Java/47. Permutations II.java @@ -0,0 +1,162 @@ +M +tags: Backtracking, DFS +time O(n!) +space: O(n!) + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +Similar to 46. Permutations, but with dedup + +#### Backtracking +- Sort the list, and on same level, if last element is the same as curr, skip this recursive call +- time O(n!) + +#### 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]在重复时候, 不用重新记录. + +``` +/* +Given a collection of numbers that might contain duplicates, +return all possible 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 + +*/ +// option1 with remaining Queue +class Solution { + public List> permuteUnique(int[] nums) { + if (nums == null || nums.length == 0) return new ArrayList<>(); + Queue queue = new LinkedList<>(); + Arrays.sort(nums); + for (int num : nums) queue.offer(num); // [1, 2, 3] + List> rst = new ArrayList<>(); + dfs(rst, new ArrayList<>(), queue); + return rst; + } + + private void dfs(List> rst, List list, Queue queue) { + if (queue.isEmpty()) { // + rst.add(new ArrayList<>(list)); + return; + } + int size = queue.size(); + Integer last = null; + while (size-- > 0) { + int val = queue.poll(); + if (last != null && last == val) { + queue.offer(val); + continue; + } + list.add(val); + dfs(rst, list, queue); + list.remove(list.size() - 1); + queue.offer(val); + last = val; + } + } +} + + +// option2: Use visited[i] to mark visited copies, then skip [i-1] item +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); + } +} + +``` \ No newline at end of file diff --git a/Java/485. Max Consecutive Ones.java b/Java/485. Max Consecutive Ones.java new file mode 100755 index 0000000..8c9ef85 --- /dev/null +++ b/Java/485. Max Consecutive Ones.java @@ -0,0 +1,49 @@ +E +tags: Array, Basic Implementation +time: O(n) +space: O(1) + +- preserve max +- 清零count + +``` +/* +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/49. Group Anagrams.java b/Java/49. Group Anagrams.java new file mode 100755 index 0000000..d64638f --- /dev/null +++ b/Java/49. Group Anagrams.java @@ -0,0 +1,71 @@ +M +tags: String, Hash Table +time: O(nk) +space: O(nk) + +给一串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 (too slow) +- 和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. + +Example: + +Input: ["eat", "tea", "tan", "ate", "nat", "bat"], +Output: +[ + ["ate","eat","tea"], + ["nat","tan"], + ["bat"] +] +Note: + +All inputs will be in lowercase. +The order of your output does not matter. + +*/ + +/* +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) return rst; + Map> map = new HashMap<>(); + for (String str : strs) { + String key = getKey(str); + map.putIfAbsent(key, new ArrayList<>()); + map.get(key).add(str); + } + // convert + for (List list : map.values()) rst.add(list); + return rst; + } + + private String getKey(String s) { // O(n) + int[] count = new int[26]; + for (char c : s.toCharArray()) count[c - 'a']++; + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < 26; i++) sb.append(count[i]); + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/493. Reverse Pairs.java b/Java/493. Reverse Pairs.java new file mode 100755 index 0000000..81212aa --- /dev/null +++ b/Java/493. Reverse Pairs.java @@ -0,0 +1,90 @@ +M +tags: Divide and Conquer, Merge Sort, BST, 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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; + + // for each index i in nums, need to cover [i+1, ... n-1] to satisfy `reverse pair` rule + // divide and conquer [i ~ n] into 2 sections: + // 1) solve subProblem(start,mid) & subProblem(mid+1, end), 2) cover the rest range: for all [i, mid], check against [mid+1, end]. + 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++; // `num/2.0` to avaoid overflow + count += j - (mid + 1); // use index i (ahead of mid point) to compare all elements after mid point + } + + Arrays.sort(nums, start, end + 1); // partial sort of range [start, end] inclusively + + return count; + } +} +``` \ No newline at end of file diff --git a/Java/496. Next Greater Element I.java b/Java/496. Next Greater Element I.java new file mode 100755 index 0000000..c630a59 --- /dev/null +++ b/Java/496. Next Greater Element I.java @@ -0,0 +1,86 @@ +E +tags: Stack, Hash Table +time: O(n) +space: O(n) + +#### stack +- use stack to hold all elements + - keep poping if `stack.peek() < num` + - use map to record (top, num) +- time O(n), run through base once and sub-sequence once +- space O(n), stack, map + +#### HashMap +- O(n) space, O(n^2) time worst case + +``` +/* +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. +*/ + +// stack +class Solution { + public int[] nextGreaterElement(int[] sub, int[] base) { + Map baseMap = generateSubMap(base); + for (int i = 0; i < sub.length; i++) { + sub[i] = baseMap.getOrDefault(sub[i], -1); + } + return sub; + } + + private Map generateSubMap(int[] base) { + Stack stack = new Stack<>(); + Map baseMap = new HashMap<>(); + for (int num : base) { + while (!stack.isEmpty() && stack.peek() < num) { + int pop = stack.pop(); + baseMap.put(pop, num); + } + stack.push(num); + } + return baseMap; + } +} + +// O(n^2) +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/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/5. Longest Palindromic Substring.java b/Java/5. Longest Palindromic Substring.java new file mode 100755 index 0000000..5af15fa --- /dev/null +++ b/Java/5. Longest Palindromic Substring.java @@ -0,0 +1,99 @@ +M +tags: String, DP +time: O(n^2) +space: O(n^2) + +给一个string, 找到最长的palindrome substring. + +Related: Longest Palindromic Subsequence, Palindrome Partioning II + +O(n^2) is not too hard to think of. How about O(n)? + +#### Method1: DP of interval +- Very similar to `216. Longest Palindromic Subsequence`, but this problem requires solid substring(i+1, j-1) to be palindromic +- Similarly: process i = n-1, from end so [i + 1, j] is always ready to consume +- boolean dp[i][j] to mark range (i, j) as palindrome or not. +- 在计算 dp[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- time: O(n^2) dp +- space: O(n^2) + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + + + +#### 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. + +Example +Given the string = "abcdzdcab", return "cdzdc". + +Challenge +O(n2) time is acceptable. Can you do it in O(n) time. + +Hide Company Tags Amazon Microsoft Bloomberg +Hide Tags String +Hide Similar Problems (H) Shortest Palindrome (E) Palindrome Permutation + + +*/ +// Method1: DP of interval +public class Solution { + public String longestPalindrome(String s) { + if (s == null || s.length() <= 1) return s; + int n = s.length(); + boolean dp[][] = new boolean[n][n]; + String str = String.valueOf(s.charAt(n - 1)); + for (int i = n - 1; i >= 0; i--) { + dp[i][i] = true; + for (int j = i + 1; j < n; j++) { + if (s.charAt(i) == s.charAt(j) && (i + 1 == j || dp[i + 1][j - 1])) { + dp[i][j] = true; + str = str.length() <= (j - i + 1) ? s.substring(i, j + 1) : str; + } + } + } + return str; + } +} + + +// 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; + } + } +} + + + +``` \ No newline at end of file diff --git a/Java/509. Fibonacci Number.java b/Java/509. Fibonacci Number.java new file mode 100755 index 0000000..aed8a60 --- /dev/null +++ b/Java/509. Fibonacci Number.java @@ -0,0 +1,120 @@ +E +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. + +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 + + +*/ + +// 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 { + public int fibonacci(int n) { + if (n <= 1) { + 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]; + } + 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 fibonacci(n - 1) + fibonacci(n - 2); + } +} + +``` \ No newline at end of file diff --git a/Java/51. N-Queens.java b/Java/51. N-Queens.java new file mode 100755 index 0000000..b3c0685 --- /dev/null +++ b/Java/51. N-Queens.java @@ -0,0 +1,188 @@ +H +tags: Backtracking +time: O(n!) +space: O(n^2) + +N-Queen 问题, 给数字n, 和 nxn board, 找到所有N-queens的答案. + +#### Backtracking +- 用dfs找所有情况, 每一个iteration, 从找一行里挑合适的点, dfs +- 选中的点加进candidate list 里面, 记得要backtracking. +- 每一个candidate都需要validation, 检查 row, col, 2 diagnal 有没有queen +- Backtracking by replacement: each row has 1 queen, so just store it in int[] columns (CC book solution) + +#### 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 + + +*/ + +// Simplified backtracking with int[] columns +class Solution { + public List> solveNQueens(int n) { + List> rst = new ArrayList<>(); + if (n <= 0) return rst; + placeQueen(rst, new int[n], 0); + return rst; + } + // build sequence row + private void placeQueen(List> rst, int[] columns, int row){ + if (row == columns.length) { + rst.add(createBoard(columns)); + } else { + for (int col = 0; col < columns.length; col++) { + if (validate(columns, row, col)) { + columns[row] = col; // place queen + placeQueen(rst, columns, row + 1); + } + } + } + } + + /* + Validate the prior row, colomn & diagnal + Case1, same column: col == newCol + Case2, same diagnal: Math.abs(row - newRow) == Math.abs(col - newCol) + */ + private boolean validate(int[] columns, int row, int col) { + for (int newRow = 0; newRow < row; newRow++) { + int newCol = columns[newRow]; + if (col == newCol || (Math.abs(row - newRow) == Math.abs(col - newCol))) { + return false; + } + } + return true; + } + + private List createBoard(int[] columns){ + int n = columns.length; + List board = new ArrayList<>(); + for (int row = 0; row < n; row++) { + StringBuffer sb = new StringBuffer(); + int queenCol = columns[row]; + for (int col = 0; col < n; col++) { + sb.append(queenCol == col ? "Q" : "."); + } + board.add(sb.toString()); + } + return board; + } +} + + +// Backtracking approach: using list to add/remove queen candidate +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; + } +} + + + +``` \ No newline at end of file diff --git a/Java/515. Find Largest Value in Each Tree Row.java b/Java/515. Find Largest Value in Each Tree Row.java new file mode 100755 index 0000000..5201b2b --- /dev/null +++ b/Java/515. Find Largest Value in Each Tree Row.java @@ -0,0 +1,88 @@ +M +tags: Tree, BFS, DFS +time: O(n) +space: O(n) + +#### Method1: DFS +- faster than BFS, using less space if not couting final rst: stack size, O(logn) +- time: O(n), visit all + +#### Method2: BFS with queue +- loop over queue level and record max + + +``` +/* +You need to find the largest value in each row of a binary tree. + +Example: +Input: + + 1 + / \ + 3 2 + / \ \ + 5 3 9 + +Output: [1, 3, 9] +*/ +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +/* +DFS, use level to track: 1) time to add new item, 2) index to insert max val +time: O(n) +space: O(n) +*/ +class Solution { + public List largestValues(TreeNode root) { + List rst = new ArrayList<>(); + dfs(rst, root, 0); + return rst; + } + + private void dfs(List rst, TreeNode node, int level) { + if (node == null) return; + if (level == rst.size()) rst.add(node.val); + else rst.set(level, Math.max(rst.get(level), node.val)); + dfs(rst, node.left, level + 1); + dfs(rst, node.right, level + 1); + } +} + +/* +BFS: +time: O(n) +space: O(n) +*/ +class Solution { + public List largestValues(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(); + int max = queue.peek().val; + while (size-- > 0) { + TreeNode node = queue.poll(); + max = Math.max(max, node.val); + if (node.left != null) queue.offer(node.left); + if (node.right != null) queue.offer(node.right); + } + rst.add(max); + } + + return rst; + } +} + +``` \ No newline at end of file diff --git a/Java/516. Longest Palindromic Subsequence.java b/Java/516. Longest Palindromic Subsequence.java new file mode 100755 index 0000000..fa29458 --- /dev/null +++ b/Java/516. Longest Palindromic Subsequence.java @@ -0,0 +1,199 @@ +M +tags: DP, Interval DP, Memoization, DFS +time: O(n^2) +space: O(n^2) + +给一个string s, 找最长的sub-sequence which is also palindrome. + +注意!subsequence并不是substring, 是可以skip letter / non-continuous character sequence + +#### Interval DP +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: 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 +*/ +// Method1, Option1 +class Solution { + public int longestPalindromeSubseq(String s) { + char[] ss = s.toCharArray(); + int n = s.length(); + int[][] dp = new int[n][n]; + + for (int i = n - 1; i >= 0; i--) { // starting from tail + dp[i][i] = 1; + for (int j = i + 1; j < n; j++) { + if (ss[i] == ss[j]) dp[i][j] = dp[i + 1][j - 1] + 2; + else dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]); + } + } + return dp[0][n - 1]; + } +} + +// Method1, Option2 +class Solution { + public int longestPalindromeSubseq(String s) { + char[] ss = 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] = ss[i] == ss[i + 1] ? 2 : 1; + + // len == 3 + for (int len = 3; len <= n; len++) { + // starting from head + for (int i = 0; i <= n - len; i++) { // `i <= n - len` to keep j bounded by n + int j = len + i - 1; + + // exclude ss[i] or exclude ss[j] + dp[i][j] = Math.max(dp[i + 1][j], dp[i][j - 1]); + + // if ss[i]==ss[j], include outter ss[i] and ss[j] to build larger palindrome from inner subsequence + if (ss[i] == ss[j]) dp[i][j] = Math.max(dp[i][j], dp[i + 1][j - 1] + 2); + } + } + return dp[0][n - 1]; + } +} + + +/** +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization +- Space: O(n^2) +- Time: O(n^2) +*/ + +class Solution { + Integer[][] memo = null; + public int longestPalindromeSubseq(String s) { + int n = s.length(); + memo = new Integer[n][n]; + return dfs(s, 0, n - 1); + } + + public int dfs(String s, int i, int j) { + if (memo[i][j] != null) return memo[i][j]; + if (i > j) return 0; + if (i == j) return 1; + if (s.charAt(i) == s.charAt(j)) memo[i][j] = dfs(s, i + 1, j - 1) + 2; + else memo[i][j] = Math.max(dfs(s, i + 1, j), dfs(s, i, j - 1)); + + return memo[i][j]; + } +} + +// Slight complex way, with initialization +class Solution { + int[][] memo = null; + public int longestPalindromeSubseq(String s) { + + if (s.length() == 1) return 1; + + int n = s.length(); + memo = new int[n][n]; + // init + for (int i = 0; i < n; i++) { + for (int j = 0; j < n; j++) memo[i][j] = -1; + } + + // len = 1 + for (int i = 0; i < n; i++) memo[i][i] = 1; + + // len = 2 + for (int i = 0; i < n - 1; i++) memo[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 (memo[x][y] != -1) return memo[x][y]; + if (s.charAt(x) == s.charAt(y)) { + dfs(s, x + 1, y - 1); + if (memo[x + 1][y - 1] != -1) memo[x][y] = memo[x + 1][y - 1] + 2; + } else { + memo[x][y] = Math.max(dfs(s, x + 1, y), dfs(s, x, y - 1)); + } + return memo[x][y]; + } +} + +``` diff --git a/Java/518. Coin Change 2.java b/Java/518. Coin Change 2.java new file mode 100755 index 0000000..7718825 --- /dev/null +++ b/Java/518. Coin Change 2.java @@ -0,0 +1,69 @@ +M +tags: DP, Backpack DP +time: O(n) +space: O(n) + +给串数字, 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[] dp = new int[amount + 1]; + dp[0] = 1; // for dp, not quite meaning full: 1 way to build 0 amount + for (int coin : coins) { + for (int i = coin; i <= amount; i++) { + dp[i] += dp[i - coin]; + } + } + return dp[amount]; + } +} +``` \ No newline at end of file diff --git a/Java/52. N-Queens II.java b/Java/52. N-Queens II.java new file mode 100755 index 0000000..7103ed6 --- /dev/null +++ b/Java/52. N-Queens II.java @@ -0,0 +1,109 @@ +H +tags: Backtracking +time: O(n!) +space: O(n) + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking (with replacement) +- Each row has just 1 Queen value +- As CC book suggests, use `int[] columns` of length n to store all queen col positions for n rows + - `int[] columns` is slightly easier to backtrack by updating certain index i with new col + - list will usualy has the add/remove pattern for backtracking + +#### 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. +*/ + +class Solution { + public int totalNQueens(int n) { + if (n <= 0) return 0; + return dfs(new int[n], 0); + } + + private int dfs(int[] columns, int row) { + if (row == columns.length) return 1; + int count = 0; + for (int col = 0; col < columns.length; col++) { + if (validate(columns, row, col)) { + columns[row] = col; // place queen + count += dfs(columns, row + 1); + } + } + return count; + } + + // Validate the prior row, colomn & diagnal + private boolean validate(int[] columns, int row, int col) { + for (int newRow = 0; newRow < row; newRow++) { + int newCol = columns[newRow]; + if (col == newCol || Math.abs(row - newRow) == Math.abs(col - newCol)) { + return false; + } + } + return true; + } +} + +/* +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; + } +} + +``` \ No newline at end of file diff --git a/Java/523. Continuous Subarray Sum.java b/Java/523. Continuous Subarray Sum.java new file mode 100755 index 0000000..c50e43f --- /dev/null +++ b/Java/523. Continuous Subarray Sum.java @@ -0,0 +1,139 @@ +M +tags: Math, DP, Coordinate DP, Subarray, PreSum +time: O(n) +space: O(k) + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + +``` +/* +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. +*/ +class Solution { + public boolean checkSubarraySum(int[] nums, int k) { + k = k == 0 ? Integer.MAX_VALUE : Math.abs(k); // set k to positive for moding usage + if (nums.length / 2 > k) return true; // Pigeonhole principle + + Set set = new HashSet<>(); + int lastMod = 0; + for (int num : nums) { + int mod = (lastMod + num) % k; + if (set.contains(mod)) return true; + set.add(lastMod); + lastMod = mod; + } + + return false; + } +} + + +/* +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/53. Maximum Subarray.java b/Java/53. Maximum Subarray.java new file mode 100755 index 0000000..cd8fdfc --- /dev/null +++ b/Java/53. Maximum Subarray.java @@ -0,0 +1,329 @@ +E +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 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) +/* + // 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); + } +*/ + + +``` +/** +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; + } +} + +/* + 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 { + public int maxSubArray(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int preSum = 0, minPreSum = 0; + int max = Integer.MIN_VALUE; + for (int num : nums) { + preSum += num; + max = Math.max(max, preSum - minPreSum); + minPreSum = Math.min(minPreSum, preSum); + } + 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, max = Integer.MIN_VALUE; + int[] dp = new int[n + 1]; // extra space to store dp[n] + dp[0] = 0; + for (int i = 1; i <= n; i++) { + // continuous so always will use nums[i-1] + dp[i] = Math.max(dp[i - 1] + nums[i - 1], 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; + } +} + + +/** + Slight diff: 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) { + if (nums == null || nums.length == 0) return 0; + + // init dp, global max + int n = nums.length, max = Integer.MIN_VALUE; + int[] dp = new int[n + 1]; + dp[0] = 0; + 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) { + if (nums == null || nums.length == 0) return 0; + int n = nums.length, max = Integer.MIN_VALUE; + int[] dp = new int[2]; + dp[0] = 0; + + 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; + } +} + + +/* +DFS, divide and conquer, +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) { + 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 + // find continuous max going towards left + int continuousLeftSumMax = findContinuousSum(mid-1, left, right, -1, nums); + // find continuous max going towards right + int continuousRightSumMax = findContinuousSum(mid+1, left, right, 1, nums); + maxSum = Math.max(maxSum, nums[mid] + continuousLeftSumMax + continuousRightSumMax); + return maxSum; + } + + // set up left/right bound, and use offset to customize for loop: avoid redundant code + private int findContinuousSum(int start, int left, int right, int offset, int[] nums) { + int continuousSum = 0, max = 0; + for (int i = start; i >= left && i <= right; i+=offset) { + continuousSum += nums[i]; + max = Math.max(max, continuousSum); + } + return max; + } +} + + + +/* +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; + } +} + +/* + 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 { + public int maxSubArray(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int preSum = 0, minPreSum = 0; + int max = Integer.MIN_VALUE; + for (int num : nums) { + preSum += num; + max = Math.max(max, preSum - minPreSum); + minPreSum = Math.min(minPreSum, preSum); + } + return max; + } +} + +``` \ No newline at end of file diff --git a/Java/543. Diameter of Binary Tree.java b/Java/543. Diameter of Binary Tree.java new file mode 100755 index 0000000..def5ae8 --- /dev/null +++ b/Java/543. Diameter of Binary Tree.java @@ -0,0 +1,89 @@ +E +tags: Tree +time: O(n) when non-balanced +space: O(n) when non-balanced + +找longest path (include or not include root) + +跟Binary Tree Maximum Path Sum 的想法一样: 处理single path, 或者combined path (do not include curr root) + +#### Singlepath and CombinedPath +- Option1: Use local single path max & global combined max + - Since the local combined diameter is used for comparision, but not used for specific calculation + - calculate path length (diameter), understand: + - for single path: child single path value + 1 (curr node) + - for combined path including curr node: left child single + right child path +- Option2: record local combined and single path for each iteration + - `int[]{combinedPath, singlePath}`; + - single path: pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; + - combined path `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`, find max from: + - 1) complete left child combined path + - 2) complete right child combined path + - 3) combined path with curr root + - Note: we treat a single node itself with diameter of 1, so we want to -1 in final result + - problem statement wants the path length (not # of nodes or depth) + +``` +/* +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. + + +*/ +// Option1: with global combined max +class Solution { + int globalCombinedMax = Integer.MIN_VALUE; + public int diameterOfBinaryTree(TreeNode root) { + if (root == null) return 0; + dfs(root); + return globalCombinedMax; + } + + private int dfs(TreeNode root) { + if (root == null) return 0; + + int left = dfs(root.left); + int right = dfs(root.right); + int localCombined = left + right; + globalCombinedMax = Math.max(globalCombinedMax, localCombined); // complete left/right child, or join curr root. + return Math.max(left , right) + 1; // pick single path + 1 + } +} + + +/* +Option2: +*/ +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 + curr node + 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/55. Jump Game.java b/Java/55. Jump Game.java new file mode 100755 index 0000000..2d38e18 --- /dev/null +++ b/Java/55. Jump Game.java @@ -0,0 +1,135 @@ +M +tags: Array, Greedy, DP +time: O(n) +space: O(1) + +给出步数,看能不能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 +- 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) +- space: O(1) + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (j + A[j] >= i), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- time: O(n^2) +- space: O(n) + + +``` +/* +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 1: + +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: + +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. + + +*/ + + +/* + +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. +*/ + +/* +#### Method1: Greedy. +Option1: +- 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 < nums.length; i++) { + farest = Math.max(farest, i + nums[i]); + if (farest >= nums.length - 1) { + return true; + } + if (farest <= i) { + return false; + } + } + return true; + } +} + +// Method1 Option2: even simpler, check from end. beat 100% +class Solution { + public boolean canJump(int[] nums) { + if (nums == null || nums.length == 0) return true; + int n = nums.length, lastPos = n - 1; + for (int i = lastPos; i >= 0; i--) { + if (i + nums[i] >= lastPos) lastPos = i; + } + return lastPos == 0; + } +} + +/* +#### Method2, DP +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 true; + int n = nums.length; + boolean[] dp = new boolean[n]; + dp[0] = true; + + for (int i = 1; i < n; i++) { + dp[i] = false; + for (int j = 0; j < i; j++) { // use pre-computed stages form [0 ~ i) to compute dp[i] + if (dp[j] && j + nums[j] >= i) { + dp[i] = true; + break; + } + } + } + + return dp[n - 1]; + } +} + + +``` \ No newline at end of file diff --git a/Java/557. Reverse Words in a String III.java b/Java/557. Reverse Words in a String III.java new file mode 100755 index 0000000..d436337 --- /dev/null +++ b/Java/557. Reverse Words in a String III.java @@ -0,0 +1,51 @@ +E +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/56. Merge Intervals.java b/Java/56. Merge Intervals.java new file mode 100755 index 0000000..9c77737 --- /dev/null +++ b/Java/56. Merge Intervals.java @@ -0,0 +1,245 @@ +M +tags: Array, Sort, Sweep Line, PriorityQueue +time: O(nlogn) +space: O(n) + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + +/* + // 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 1: + +Input: [[1,3],[2,6],[8,10],[15,18]] +Output: [[1,6],[8,10],[15,18]] +Explanation: Since intervals [1,3] and [2,6] overlaps, merge them into [1,6]. +Example 2: + +Input: [[1,4],[4,5]] +Output: [[1,5]] +Explanation: Intervals [1,4] and [4,5] are considered overlapping. + +*/ + +/* +Method1: sweep line, extra space used +- turn into point struct, sort +- mark start flag = 1, end flag = -1. struct { int val, flag = 1/-1;} +- when count == 0, close a period +*/ +class Solution { + public class Point { + int val, flag; + public Point(int val, int flag) { + this.val = val; + this.flag = flag; + } + } + + public int[][] merge(int[][] intervals) { + if (intervals == null) return intervals; + + PriorityQueue queue = build(intervals); + List list = compute(queue); + + int[][] result = new int[list.size()][2]; + for (int i = 0; i < list.size(); i++) { + result[i] = list.get(i); + } + return result; + } + + private List compute(PriorityQueue queue) { + List list = new ArrayList<>(); + + int count = 0; + int[] interval = new int[2]; + while (!queue.isEmpty()) { + Point p = queue.poll(); + if (count == 0) interval[0] = 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[1] = p.val; + list.add(interval); + interval = new int[2]; + } + } + return list; + } + + private PriorityQueue build(int[][] intervals) { + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(p -> p.val)); + for (int[] interval : intervals) { + queue.offer(new Point(interval[0], 1)); + queue.offer(new Point(interval[1], -1)); + } + return queue; + } +} + + +/** + Method2: Sort Intervals and append end logically + */ +// extra space used for rst O(n) +class Solution { + int START = 0; + int END = 1; + public int[][] merge(int[][] intervals) { + if (intervals == null) return intervals; + List rst = new ArrayList(); + + // Sort intervals + Arrays.sort(intervals, Comparator.comparing(i -> i[START])); + + int[] last = null; + for (int[] curr : intervals) { + if (last == null || last[END] < curr[START]) {// Found closure + rst.add(curr); + last = curr; + } else { // last != null && last[END] >= curr[START] + // Keep `last` to have the longest END index possible, + // so to merge all visited interval in-between + last[END] = Math.max(last[END], curr[END]); + } + } + + int[][] result = new int[rst.size()][2]; + for (int i = 0; i < rst.size(); i++) result[i] = rst.get(i); + return result; + } +} + + +// Old fn interface with List +class Solution { + public List merge(List intervals) { + List rst = new ArrayList<>(); + if (intervals == null || intervals.size() == 0) { + 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); + } + } + + return rst; + } +} + + + + +/* + Method3: + Space O(1), time: O(nlogn) + + 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) + Interval prev = intervals.get(0); + Interval curr; + + for (int i = 1; i < intervals.size(); i++) { + 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; + } +} + +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; + } +} + + +``` diff --git a/Java/560. Subarray Sum Equals K.java b/Java/560. Subarray Sum Equals K.java new file mode 100755 index 0000000..55d0a11 --- /dev/null +++ b/Java/560. Subarray Sum Equals K.java @@ -0,0 +1,87 @@ +M +tags: Array, Hash Table, PreSum, Subarray +time: O(n) +space: O(n) + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: 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 preSumCount = new HashMap<>(); + int n = nums.length, sum = 0, count = 0; + preSumCount.put(0, 1); + for (int num : nums) { + sum += num; + int priorSum = sum - k; + if (preSumCount.containsKey(priorSum)) { // # ways to sum up to priorSum + count += preSumCount.get(priorSum); + } + preSumCount.put(sum, preSumCount.getOrDefault(sum, 0) + 1); + } + return count; + } +} + +// Method2: PreSum, but still calculate each individual range +// time O(n^2), space O(n) +class Solution { + public int subarraySum(int[] nums, int k) { + 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 + 1] == k ? 1 : 0; + else count += (preSum[j + 1] - preSum[i]) == k ? 1 : 0; // range [i, j] + } + } + + return count; + } + + private int[] calcPreSum(int[] nums) { + int[] preSum = new int[nums.length + 1]; + for (int i = 1; i <= nums.length; i++) { + preSum[i] += preSum[i - 1] + nums[i - 1]; + } + return preSum; + } +} + + +``` \ No newline at end of file diff --git a/Java/561. Array Partition I.java b/Java/561. Array Partition I.java new file mode 100755 index 0000000..24f4d5c --- /dev/null +++ b/Java/561. Array Partition I.java @@ -0,0 +1,56 @@ +E +tags: Array +time: O(nlogn) +space: O(1) + +给串数字, 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发现规律: 升序排列会让 `高位的min(pair)` 最大化, 于是`一言不合先排列` +- 找到排列取单数位的规律,再考虑负数和正数的相同规律,即可找到排列求解的方法。 +- 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/567. Permutation in String.java b/Java/567. Permutation in String.java new file mode 100755 index 0000000..959a75f --- /dev/null +++ b/Java/567. Permutation in String.java @@ -0,0 +1,164 @@ +M +tags: Two Pointers, Sliding Window +time: O(m + n) +space: O(1) + +#### Method1: Sliding window with left/right Pointers +- Sliding window template: + - 1) Check right pointer and move right + - 2) Move left when necessary + - 3) Verify count == 0 & end state + - Note: normally 2) and 3) are in reversed order; this problem is a bit different +- This is efficient when the number of characters is not limited to 26, the runtime is still O(m + n) +- time: O(m + n), m = s1 length, n = s2 length +- space: O(k), k = # of possible chars, 26 in this case + +#### Method2: Two Pointer, but brutle verify freq count +- 如果做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) 这一步 +- time: (m + n) + - However, if # of possible chars is more than 26 + - For example, `k unique characters`, then the runtime will become: O(m + nk) + +- space: O(k), k = # of possible chars, 26 in this case + +``` +/* +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]. + +*/ + +// Method1: Sliding Window Two Pointers. left/right. O(n + m) +class Solution { + public boolean checkInclusion(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() > s2.length()) return false; + + Integer[] freq = countFreq(s1); + int n = s2.length(), left = 0, right = 0; + int count = s1.length(); + while (right < n) { + char c = s2.charAt(right); + // Verify and move right + if (freq[c] == null) {// reset + freq = countFreq(s1); + left = ++right; + count = s1.length(); + continue; + } + right++; + count--; + freq[c]--; + + // Move left when necessary + while(freq[c] < 0) { + freq[s2.charAt(left++)]++; + count++; + } + + // verify: + if (count == 0) return true; + } + return count == 0; + } + + private Integer[] countFreq(String s) { + Integer[] freq = new Integer[256]; + for (char c : s.toCharArray()) freq[c] = freq[c] == null ? 1 : freq[c] + 1; + return freq; + } +} + +// Method2: Optoin2, slight improvement by manually moving left when `freq[c]==null`, +// rather than calling `freq = countFreq(s1)`; +class Solution { + public boolean checkInclusion(String s1, String s2) { + if (s1 == null || s2 == null || s1.length() > s2.length()) return false; + + Integer[] freq = countFreq(s1); + int n = s2.length(), left = 0, right = 0; + int count = s1.length(); + while (right < n) { + char c = s2.charAt(right); + // Verify and move right + if (freq[c] == null) { + while (left != right) { // reset + c = s2.charAt(left++); + if (freq[c] != null) { + freq[c]++; + count++; + } + } + left = ++right; + count = s1.length(); + continue; + } + right++; + count--; + freq[c]--; + + // Move left when necessary + while(freq[c] < 0) { + freq[s2.charAt(left++)]++; + count++; + } + + // verify: + if (count == 0) return true; + } + return count == 0; + } + + private Integer[] countFreq(String s) { + Integer[] freq = new Integer[256]; + for (char c : s.toCharArray()) freq[c] = freq[c] == null ? 1 : freq[c] + 1; + return freq; + } +} + + +// Method2: Two Pointer, brutle check by updating freq map O(n + m) +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 m = s1.length(), n = s2.length(); + int[] charCount = new int[26]; + for (int i = 0; i < m; i++) { + charCount[s1.charAt(i) - 'a']++; + charCount[s2.charAt(i) - 'a']--; + } + + if (zeroCount(charCount)) return true; + + for (int i = m; i < n; i++) { + charCount[s2.charAt(i) - 'a']--; + charCount[s2.charAt(i - m) - '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/57. Insert Interval.java b/Java/57. Insert Interval.java new file mode 100755 index 0000000..fffe4a2 --- /dev/null +++ b/Java/57. Insert Interval.java @@ -0,0 +1,222 @@ +H +tags: Array, Sort, PriorityQueue, Sweep Line +time: O(n) +space: O(n) + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + +``` + +/* +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: + +Input: intervals = [[1,3],[6,9]], newInterval = [2,5] +Output: [[1,5],[6,9]] +Example 2: + +Input: intervals = [[1,2],[3,5],[6,7],[8,10],[12,16]], newInterval = [4,8] +Output: [[1,2],[3,10],[12,16]] +Explanation: Because the new interval [4,8] overlaps with [3,5],[6,7],[8,10]. +NOTE: input types have been changed on April 15, 2019. Please reset to default code definition to get new method signature. + +*/ +/** +Method1: convert to list first, and use a merge function +*/ +class Solution { + int START = 0, END = 1; + public int[][] insert(int[][] intervals, int[] newInterval) { + int n = intervals.length; + if (n == 0) return new int[][]{newInterval}; + List list = addInterval(intervals, newInterval); //1. Insert without merge + merge(list);//2. Merge + return toArray(list); // 3. convert back to int[][] + } + + private void merge(List list) { + int[] pre = list.get(0), curr = null; + for (int i = 1; i < list.size(); i++) { + curr = list.get(i); + if (pre[END] >= curr[START]) { + pre[END] = pre[END] > curr[END] ? pre[END] : curr[END]; + list.remove(i); + i--; + } else pre = curr; + } + } + + private List addInterval(int[][] intervals, int[] newInterval) { + List list = new LinkedList<>(); + Integer front = null; + for (int i = 0; i < intervals.length; i++) { + if (intervals[i][START] <= newInterval[START]) front = i + 1; // found the spot to insert + list.add(intervals[i]); + } + // insert newInterval + if (front == null) list.add(0, newInterval); + else list.add(front, newInterval); + return list; + } + + private int[][] toArray(List list) { + int[][] rst = new int[list.size()][2]; + for (int i = 0; i < list.size(); i++) { + rst[i][START] = list.get(i)[START]; + rst[i][END] = list.get(i)[END]; + } + return rst; + } +} + +/* Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) +*/ +class Solution { + public int[][] insert(int[][] intervals, int[] newInterval) { + int n = intervals.length; + int start = newInterval[0], end = newInterval[1]; + if (n == 0) return new int[][]{newInterval}; + + List list = new LinkedList<>(); + if (end < intervals[0][0]) list.add(0, new int[]{start, end}); //non-overlapping: head + + for (int i = 0; i < n; i++) { + int lo = intervals[i][0], hi = intervals[i][1]; + if (lo > end || hi < start) { + list.add(intervals[i]); // free interval to insert + if (hi < start && isEnd(intervals, end, i)) list.add(new int[]{start, end}); // non-overlapping insert + continue; + } + if (lo <= start && start <= hi) start = lo; + if (lo <= end && end <= hi) end = hi; + if (end == hi || isEnd(intervals, end, i)) list.add(new int[]{start, end}); // merge + } + + // non-overlapping: tail + if (end > intervals[n-1][1]) list.add(new int[]{start, end}); + return convert(list); + } + + private boolean isEnd(int[][] intervals, int end, int i) { + return i + 1 < intervals.length && end < intervals[i+1][0]; + } + + private int[][] convert(List list) { + int[][] rst = new int[list.size()][2]; + for (int i = 0; i < list.size(); i++) { + rst[i][0] = list.get(i)[0]; + rst[i][1] = list.get(i)[1]; + } + return rst; + } +} + + + + +/* +Method3: sweep line +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 { + class Point { + int val, flag; + public Point(int val, int flag) { + this.val = val; + this.flag = flag; + } + } + public int[][] insert(int[][] intervals, int[] newInterval) { + List rst = new LinkedList<>(); + PriorityQueue queue = buildQueue(intervals, newInterval); + + int count = 0, start = 0, end = 0; + while (!queue.isEmpty()) { // iterate over queue, count and build interval + Point p = queue.poll(); + if (count == 0) start = p.val; //detect start + count += p.flag; + + while (!queue.isEmpty() && p.val == queue.peek().val) { // overlapping point + p = queue.poll(); + count += p.flag; + } + + if (count == 0) rst.add(new int[]{start, p.val}); // detect end; end = p.val; + } + return convert(rst); + } + + private PriorityQueue buildQueue(int[][] intervals, int[] newInterval) { + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(p -> p.val)); + queue.offer(new Point(newInterval[0], 1)); + queue.offer(new Point(newInterval[1], -1)); + for (int[] interval : intervals) { + queue.offer(new Point(interval[0], 1)); + queue.offer(new Point(interval[1], -1)); + } + return queue; + } + + private int[][] convert(List list) { + int[][] rst = new int[list.size()][2]; + for (int i = 0; i < list.size(); i++) { + rst[i][0] = list.get(i)[0]; + rst[i][1] = list.get(i)[1]; + } + return rst; + } +} + + + + +``` diff --git a/Java/58. Length of Last Word.java b/Java/58. Length of Last Word.java new file mode 100755 index 0000000..f301e20 --- /dev/null +++ b/Java/58. Length of Last Word.java @@ -0,0 +1,76 @@ +E +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. + +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: +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 { + /** + * @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(); + } +} + +``` \ No newline at end of file diff --git a/Java/605. Can Place Flowers.java b/Java/605. Can Place Flowers.java new file mode 100755 index 0000000..8274042 --- /dev/null +++ b/Java/605. Can Place Flowers.java @@ -0,0 +1,48 @@ +E +tags: Array, Greedy +time: O(n) +space: O(1) + +#### Array +- just check flowerbed[i-1] & flowerbed[i+1] and count + +``` +/* +Suppose you have a long flowerbed in which some of the plots are planted and some are not. However, flowers cannot be planted in adjacent plots - they would compete for water and both would die. + +Given a flowerbed (represented as an array containing 0 and 1, where 0 means empty and 1 means not empty), and a number n, return if n new flowers can be planted in it without violating the no-adjacent-flowers rule. + +Example 1: +Input: flowerbed = [1,0,0,0,1], n = 1 +Output: True +Example 2: +Input: flowerbed = [1,0,0,0,1], n = 2 +Output: False +Note: +The input array won't violate no-adjacent-flowers rule. +The input array size is in the range of [1, 20000]. +n is a non-negative integer which won't exceed the input array size. +*/ + +/* + Greedy,just check flowerbed[i-1] and move on +*/ +class Solution { + public boolean canPlaceFlowers(int[] flowerbed, int n) { + if (flowerbed.length == 0 || n <= 0) return true; + int m = flowerbed.length; + for (int i = 0; i < m; i++) { + if (flowerbed[i] == 1) continue; + int lastVal = i == 0 ? 0 : flowerbed[i-1]; + int nextval = i == m-1? 0: flowerbed[i+1]; + if (lastVal == 0 && nextval == 0) { + flowerbed[i] = 1; + n--; + } + if (n <= 0) return true; + } + + return false; + } +} +``` \ No newline at end of file diff --git a/Java/611. Valid Triangle Number.java b/Java/611. Valid Triangle Number.java new file mode 100755 index 0000000..4780224 --- /dev/null +++ b/Java/611. Valid Triangle Number.java @@ -0,0 +1,94 @@ +M +tags: Array, Two Pointers +time: O(n^2) +space: O(logn), sorting space + +#### Method1: Fix max and backward counting +- Sort nums: O(nlogn) +- Set max value fixed on right side at k + - set 2nd value from right index j + - set last value at min index i + - if `nums[i] + nums[j] > nums[k]`: with fixed j, i can pick from [i, j-1] combinations + - then j--, to pick another j candidate + - maintain a window [i,j]; if invalid, move i++ +- time: O(n^2) +- Note: very similar to 3-sum, fixing 1 index and use 2 pointers to move window + +#### Method2: Fix min and forward counting +- Sort nums: O(nlogn) +- Set min value at i + - set 2nd value at j=i+1; and 3rd value at k=i+2 + - find max of k that fits into triangle + - count all possible k candidates from [j+1, k] + - then move j to a new candidate +- O(n^2) + +``` +/* +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]. +*/ + +/* +Method1: Fix max and backward counting +- Sort nums: O(nlogn) +- Set max value fixed on right side at k + - set 2nd value from right index j + - set last value at min index i + - maintain a window [i,j] that `nums[i] + nums[j] > nums[k]` +- O(n^2) +*/ +class Solution { + public int triangleNumber(int[] nums) { + + Arrays.sort(nums); + int n = nums.length, count = 0; + for (int k = n - 1; k >= 2; k--) { // k starting at max, decreasing + for (int i = 0, j = k - 1; i < j;) { // j starting at max, to make (nums[j] + nums[k]) at max + if (nums[i] + nums[j] > nums[k]) { + count += j - i; + j--; // move j down for new possibilities + } else i++; // when invalid, need to increase min value to make it valid + } + } + return count; + } +} + +/* +Method2: Fix minimum and forward counting +- Sort nums: O(nlogn) +- Set min value at i + - set 2nd value at j=i+1; and 3rd value at k=i+2 + - find max of k that fits into triangle + - count all possible k candidates from [j+1, k] + - then move j to a new candidate +- O(n^2) +*/ +class Solution { + public int triangleNumber(int[] nums) { + + Arrays.sort(nums); + int n = nums.length, count = 0; + for (int i = 0; i <= n - 3; i++) { // set min value at i + if (nums[i] == 0) continue; // pointless, skip + int k = i + 2; // pick k + for (int j = i + 1; j <= n - 2; j++) { // fix j + while (k < n && nums[i] + nums[j] > nums[k]) k++; // find k max + count += k - j - 1; // count all possible k candidates from [j+1, k] + } + } + return count; + } +} +``` \ No newline at end of file diff --git a/Java/62. Unique Path.java b/Java/62. Unique Path.java new file mode 100755 index 0000000..8620bbc --- /dev/null +++ b/Java/62. Unique Path.java @@ -0,0 +1,133 @@ +M +tags: Array, DP, Coordinate DP +time: O(mn) +space: O(mn), rolling array O(n) + +2D array, 算走到最右下角,有多少种方式. + +#### DP, 加法原理 +- 计数DP: 2 ways to reach (i,j): dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + - non-overlapping: `dp[i - 1][j]`, `dp[i][j - 1]` + - covers the only 2 possible way to reach (i,j) +- initialization: dp[i][0] = 1, dp[0][i] = 1 + - Of course, row i = 0, or col j = 0, there is only 1 way to reach +- time O(mn), space O(mn) + +##### 滚动数组 Rolling Array +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + +#### DFS + Memoization +- move from (0,0) towards (m, n) +- use Map as memoization technique + +``` +/* +A robot is located at the top-left corner of a m x n dp (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 dp (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 dp. How many possible unique paths are there? + +Tags Expand +Array Dynamic Programming + +*/ + +/* +DP: bottom-up approach. 加法原理 +'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 dp[x-1,y], and dp[x, y-1]. +dp[x][y] = Math.min(dp[x-1,y], dp[x, y-1]) + 1; + +Boundary: when x = 0, dp[0, 0~y] = 0~y; same for y=0, dp[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 + int[][] dp = new int[m][n]; + for (int i = 0; i < m; i++) dp[i][0] = 1; + for (int i = 0; i < n; i++) dp[0][i] = 1; + + // Calculate based on equation + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + return dp[m-1][n-1]; + } +} + +/* +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; + // Initialization + int[][] dp = new int[2][n]; + for (int i = 0; i < 2; i++) dp[i][0] = 1; + for (int i = 0; i < n; i++) dp[0][i] = 1; + + // Calculate based on equation + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + dp[i%2][j] = dp[(i - 1)%2][j] + dp[i%2][j - 1]; + } + } + return dp[(m-1)%2][n-1]; + } +} + +/* +DFS, Memoization +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 { + int m,n; + public int uniquePaths(int m, int n) { + if (m <= 1 || n <= 1) return 1; + this.m = m; + this.n = n; + return dfs(0, 0, new HashMap<>()); + } + + public int dfs(int x, int y, HashMap map) { + String key = getKey(x, y); + if (map.containsKey(key)) return map.get(key); + if (x >= m -1 || y >= n - 1) map.put(key, 1); + else map.put(key, dfs(x, y + 1, map) + dfs(x + 1, y, map)); + return map.get(key); + } + + private String getKey(int x, int y) { + return x + "," + y; + } +} + +``` \ No newline at end of file diff --git a/Java/621. Task Scheduler.java b/Java/621. Task Scheduler.java new file mode 100755 index 0000000..728815a --- /dev/null +++ b/Java/621. Task Scheduler.java @@ -0,0 +1,149 @@ +M +tags: Array, Greedy, Enumeration, Queue, PriorityQueue +time: O(n) +space: O(1) + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + +``` +/* +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]. + +*/ + + +// Method1: 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) -> b.count - a.count); + 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 section are all filled with idle interval + } + return count; + } +} +// Simplification: Only need the `letter count` to in the priorityQueue +class Solution { + public int leastInterval(char[] tasks, int n) { + if (tasks == null) return 0; + + // Prepare priority queue of Task count + PriorityQueue pq = new PriorityQueue<>(26, Collections.reverseOrder()); + int[] counts = new int[26]; + for (char c : tasks) counts[c - 'A']++; + for (int i = 0; i < counts.length; i++) { + if (counts[i] > 0) pq.offer(counts[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 + List list = new ArrayList<>(); + while (k > 0 && !pq.isEmpty()) { + Integer taskCount = pq.poll(); + if (taskCount-- > 1) list.add(taskCount); + count++; + k--; + } + + pq.addAll(list); // add valid task count back to pq + if (pq.isEmpty()) break; + count += k; // if k > 0, the section are all filled with idle interval + } + return count; + } +} + +// Method2: enumeration and analyaze +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); + } +} + +``` \ No newline at end of file diff --git a/Java/63. Unique Paths II.java b/Java/63. Unique Paths II.java new file mode 100755 index 0000000..517a012 --- /dev/null +++ b/Java/63. Unique Paths II.java @@ -0,0 +1,87 @@ +M +tags: Array, DP, Coordinate DP +time: O(mn) +space: O(mn) + +跟unique path的grid一样, 目标走到右下角, 但是grid里面可能有obstacle, 不能跨越. 求unique path 的count. + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +``` +/* +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. +*/ + +/* +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) return 0; + int m = obstacleGrid.length, 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; + dp[i][j] = 1; + } + for (int i = 0, j = 0; i < m; i++) { + if (obstacleGrid[i][j] == 1) break; + dp[i][j] = 1; + } + + for (int i = 1; i < m; i++) { + for (int j = 1; j < n; j++) { + if (obstacleGrid[i][j] == 1) continue; + dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + } + } + return dp[m - 1][n - 1]; + } +} + + +``` \ No newline at end of file diff --git a/Java/632. Smallest Range Covering Elements from K Lists.java b/Java/632. Smallest Range Covering Elements from K Lists.java new file mode 100755 index 0000000..7ba5533 --- /dev/null +++ b/Java/632. Smallest Range Covering Elements from K Lists.java @@ -0,0 +1,106 @@ +H +tags: Sliding Window, Two Pointers, Hash Table +time: O(nlogn), n = total elements +space: O(n) to store sorted list + +#### Method1: Sliding Window +- First sort all of the items together by actual val using `Node {int val, int row}` +- Slinding window goal: + - 1) use right to find range that touches all rows, + - 2) use left to shrink the range +- Sliding Window Template + - move right pointer + - Counts[i] = # of elements used in left/right range + - when counts[i] == 0, countUnique++; the number of row/list being included + - when count == row size: + - processing & save shorter range by using left/right Pointers + - move left pointer; when counts[i] == 0, countUnique-- +- time: O(nlogn) for initial sort and then O(n) to process +- space: O(n) +- What is hard here? To think of the idea of counting one usage of each row: + - when each all rows are used at least 1 time + - calculate the min dist + +#### Method2 PQ?, Similar to merging k array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104893/Java-Code-using-PriorityQueue.-similar-to-merge-k-array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/solution/ + +``` +/** +You have k lists of sorted integers in ascending order. Find the smallest range that includes at least one number from each of the k lists. + +We define the range [a,b] is smaller than range [c,d] if b-a < d-c or a < c if b-a == d-c. + + + +Example 1: + +Input: [[4,10,15,24,26], [0,9,12,20], [5,18,22,30]] +Output: [20,24] +Explanation: +List 1: [4, 10, 15, 24,26], 24 is in range [20,24]. +List 2: [0, 9, 12, 20], 20 is in range [20,24]. +List 3: [5, 18, 22, 30], 22 is in range [20,24]. + + +Note: + +The given list may contain duplicates, so ascending order means >= here. +1 <= k <= 3500 +-105 <= value of elements <= 105. + +*/ + +class Solution { + private class Node{ + int val, row; + public Node(int val, int row) { + this.val = val; + this.row = row; + } + } + + public int[] smallestRange(List> nums) { + + List list = sortByVal(nums); + int n = nums.size(), left = 0, right = 0, count = 0; + int minStartVal = -1, minDist = Integer.MAX_VALUE; + int[] rowFreq = new int[n]; // row freq + + // goal: 1) use right to find range that touches all rows, 2) use left to shrink the range + while (right < list.size()) { + Node node = list.get(right); + // move right: if this is very first time when this row is visited, count++ + if (rowFreq[node.row]++ == 0) count++; + // process count == n: all rows have been visisted + while (count == n) { + Node leftNode = list.get(left); + int dist = node.val - leftNode.val + 1; + if (dist < minDist) { + minStartVal = leftNode.val; + minDist = dist; + } + // move left + while (left <= right && leftNode.val == list.get(left).val) { + if (--rowFreq[list.get(left).row] == 0) count--; // this breaks `count==n` + left++; + } + } + right++; + } + + return new int[]{minStartVal, minStartVal + minDist - 1}; + } + + private List sortByVal(List> nums) { + List list = new ArrayList<>(); + for (int i = 0; i < nums.size(); i++) { + List row = nums.get(i); + for (int val : row) list.add(new Node(val, i)); + } + Collections.sort(list, Comparator.comparing(a -> a.val)); + return list; + } +} + +``` \ No newline at end of file diff --git a/Java/636. Exclusive Time of Functions.java b/Java/636. Exclusive Time of Functions.java new file mode 100755 index 0000000..56dfd4d --- /dev/null +++ b/Java/636. Exclusive Time of Functions.java @@ -0,0 +1,81 @@ +M +tags: Stack +time: O(n) +space: O(n) + +#### Stack +- Task time range: + - start range = next task timestamp - start.timestamp + - end range = curr task timestamp - last task timestamp + 1; because end node is inclusive. +- How to think of using stack: a task cannot finish until end is met; a early task cannot stop until a later task ends + - Alternatively, we can use a hashmap to track as well +- Keep track of the timestamp +- make sure to +1 when end node is met because end task is inclusive to this finishing task + + +``` +/* +On a single threaded CPU, we execute some functions. Each function has a unique id between 0 and N-1. + +We store logs in timestamp order that describe when a function is entered or exited. + +Each log is a string with this format: "{function_id}:{"start" | "end"}:{timestamp}". For example, "0:start:3" means the function with id 0 started at the beginning of timestamp 3. "1:end:2" means the function with id 1 ended at the end of timestamp 2. + +A function's exclusive time is the number of units of time spent in this function. Note that this does not include any recursive calls to child functions. + +The CPU is single threaded which means that only one function is being executed at a given time unit. + +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 the beginning of time 0, then it executes 2 units of time and reaches the end of time 1. +Now function 1 starts at the beginning of time 2, executes 4 units of time and ends at time 5. +Function 0 is running again at the beginning of time 6, and also ends at the end of time 6, thus executing for 1 unit of time. +So function 0 spends 2 + 1 = 3 units of total time executing, and function 1 spends 4 units of total time executing. + +Note: + +1 <= n <= 100 +Two functions won't start or end at the same time. +Functions will always log when they exit. + +*/ + +/* +A task cannot finish until end is met; a early task cannot stop until a later task ends. Use a stack. +*/ +class Solution { + public int[] exclusiveTime(int n, List logs) { + + int[] rst = new int[n]; + Stack stack = new Stack<>(); + int timestamp = 0; + for (String log : logs) { + String[] parts = log.split(":"); + int task = Integer.parseInt(parts[0]); + boolean start = parts[1].equals("start"); + int time = Integer.parseInt(parts[2]); + + // Update last record's time + if (!stack.isEmpty()) rst[stack.peek()] += time - timestamp; + timestamp = time; + + if (start) stack.push(task); + else { + int endTask = stack.pop(); + rst[endTask] += 1; // inclusive + timestamp++; + } + } + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/639. Decode Ways II.java b/Java/639. Decode Ways II.java new file mode 100755 index 0000000..3adfcf6 --- /dev/null +++ b/Java/639. Decode Ways II.java @@ -0,0 +1,124 @@ +H +tags: DP, Partition DP, Enumeration +time: O(n) +space: O(n) + +给出一串数字, 要翻译(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]) +- Enumeration: + - 具体分析 '*' 出现的位置, 枚举出数字, 基本功. + - 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) + - 枚举好以后, 其实这个题目的写法和思考过程都不难 +- Mode: + 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. + + +#### DFS + memoization +- DFS top-down approach is used to analyze the problem. The logic flow: +- 1) consider the case of 1 letter or 2 letters. +- 2) one letter: + - [*]: + 9 * dfs(s, i + 1) + - [0~9]: + dfs(s, i + 1) +- 3) two letters: + - [_, *]: depends + - [*, _]: depends + - [*, *]: + 15 * dfs(s, i + 2) +- memo[i] records # of ways to decode from [i ~ n] +- space: O(n), Size of recursion tree can go upto n +- time: O(n), `memo array is filled exactly once`!!! + +``` +/* +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(), mod = 1000000007; + + char[] ss = s.toCharArray(); + long[] dp = new long[n + 1]; + dp[0] = 1; // need use 1 as base for calculating product + + for (int i = 1; i <= n; i++) { + dp[i] += dp[i - 1] * countSingle(ss[i - 1]); + if (i >= 2) dp[i] += dp[i - 2] * countDouble(ss[i - 2], ss[i - 1]); + dp[i] %= mod; + } + + return (int)dp[n]; + } + + // count single digit variations + private int countSingle(char c) { + if (c == '*') return 9; // 9 ways + if (c == '0') return 0; // not applicable + return 1; // regular char + } + + // enumerate a + private int countDouble(char a, char b) { + // eliminate invalid cases for a + if (a == '0' || (a >= '3' && a <= '9')) return 0; // not applicable to build 2-digit. a must be in [1, 2] + + // enumerate a == 1, a == 2 + if (a == '1') return b == '*' ? 9 : 1; // b can be *=[1~9] or one of [0~9] + if (a == '2') { // b can be *=[1~6] or one of [0~6] + if (b == '*') return 6; + if (b <= '6') return 1; + return 0; + } + + // 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/64. Minimum Path Sum.java b/Java/64. Minimum Path Sum.java new file mode 100755 index 0000000..d6f0ddb --- /dev/null +++ b/Java/64. Minimum Path Sum.java @@ -0,0 +1,161 @@ +M +tags: Array, DP, Coordinate DP +time: O(mn) +space: O(n) rolling array + +#### 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(N) + - 1) 需要在同一个for loop里面完成initialization, 2) reuse dp[i][j] + - Reason: dp[i % 2][j] 在被计算出来的时候, 马上在下一轮会被用. 被覆盖前不用,就白算 + - Option3 below initialize dp outside of loop: 看起来固然简单, 但是不方便空间优化 + +#### DFS (top-down) Thinking process +- Enumerate the possible 2 paths: go right, go down +- sub problem: dfs(i+1,j), dfs(i,j+1); pick the min of the two +- memoization: after the path from a point (i,j) to end is computed, record memo[i][j] to avoid repatative calculation +- time: O(mn), only visite and record memo[i][j] once. +- space: O(mn) extrem case of m=100000, n = 2; the stack height is O(mn) +``` +/* +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 +*/ + +/* +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] + +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] + +*/ +// Option1: inline use `Integer.MAX_VALUE` to handle `i==0` or `j==0` cases +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, n = grid[0].length; + int[][] dp = new int[m][n]; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = grid[i][j]; // Initialize + if (i == 0 && j == 0) continue; // skip starting point + // 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); + + } + } + + 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]; + } +} + +// Optional2: Not 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 m = grid.length, n = grid[0].length; + int[][] dp = new int[m][n]; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + dp[i][j] = grid[i][j]; // Initialize + if (i == 0 && j == 0) continue; + 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 dp[i][j] += Math.min(dp[i - 1][j], dp[i][j - 1]); // Calculate DP + } + } + return dp[m - 1][n - 1]; + } +} + +// Optional2: 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, n = grid[0].length; + int[][] dp = new int[2][n]; + + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + dp[i%2][j] = grid[i][j]; // Initialize + if (i == 0 && j == 0) continue; + 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 dp[i%2][j] += Math.min(dp[(i - 1)%2][j], dp[i%2][j - 1]); // Calculate DP + } + } + return dp[(m - 1)%2][n - 1]; + } +} + +// Optoin3: init the 1st row, 1st col outside: 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, 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]; + + // 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]; + } +} +``` \ No newline at end of file diff --git a/Java/65. Valid Number.java b/Java/65. Valid Number.java new file mode 100755 index 0000000..6433660 --- /dev/null +++ b/Java/65. Valid Number.java @@ -0,0 +1,59 @@ +H +tags: Math, String, Enumeration +time: O(n) +space: O(1) + +分析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; // `.` exists, or it is part of e, 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; // start checking integer suffix to `e` + } else if (c == '+' || c == '-') { + if (i >= 1 && s.charAt(i - 1) != 'e') return false; + } else return false; // all other characters + } + + return isNum; + } +} +``` \ No newline at end of file diff --git a/Java/665. Non-decreasing Array.java b/Java/665. Non-decreasing Array.java new file mode 100755 index 0000000..d45961b --- /dev/null +++ b/Java/665. Non-decreasing Array.java @@ -0,0 +1,70 @@ +E +tags: Array +time: O(n) +space: O(1) + +- 比较升序的时候, 必须要估计到 `i-1, i, i+1` 三个数位. +- 写出来`i-1, i, i+1`之间的关系, 然后做合理的fix. + 1. reduce nums[i+1] to fix + 1. raise nums[i+1] to 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]) { // 遇到case + // 两种换数方式 + if (i >= 1 && nums[i - 1] < nums[i + 1]) { + nums[i] = nums[i + 1]; // reduce nums[i+1] to fix + } else if (i >= 1 && nums[i - 1] > nums[i + 1]) { + nums[i + 1] = nums[i]; // raise nums[i+1] to fix + } + count++; + } + if (count >= 2) { + return false; + } + } + return true; + } +} + + +``` \ No newline at end of file diff --git a/Java/67. Add Binary.java b/Java/67. Add Binary.java new file mode 100755 index 0000000..e76ee27 --- /dev/null +++ b/Java/67. Add Binary.java @@ -0,0 +1,58 @@ +E +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 + +#### reverse +- Reverse string -> Convert to Integer List, add up -> Convert back to string +- pointer 从前向后, 所以只需要 1个pointer. +- 操作复杂, 如上, 证明可以解决. 没必要reverse. + +#### Incorrect: convert to Integer +把binary换成数字作加法. 如果input很大,那么很可能int,long都hold不住。不保险。 + +``` +/* +Given two binary strings, return their sum (also a binary string). + +The input strings are both non-empty and contains only characters 1 or 0. + +Example 1: + +Input: a = "11", b = "1" +Output: "100" +Example 2: + +Input: a = "1010", b = "1011" +Output: "10101" +*/ +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 +*/ + + + +``` diff --git a/Java/671. Second Minimum Node In a Binary Tree.java b/Java/671. Second Minimum Node In a Binary Tree.java new file mode 100755 index 0000000..43bc23f --- /dev/null +++ b/Java/671. Second Minimum Node In a Binary Tree.java @@ -0,0 +1,98 @@ +E +tags: Tree, BFS +time: O(n) +space: O(n) leaf nodes + +#### BFS +- min tree: parent node is the min of left/right child +- BFS to traverse the tree and find 1st non-root smallest val +- Improvement area: when `node.val >= nextMin`, no need to dive into node children since it is a min Tree. + +#### DFS +- Find left and right val: + - if left/right equals root.val, that means the left or right sub children could have larger number + - Therefore DFS into left or right +- compare and return min(left, right) + +``` +/* +Given a non-empty special binary tree consisting of nodes with the non-negative value, where each node in this tree has exactly two or zero sub-node. If the node has two sub-nodes, then this node's value is the smaller value among its two sub-nodes. More formally, the property root.val = min(root.left.val, root.right.val) always holds. + +Given such a binary tree, you need to output the second minimum value in the set made of all the nodes' value in the whole tree. + +If no such second minimum value exists, output -1 instead. + +Example 1: + +Input: + 2 + / \ + 2 5 + / \ + 5 7 + +Output: 5 +Explanation: The smallest value is 2, the second smallest value is 5. + + +Example 2: + +Input: + 2 + / \ + 2 2 + +Output: -1 +Explanation: The smallest value is 2, but there isn't any second smallest value. +*/ + + +/* +Method1: Brutle Force, BFS +- min tree: parent node is the min of left/right child +- 2nd mini will be < root/min +- BFS to find 1st non-root smallest val +*/ +class Solution { + public int findSecondMinimumValue(TreeNode root) { + Integer nextMin = null; + int min = root.val; + + Queue queue = new LinkedList<>(); + queue.offer(root); + + while(!queue.isEmpty()) { + int size = queue.size(); + while(size-- > 0) { + TreeNode node = queue.poll(); + // optimization: no need to work on this subtree + if (nextMin != null && node.val >= nextMin) continue; + + if (node.left != null) queue.offer(node.left); + if (node.right != null) queue.offer(node.right); + if (node.val != min) { + nextMin = nextMin == null ? node.val : Math.min(nextMin, node.val); + } + } + } + + return nextMin != null ? nextMin : -1; + } +} + +// Method2: DFS +class Solution { + public int findSecondMinimumValue(TreeNode root) { + if (root == null) return -1; + if (root.left == null && root.right == null) return -1; + + int left = root.left.val, right = root.right.val; + if (root.val == left) left = findSecondMinimumValue(root.left); + if (root.val == right) right = findSecondMinimumValue(root.right); + + if (left != -1 && right != -1) return Math.min(left, right); + if (left != -1) return left; + return right; + } +} +``` \ No newline at end of file diff --git a/Java/674. Longest Continuous Increasing Subsequence.java b/Java/674. Longest Continuous Increasing Subsequence.java new file mode 100755 index 0000000..767f430 --- /dev/null +++ b/Java/674. Longest Continuous Increasing Subsequence.java @@ -0,0 +1,123 @@ +E +tags: Array, DP, Coordinate DP, Sliding Window +time: O(n) +space: O(1) + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + +``` +/* +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. + + */ + +// Method1 :Sliding window: record the starting point of the slop +class Solution { + public int findLengthOfLCIS(int[] nums) { + if (nums == null || nums.length == 0) return 0; + int n = nums.length, windowStart = 0; + int max = 1; + for (int i = 1; i < n; i++) { + if (nums[i] <= nums[i - 1]) windowStart = i; + max = Math.max(max, i - windowStart + 1); + } + 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 = 1, size = 1; + for (int i = 1; i < nums.length; i++) { + if (nums[i] > nums[i - 1]) size++; + else size = 1; + max = Math.max(size, max); + } + return max; + } +} + +/* +Method2: DP +'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; + // Below can just be dp[i] = dp[i - 1] + 1; since dp[i - 1] + 1 is always greater than 1 + if (nums[i] > nums[i - 1]) dp[i] = Math.max(dp[i], dp[i - 1] + 1); + max = Math.max(max, dp[i]); + } + return max; + } +} + +// rolling array 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; + // Below can just be dp[i] = dp[i - 1] + 1; since dp[i - 1] + 1 is always greater than 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; + } +} + +``` diff --git a/Java/68. Text Justification.java b/Java/68. Text Justification.java new file mode 100755 index 0000000..74b346a --- /dev/null +++ b/Java/68. Text Justification.java @@ -0,0 +1,145 @@ +H +tags: String, Enumeration +time: O(n) go over words +space: O(maxLength) buffer list + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- greedy approach: line up as many words as possible; once exceed the MaxLength, justify the list of words +- Steps + - 1) Split & group + - 2) Juststify a row of words + - 3) clean up last row +- Calcualte bounded row length = `width + (list.size() - 1)`. `list.size()-1` = Minimum amount of slot/space used. +- Calculate max ave spaces ot insert into each slot = `totalExtraSpace/slot` +- `Juststify a row of words`: + - 1) take a list of words and assume minimum 1 space in-between words + - 2) distribute the remaining spaces evenly to each slot +- 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; + + // 1) Split & group + for (String s : words) { + // when size reach maxWidth, comebine list[s] into s + if (getRowLength(list, width) + s.length() + 1 > maxWidth) { + // 2) Juststify a row of words + rst.add(justify(list, width, maxWidth)); + list = new ArrayList<>(); + width = 0; + } + list.add(s); + width += s.length(); + } + + if (list.size() == 0) return rst; + + // 3) 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; + } + + // word total width + (n - 1) spaces + private int getRowLength(List list, int width) { + return width + (list.size() - 1); // overall width + (n - 1) spaces + } + + // Juststify a row of words + private String justify(List list, int width, int max) { + int slot = list.size() - 1; // # of slots in-between words + int extraSpace = max - width; // additional space to consume + if (slot == 0) return list.get(0) + generateSpace(extraSpace); // single word, just return + + int length = extraSpace / slot; // max avg length to insert in-between words + int remain = extraSpace % slot; // remaining extra spaces + String space = generateSpace(length); + StringBuffer sb = new StringBuffer(); + // aggregate first i word + for (int i = 0; i < slot; i++) { + sb.append(list.get(i)).append(space); + if (i < remain) sb.append(" "); + } + // append last word + sb.append(list.get(slot)); + sb.append(generateSpace(max - sb.length())); + return sb.toString(); + } + + private String generateSpace(int n) { + if (n <= 0) return ""; + StringBuffer sb = new StringBuffer(); + for (int i = 0; i < n; i++) sb.append(" "); + return sb.toString(); + } +} + + +``` \ No newline at end of file diff --git a/Java/680. Valid Palindrome II.java b/Java/680. Valid Palindrome II.java new file mode 100755 index 0000000..fe8102b --- /dev/null +++ b/Java/680. Valid Palindrome II.java @@ -0,0 +1,70 @@ +E +tags: String + +#### Palindrome String +- delete an index: 有两种情况 +- 用一个boolean parameter来表现state. 如果有其他status, state可以变成 String/enum + +``` +/* +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 state) { + if (start > end) return false; + while (start < end) { + if (s.charAt(start) == s.charAt(end)) { + start++; + end--; + continue; + } else if (state) { + 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 state) { + if (start > end) return false; + while (start < end) { + if (s.charAt(start++) == s.charAt(end--)) { + continue; + } else if (state) { + 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/686. Repeated String Match.java b/Java/686. Repeated String Match.java new file mode 100755 index 0000000..d017279 --- /dev/null +++ b/Java/686. Repeated String Match.java @@ -0,0 +1,58 @@ +E +1557812783 +tags:String, Basic Implementation, Edge Case + +Track: 纸上分析edge case. +Validation helps speed it up. + +``` + +/* +Given two strings A and B, find the minimum number of times A has to be repeated such that B is a substring of it. If no such solution, return -1. + +For example, with A = "abcd" and B = "cdabcdab". + +Return 3, because by repeating A three times (“abcdabcdabcd”), B is a substring of it; and B is not a substring of A repeated two times ("abcdabcd"). + +Note: +The length of A and B will be between 1 and 10000. +*/ +/* +Keep Index of until finding it? +validaion optimization: all B chars should appear in A, to avoid unused calculation. +brutal: keep appending A to sb and do indexOf(B), until sb longer than B +edge case: one extra A should cover the bridge case +*/ +class Solution { + public int repeatedStringMatch(String A, String B) { + if (!validate(A, B)) return -1; + int count = 0; + StringBuffer sb = new StringBuffer(); + while (sb.length() < B.length()) { + count++; + sb.append(A); + } + if (sb.indexOf(B) != -1) return count; + if (sb.append(A).indexOf(B) != -1) return count + 1; + return -1; + } + + private boolean validate(String A, String B) { + boolean[] checkA = new boolean[256]; + boolean[] checkB = new boolean[256]; + + for (char c : A.toCharArray()) { + checkA[c] = true; + } + for (char c : B.toCharArray()) { + checkB[c] = true; + } + for (int i = 0; i < 256; i++) { + if (checkB[i] && !checkA[i]) { + return false; + } + } + return true; + } +} +``` \ No newline at end of file diff --git a/Java/689. Maximum Sum of 3 Non-Overlapping Subarrays.java b/Java/689. Maximum Sum of 3 Non-Overlapping Subarrays.java new file mode 100755 index 0000000..2fb628c --- /dev/null +++ b/Java/689. Maximum Sum of 3 Non-Overlapping Subarrays.java @@ -0,0 +1,113 @@ +H +tags: Array, DP +time: O(n) +space: O(n) + +#### DP, Divide and conquer +- split into 3 parts [0, i -1], [i, i + k -1]. [i + k, n - 1] +- NOTE: be very careful about index handling: + - `presum[i + 1] - presum[0]` gives inclusive range of `[0, i]` +- Use DP to record the starting position of max sum, +- inspired by: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C%2B%2BJava-DP-with-explanation-O(n) + - 1) calculate preSum with range [0, n] + - 2) calculate leftMaxIndex[], rightMaxIndex[] + - 3) test middle range to find max solution +- Note: the test range for 1, 2, 3 always start with assumption that k has been consumed from one side +- Note: When need to record at max/min value change, we can check/assign it manually (rather than use a object to carry & sort) + + +``` +/* +In a given array nums of positive integers, find three non-overlapping subarrays with maximum sum. + +Each subarray will be of size k, and we want to maximize the sum of all 3*k entries. + +Return the result as a list of indices representing the starting position of each interval (0-indexed). +If there are multiple answers, return the lexicographically smallest one. + +Example: + +Input: [1,2,1,2,6,7,5,1], 2 +Output: [0, 3, 5] +Explanation: Subarrays [1, 2], [2, 6], [7, 5] correspond to the starting indices [0, 3, 5]. +We could have also taken [2, 1], but an answer of [1, 3, 5] would be lexicographically larger. + + +Note: + +nums.length will be between 1 and 20000. +nums[i] will be between 1 and 65535. +k will be between 1 and floor(nums.length / 3). +*/ + +/* +- Divide and conquer: split into 3 parts [0, i -1], [i, i + k -1]. [i + k, n - 1] +- Use DP to record the starting position of max sum, inspired by: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C%2B%2BJava-DP-with-explanation-O(n) + - 1) calculate preSum with range [0, n] + - 2) calculate leftMaxIndex[], rightMaxIndex[] + - 3) test middle range to find best solution +- Note: the test range for 1, 2, 3 always start with assumption that k has been consumed from one side +*/ + +class Solution { + public int[] maxSumOfThreeSubarrays(int[] nums, int k) { + + int n = nums.length, max = 0; + int[] ans = new int[3]; + int[] presum = calcPresum(nums); + int[] left = calcLeft(nums, presum, k); + int[] right = calcRight(nums, presum, k); + + // process middle part + for (int i = k; i <= n - 2 * k; i++) { + int low = left[i - 1], high = right[i + k]; + int total = (presum[i + k] - presum[i]) + + (presum[low + k] - presum[low]) + + (presum[high + k] - presum[high]); + if (total > max) { + max = total; + ans[0] = low; + ans[1] = i; + ans[2] = high; + } + } + + return ans; + } + + private int[] calcLeft(int[] nums, int[] presum, int k) { + int n = nums.length; + int[] left = new int[n]; + for (int i = k, total = presum[k] - presum[0]; i < n; i++) { + int sum = presum[i + 1] - presum[i + 1 - k]; // sum of range [i - k + 1, i], size = k + if (sum > total) { + left[i] = i + 1 - k; + total = sum; + } else left[i] = left[i - 1]; + } + + return left; + } + + private int[] calcRight(int[] nums, int[] presum, int k) { + int n = nums.length; + int[] right = new int[n]; + right[n - k] = n - k; + for (int i = n - k - 1, total = presum[n] - presum[n - k]; i>= 0; i--) { + int sum = presum[i + k] - presum[i]; // sum of range [i, i + k - 1], size = k + if (sum >= total) { + right[i] = i; + total = sum; + } else right[i] = right[i + 1]; + } + return right; + } + + private int[] calcPresum(int[] nums) { + int[] presum = new int[nums.length + 1]; + for (int i = 1; i <= nums.length; i++) + presum[i] += presum[i - 1] + nums[i - 1]; + return presum; + } +} +``` \ No newline at end of file diff --git a/Java/69. Sqrt(x).java b/Java/69. Sqrt(x).java new file mode 100755 index 0000000..291a770 --- /dev/null +++ b/Java/69. Sqrt(x).java @@ -0,0 +1,117 @@ +E +tags: Math, Binary Search + +#### sqrt(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). + +Compute and return the square root of x, +where x is guaranteed to be a non-negative integer. + +Since the return type is an integer, +the decimal digits are truncated and only the integer part of the result is returned. + +Example 1: + +Input: 4 +Output: 2 +Example 2: + +Input: 8 +Output: 2 +Explanation: The square root of 8 is 2.82842..., and since + the decimal part is truncated, 2 is returned. +*/ + +/* +Thoughts: +Binary Search a candidate between [0~x] +*/ +class Solution { + 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/692. Top K Frequent Words.java b/Java/692. Top K Frequent Words.java new file mode 100755 index 0000000..20c6235 --- /dev/null +++ b/Java/692. Top K Frequent Words.java @@ -0,0 +1,206 @@ +M +tags: Hash Table, Heap, Trie, PriorityQueue, MaxHeap, MinHeap +time: O(n) +space: O(n) + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: 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 +*/ + +// Method1: Bucket Sort + +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 (words == null) return rst; + List[] bucket = buildBucket(words); + + for (int i = bucket.length -1; i > 0 && k > 0; i--) { + if (bucket[i] != null) { + Collections.sort(bucket[i]); + for (int j = 0; j < bucket[i].size() && k > 0; j++, k--) { + rst.add(bucket[i].get(j)); + } + } + } + return rst; + } + + private List[] buildBucket(String[] words) { + HashMap map = buildFreqMap(words); + List[] bucket = new List[map.size() + 1]; + for (Node node: map.values()) { + if(bucket[node.freq] == null) bucket[node.freq] = new ArrayList<>(); + bucket[node.freq].add(node.str); + } + return bucket; + } + + private HashMap buildFreqMap(String[] words) { + HashMap map = new HashMap<>(); + for (String word: words) { + map.putIfAbsent(word, new Node(word)); + map.get(word).freq += 1; + } + return map; + } +} + +// Method2: 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 (words == null) return rst; + PriorityQueue queue = configQueue(k); // minHeap + HashMap map = buildFreqMap(words); + + 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(); + } + + while (!queue.isEmpty()) rst.add(0, queue.poll().str);// output + return rst; + } + + private PriorityQueue configQueue(int k) { + return 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; + } + } + }); + } + + private HashMap buildFreqMap(String[] words) { + HashMap map = new HashMap<>(); + for (String word: words) { + map.putIfAbsent(word, new Node(word)); + map.get(word).freq += 1; + } + return map; + } +} + +// Method3: 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; + } +} +``` \ No newline at end of file diff --git a/Java/694. Number of Distinct Islands.java b/Java/694. Number of Distinct Islands.java new file mode 100755 index 0000000..7410216 --- /dev/null +++ b/Java/694. Number of Distinct Islands.java @@ -0,0 +1,85 @@ +M +tags: DFS, Hash Table +time: O(n) +space: O(n) + +#### DFS + HashSet +- DFS can find # of island, just like `200.Number of Islands`, aim to count total +- We need to map same-shap land + - One approach: print the **footprint** starting from coordinate (0, 0) + - Another approach: print the actual island in its boundary, like a QR code. (too hard to code, skip) +- Footprint approach: + - 1. always assume a newly found islands starts from (0, 0) + - 2. take 4 direction from init pos and keep printing the footprint + - 3. Since we always visit nodes from top->right->nextrow, we always visit top-left cornor of a new island, and the footprint will be identical + - Otherwises, if needed, we can sort the footprint and output the hash +- time: O(n), visit all +- space: O(n), store footprint, and dfs stacks worst case visit all nodes + +``` +/* +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. + +Count the number of distinct islands. An island is considered to be the same as another if and only if one island can be translated (and not rotated or reflected) to equal the other. + +Example 1: +11000 +11000 +00011 +00011 +Given the above grid map, return 1. +Example 2: +11011 +10000 +00001 +11011 +Given the above grid map, return 3. + +Notice that: +11 +1 +and + 1 +11 +are considered different island shapes, because we do not consider reflection / rotation. +Note: The length of each dimension in the given grid does not exceed 50. +*/ +/* +- DFS can find # of island. +- We need to map same-shap land + - One approach: print the footprint starting from coordinate (0, 0) +*/ +class Solution { + private int[] dx = {1, -1, 0, 0}; + private int[] dy = {0, 0, 1, -1}; + + public int numDistinctIslands(int[][] grid) { + if (grid == null) return 0; + int m = grid.length, n = grid[0].length; + Set count = new HashSet<>(); + for (int i = 0; i < m; i++) { + for (int j = 0; j < n; j++) { + if (grid[i][j] == 1) { + StringBuffer sb = new StringBuffer(); + dfs(grid, sb, i, j, 0, 0); + count.add(sb.toString()); + } + } + } + return count.size(); + } + + private void dfs(int[][] grid, StringBuffer sb, int x, int y, int xPos, int yPos) { + if (validateInput(grid, x, y)) return; + grid[x][y] = 0; + sb.append(xPos).append("-").append(yPos).append(","); + for (int i = 0; i < dx.length; i++) { + dfs(grid, sb, x + dx[i], y + dy[i], xPos+dx[i], yPos+dy[i]); + } + } + + private boolean validateInput(int[][] grid, int x, int y) { + return x < 0 || x >= grid.length || y < 0 || y >= grid[0].length || grid[x][y] == 0; + } +} +``` \ No newline at end of file diff --git a/Java/698. Partition to K Equal Sum Subsets.java b/Java/698. Partition to K Equal Sum Subsets.java new file mode 100755 index 0000000..9e07e36 --- /dev/null +++ b/Java/698. Partition to K Equal Sum Subsets.java @@ -0,0 +1,110 @@ +M +tags: DP, Recursion, DFS +time: O(k^(n-k) * k!) +space: O(n) + +#### Method1: Brutle DFS to find subsets and return results +- Target = total / k, fixed. +- DFS: Pick one num and dfs with the remaining nums for subproblem + - subproblem1: accumulate local sum to target + - EndState: accumulatedSum == target, continue with below + - subproblem2: after accumulatedSum == target, continue dfs with k-1 + - EndState: k == 0, return overall true +- Option1: DFS with nums, and boolean[] visited. Fast +- Option2: DFS with queue. + - Specially handling: dfs(size+1) to prevent `while(size-- >0)` from skipping last item at index 0 + + +#### Method2: DP +- the problem aims to find true/false capability +- bit-masking. TODO. The DP approach is kinda hard-level +- https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/335668/DP-with-Bit-Masking-Solution-%3A-Best-for-Interviews + +``` +/* +Given an array of integers nums and a positive integer k, +find whether it's possible to divide this array into k non-empty subsets whose sums are all equal. + + + +Example 1: + +Input: nums = [4, 3, 2, 3, 5, 2, 1], k = 4 +Output: True +Explanation: It's possible to divide it into 4 subsets (5), (1, 4), (2,3), (2,3) with equal sums. + + +Note: + +1 <= k <= len(nums) <= 16. +0 < nums[i] < 10000. +*/ + + +/* +Brutle DFS to find subsets and return results +- Target = sum / k, fixed. +- DFS: Pick one num and dfs with the remaining nums for subproblem + - subproblem1: accumulate sum to target + - EndState: accumulatedSum == target, continue below + - subproblem2: once accumulatedSum == target, continue with k-1 + - EndState: k == 0, return overall true +*/ +// DFS Option1: use array, boolean[] visited +class Solution { + int[] nums; + boolean[] visited; + public boolean canPartitionKSubsets(int[] nums, int k) { + int total = 0, n = nums.length; + for (int num : nums) total += num; + if (k <= 0 || total % k != 0) return false; + + this.nums = nums; + this.visited = new boolean[n]; + return dfs(0, k, 0, total / k); + } + + private boolean dfs(int start, int k, int sum, int target) { + if (k == 0) return true; // exhaust all numbers + if (sum == target) return dfs(0, k - 1, 0, target); // move on to next subset; start=0 to reprocess all candidates + + for (int i = start; i < nums.length; i++) { // i = start, skip examined candidates in next dfs level + if (visited[i]) continue; + visited[i] = true; + if (sum + nums[i] <= target && dfs(i + 1, k, sum + nums[i], target)) return true; + visited[i] = false; + } + + return false; + } +} +// DFS Option2: Use Queue +// dfs(size+1): prevent `while(size-- >0)` from skipping last item at index 0 +class Solution { + public boolean canPartitionKSubsets(int[] nums, int k) { + + int sum = 0; + Queue queue = new LinkedList<>(); + for (int num : nums) { + sum += num; + queue.offer(num); + } + if (k <= 0 || sum % k != 0) return false; + + return dfs(queue, queue.size(), k, 0, sum / k); + } + + private boolean dfs(Queue queue, int size, int k, int sum, int target) { + if (k == 0) return true; // exhaust all numbers + if (sum == target) return dfs(queue, queue.size(), k - 1, 0, target); + + while (size-- > 0) { + int num = queue.poll(); + if (sum + num <= target && dfs(queue, size+1, k, sum + num, target)) return true; + queue.offer(num); + } + return false; + } +} + +``` \ No newline at end of file diff --git a/Java/7. Reverse Integer.java b/Java/7. Reverse Integer.java new file mode 100755 index 0000000..5c1ec68 --- /dev/null +++ b/Java/7. Reverse Integer.java @@ -0,0 +1,71 @@ +E +tags: Math +time: O(n) +space: O(1) + +#### 方法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; + } +} + +/* +use long to catch rst + x%10 to get last digit + x/10 to iterate + rst *= 10 +while (x > 0) + +*/ +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; + } +} +``` diff --git a/Java/70. Climbing Stairs.java b/Java/70. Climbing Stairs.java new file mode 100755 index 0000000..94c0e34 --- /dev/null +++ b/Java/70. Climbing Stairs.java @@ -0,0 +1,111 @@ +E +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]往往是有特殊状态的. 这里, dp[0]: 1种方式可以原地不动 + - dp[1]=1, 1种方式到达index=1, + - 之后的`dp[2] = dp[0]+dp[1]`: 就是dp[0]的走法 or dp[1]的走法 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + +``` +/* + +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? + +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 climb(n - 1) + climb(n - 2); + } + + public int climb(int n) { + if (n <= 1) return 1; + if (memo[n] > 0) return memo[n]; + memo[n - 1] = climb(n - 1); + memo[n - 2] = climb(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]; + } +} + +``` \ No newline at end of file diff --git a/Java/71. Simplify Path.java b/Java/71. Simplify Path.java new file mode 100755 index 0000000..0c80fb7 --- /dev/null +++ b/Java/71. Simplify Path.java @@ -0,0 +1,88 @@ +M +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 `/` +- parse by '/', and go over using stack + - put [folder] in stack + - ".." pop() 1 element of the stack, if anything + - "." stays the same +- output stack reversely: connect with '/', skip tail + +``` +/* +Given an absolute path for a file (Unix-style), simplify it. Or in other words, convert it to the canonical path. + +In a UNIX-style file system, a period . refers to the current directory. Furthermore, a double period .. moves the directory up a level. For more information, see: Absolute path vs relative path in Linux/Unix + +Note that the returned canonical path must always begin with a slash /, and there must be only a single slash / between two directory names. The last directory name (if it exists) must not end with a trailing /. Also, the canonical path must be the shortest string representing the absolute path. + + + +Example 1: + +Input: "/home/" +Output: "/home" +Explanation: Note that there is no trailing slash after the last directory name. +Example 2: + +Input: "/../" +Output: "/" +Explanation: Going one level up from the root directory is a no-op, as the root level is the highest level you can go. +Example 3: + +Input: "/home//foo/" +Output: "/home/foo" +Explanation: In the canonical path, multiple consecutive slashes are replaced by a single one. +Example 4: + +Input: "/a/./b/../../c/" +Output: "/c" +Example 5: + +Input: "/a/../../b/../c//.//" +Output: "/c" +Example 6: + +Input: "/a//b////c/d//././/.." +Output: "/a/b/c" +*/ +/* +- parse by '/', and go over using stack + - put [folder] in stack + - ".." pop() 1 element of the stack, if anything + - "." stays the samee +- output stack reversely: connect with '/', skip tail / +*/ + +class Solution { + public String simplifyPath(String path) { + 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(); + } +} + +``` \ No newline at end of file diff --git a/Java/715. Range Module.java b/Java/715. Range Module.java new file mode 100755 index 0000000..0319ebf --- /dev/null +++ b/Java/715. Range Module.java @@ -0,0 +1,97 @@ +H +tags: TreeSet, Segment Tree +time: query O(logn), update O(n) +space: O(n) + +#### TreeSet +- start with considering array structure but operation are all O(n) + - what if we can easily find range, and update +- TreeSet: + - build a class `Interval {int start, end;}` + - build a customized `compareTo` that sorts the interval by start at default, but sort by end if a.start==b.start + - Query: TreeSet allow us to find element in O(logn) + - Add Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + - Remove Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + +``` +/* +A Range Module is a module that tracks ranges of numbers. Your task is to design and implement the following interfaces in an efficient manner. + +addRange(int left, int right) Adds the half-open interval [left, right), tracking every real number in that interval. Adding an interval that partially overlaps with currently tracked numbers should add any numbers in the interval [left, right) that are not already tracked. +queryRange(int left, int right) Returns true if and only if every real number in the interval [left, right) is currently being tracked. +removeRange(int left, int right) Stops tracking every real number currently being tracked in the interval [left, right). +Example 1: +addRange(10, 20): null +removeRange(14, 16): null +queryRange(10, 14): true (Every number in [10, 14) is being tracked) +queryRange(13, 15): false (Numbers like 14, 14.03, 14.17 in [13, 15) are not being tracked) +queryRange(16, 17): true (The number 16 in [16, 17) is still being tracked, despite the remove operation) +Note: + +A half open interval [left, right) denotes all real numbers left <= x < right. +0 < left < right < 10^9 in all calls to addRange, queryRange, removeRange. +The total number of calls to addRange in a single test case is at most 1000. +The total number of calls to queryRange in a single test case is at most 5000. +The total number of calls to removeRange in a single test case is at most 1000. +*/ +class RangeModule { + + class Interval implements Comparable { + int start, end; + public Interval(int start, int end) { + this.start = start; + this.end = end; + } + + public int compareTo(Interval target) { + if (this.end == target.end) return this.start - target.start; // if same end, sort by start + return this.end - target.end; // otherwise, sort by start + } + } + + TreeSet ranges; + public RangeModule() { + ranges = new TreeSet<>(); + } + + public void addRange(int start, int end) { + Iterator iterator = ranges.tailSet(new Interval(0, start - 1)).iterator(); // make sure it overlaps on `start-1`, since [start-1, start] can merge into 1 interval + while (iterator.hasNext()) { + Interval interval = iterator.next(); + if (end < interval.start) break; // out of range + // remove the curr element & mark new range + iterator.remove(); + start = Math.min(start, interval.start); + end = Math.max(end, interval.end); + } + ranges.add(new Interval(start, end)); + } + + public boolean queryRange(int start, int end) { + Interval interval = ranges.higher(new Interval(0, start)); // find range larger or equal to [0, start) + return interval != null && interval.start <= start && end <= interval.end; + } + + public void removeRange(int start, int end) { + Iterator iterator = ranges.tailSet(new Interval(0, start)).iterator(); // find [start, ...] elements + List newRanges = new ArrayList<>(); + while (iterator.hasNext()) { + Interval interval = iterator.next(); + if (end < interval.start) break; // out of range + // remove the curr element & break down into new range + iterator.remove(); + if (interval.start < start) newRanges.add(new Interval(interval.start, start)); + if (end < interval.end) newRanges.add(new Interval(end, interval.end)); + } + for (Interval interval : newRanges) ranges.add(interval); + } +} + +/** + * Your RangeModule object will be instantiated and called as such: + * RangeModule obj = new RangeModule(); + * obj.addRange(left,right); + * boolean param_2 = obj.queryRange(left,right); + * obj.removeRange(left,right); + */ +``` \ No newline at end of file diff --git a/Java/716. Max Stack.java b/Java/716. Max Stack.java new file mode 100755 index 0000000..e9db4d8 --- /dev/null +++ b/Java/716. Max Stack.java @@ -0,0 +1,190 @@ +M +tags: Design, Stack, Doubly Linked List, TreeMap +time: avg O(1), [O(logN) peekMax(), TreeMap]; [O(n) popMax(), TwoStack] +space: O(n) + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + +``` +/* +Design a max stack that supports push, pop, top, peekMax and popMax. + +push(x) -- Push element x onto stack. +pop() -- Remove the element on top of the stack and return it. +top() -- Get the element on the top. +peekMax() -- Retrieve the maximum element in the stack. +popMax() -- Retrieve the maximum element in the stack, and remove it. If you find more than one maximum elements, only remove the top-most one. +Example 1: +MaxStack stack = new MaxStack(); +stack.push(5); +stack.push(1); +stack.push(5); +stack.top(); -> 5 +stack.popMax(); -> 5 +stack.top(); -> 1 +stack.peekMax(); -> 5 +stack.pop(); -> 1 +stack.top(); -> 5 +Note: +-1e7 <= x <= 1e7 +Number of operations won't exceed 10000. +The last four operations won't be called when stack is empty. +*/ + +/* +Use two stacks: + - one to keep regular elements + - one to repat the max at current stack level +*/ +class MaxStack { + Stack stack; + Stack max; + + /** initialize your data structure here. */ + public MaxStack() { + stack = new Stack<>(); + max = new Stack<>(); + } + + public void push(int x) { + stack.push(x); + int maxVal = max.isEmpty() ? x : Math.max(max.peek(), x); + max.push(maxVal); + } + + public int pop() { + max.pop(); + return stack.pop(); + } + + public int top() { + return stack.peek(); + } + + public int peekMax() { + return max.peek(); + } + + public int popMax() { + int maxVal = peekMax(); + Stack temp = new Stack<>(); + while(top() != maxVal) temp.push(pop()); + pop(); + + while(!temp.isEmpty()) push(temp.pop()); + + return maxVal; + } +} + + +/* +Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives O(logN) insert, delete and find MAX +- Build DoubleLinkedList class to perform O(1) removal +- The problem becomes finding the target value & remove from DoubleLinkedList +*/ +class MaxStack { + TreeMap> map; + DoubleLinkedList dll; + + public MaxStack() { + map = new TreeMap(); + dll = new DoubleLinkedList(); + } + + // O(1) + public void push(int x) { + Node node = dll.add(x); + map.putIfAbsent(x, new ArrayList()); + map.get(x).add(node); + } + + // O(1) + public int pop() { + int val = dll.pop(); + removeFromMap(val); + return val; + } + + // O(1) + public int top() { + return dll.peek(); + } + + // O(logN) + public int peekMax() { + return map.lastKey(); + } + + // O(1) + public int popMax() { + int max = peekMax(); + Node node = removeFromMap(max); + dll.unlink(node); + return max; + } + + // Find val from map, remove it from list, & remove list if empty + // O(1) + private Node removeFromMap(int val) { + List list = map.get(val); + Node node = list.remove(list.size() - 1); + if (list.isEmpty()) map.remove(val); + return node; + } + + // Define DoubleLinkedList class + class DoubleLinkedList { + Node head, tail; + + public DoubleLinkedList() { + head = new Node(0); + tail = new Node(0); + head.next = tail; + tail.prev = head; + } + + public Node add(int val) { + Node x = new Node(val); + x.next = tail; + x.prev = tail.prev; + tail.prev = tail.prev.next = x; // append to tail + return x; + } + + public int pop() { + return unlink(tail.prev).val; + } + + public int peek() { + return tail.prev.val; + } + + public Node unlink(Node node) { + node.prev.next = node.next; + node.next.prev = node.prev; + return node; + } + } + + class Node { + int val; + Node prev, next; + public Node(int v) {val = v;} + } +} + +``` \ No newline at end of file diff --git a/Java/717. 1-bit and 2-bit Characters.java b/Java/717. 1-bit and 2-bit Characters.java new file mode 100755 index 0000000..37ec052 --- /dev/null +++ b/Java/717. 1-bit and 2-bit Characters.java @@ -0,0 +1,99 @@ +E +1517474043 +tags: Array + +理解题目: +1. single-bit always starts with '0', two-bits always start with '1'. +1. Therefore there is ONLY 1 way to reach end. + +#### 方法1 +Greedy. +从第一个bit开始: 如果 % 2 == 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/72. Edit Distance.java b/Java/72. Edit Distance.java new file mode 100755 index 0000000..bccceba --- /dev/null +++ b/Java/72. Edit Distance.java @@ -0,0 +1,277 @@ +H +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.) + +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: +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(), 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: +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 { + 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()]; + } +} + +``` \ No newline at end of file diff --git a/Java/720. Longest Word in Dictionary.java b/Java/720. Longest Word in Dictionary.java new file mode 100755 index 0000000..9d72a16 --- /dev/null +++ b/Java/720. Longest Word in Dictionary.java @@ -0,0 +1,248 @@ +E +tags: Hash Table, Trie +time: O(nlogn) +space: O(n) + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序(lexicographically), 排序以后才能逐个看是否partial string已经存在 + - 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 + - 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: + - 长 string 排在前; + - 相等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) + +#### 从最长的word开始做 +- 按大小排序 -> 从最大的开始做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); + + 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; + } + ArrayList wordList = new ArrayList<>(Arrays.asList(words)); + Collections.sort(wordList, new Comparator(){ + public int compare(String a, String b) { + return a.length() - b.length(); + } + }); + + ArrayList result = matchWords(wordList); + Collections.sort(result); + + if (result.size() != 0) { + return result.get(0); + } + return null; + } + + private ArrayList matchWords(ArrayList wordList) { + int maxWordLength = Integer.MIN_VALUE; + 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/721. Accounts Merge.java b/Java/721. Accounts Merge.java new file mode 100755 index 0000000..2257f00 --- /dev/null +++ b/Java/721. Accounts Merge.java @@ -0,0 +1,185 @@ +M +tags: DFS, Union Find, Hash Table +- space O(mn) +- time O(mn) + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Method1: 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, 最终用来输出. + +#### Method2: 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) + +### DFS +- TODO + +``` +/* +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]. + +*/ + + + +/* +Method1: 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<>(); + + unionFind(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> map = new HashMap<>(); + for (String email : parentMap.keySet()) { + String parent = find(email); + map.putIfAbsent(parent, new ArrayList<>()); + map.get(parent).add(email); + } + + for (List list: map.values()) { + Collections.sort(list); + list.add(0, accountMap.get(list.get(0))); + rst.add(list); + } + + return rst; + } + + private void unionFind(List> accounts) { + for (List account : accounts) { + String name = account.get(0); + for (int i = 1; i < account.size(); i++) { + String email = account.get(i); + accountMap.put(email, name); // email -> name mapping + parentMap.put(email, email); // 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), parentB = find(b); + if (!parentA.equals(parentB)) parentMap.put(parentA, parentB); + } +} + +// Method2: Hash Table +/* +- use Account {name, emailSet}, and map all emails to the same node +- check each account: if any email is found, pick up that node and add all emails to it +- at the end, sort emails and return +*/ +class Solution { + class Account { + String name; + Set emails; + public Account(String name, Set emails) { + this.name = name; + this.emails = emails; + } + } + + public List> accountsMerge(List> accounts) { + Map map = new HashMap<>(); + Set mergedAccounts = new HashSet<>(); + + for (List row : accounts) { + Account account = new Account(row.get(0), new HashSet<>()); + row.remove(0); + for (String email : row) { + account.emails.add(email); + if (map.containsKey(email)) { + Account existingAccount = map.get(email); + mergedAccounts.remove(existingAccount); + account.emails.addAll(existingAccount.emails); + } + } + mergedAccounts.add(account); + for (String email: account.emails) map.put(email, account); + } + + // output as needed + return convert(mergedAccounts); + } + + private List> convert(Set accounts) { + List> rst = new ArrayList<>(); + for (Account account : accounts) { + 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; + } + +} + + +``` \ No newline at end of file diff --git a/Java/724. Find Pivot Index.java b/Java/724. Find Pivot Index.java new file mode 100755 index 0000000..b51c630 --- /dev/null +++ b/Java/724. Find Pivot Index.java @@ -0,0 +1,62 @@ +E +tags: Array, PreSum +time: O(n) +space: O(1) + +#### PreSum +- want to find `nums[i - 1] == nums[n - 1] - nums[i]`, given: + - preSum[i], sum from [0, i] inclusive + - preSum[j] - preSum[i] = [i+1, j] inclusive +- O(n) to build preSum +- O(n) to find pivot + +``` +/* +Given an array of integers nums, write a method that returns the "pivot" index of this array. + +We define the pivot index as the index where the sum of the numbers to the left of the index is equal to the sum of the numbers to the right of the index. + +If no such index exists, we should return -1. If there are multiple pivot indexes, you should return the left-most pivot index. + +Example 1: + +Input: +nums = [1, 7, 3, 6, 5, 6] +Output: 3 +Explanation: +The sum of the numbers to the left of index 3 (nums[3] = 6) is equal to the sum of numbers to the right of index 3. +Also, 3 is the first index where this occurs. + + +Example 2: + +Input: +nums = [1, 2, 3] +Output: -1 +Explanation: +There is no index that satisfies the conditions in the problem statement. +*/ + +/* +preSum[i], sum from [0, i] inclusive +preSum[j] - preSum[i] = [i+1, j] inclusive +- O(n) to build preSum +- O(n) to find pivot +*/ +class Solution { + public int pivotIndex(int[] nums) { + if (nums == null || nums.length == 0) return -1; + int n = nums.length; + // build pre sum + for (int i = 1; i < n; i++) nums[i] += nums[i - 1]; + + if (nums[n-1] - nums[0] == 0) return 0;// edge case + + for (int i = 1; i < n; i++) { + if (nums[i - 1] == nums[n - 1] - nums[i]) return i; + } + + return -1; + } +} +``` \ No newline at end of file diff --git a/Java/727. Minimum Window Subsequence.java b/Java/727. Minimum Window Subsequence.java new file mode 100755 index 0000000..a378b6b --- /dev/null +++ b/Java/727. Minimum Window Subsequence.java @@ -0,0 +1,208 @@ +H +tags: Sliding Window, DP, Two Pointers, String, Hash Table +time: O(n^2) +space: O(1) + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + +``` +/* +Given strings S and T, find the minimum (contiguous) substring W of S, so that T is a subsequence of W. + +If there is no such window in S that covers all characters in T, return the empty string "". +If there are multiple such minimum-length windows, return the one with the left-most starting index. + +Example 1: + +Input: +S = "abcdebdde", T = "bde" +Output: "bcde" +Explanation: +"bcde" is the answer because it occurs before "bdde" which has the same length. +"deb" is not a smaller window because the elements of T in the window must occur in order. + + +Note: + +All the strings in the input will only contain lowercase letters. +The length of S will be in the range [1, 20000]. +The length of T will be in the range [1, 100]. +*/ + + +/* +Method1: final rewrite version; removed some fancy ++/-- and make it easy to understand +- Since it requires sequence in substring, freqMap+counter+twoPointers approach is NOT applicable +- Simple two pointers: + - 1) move sIndex and tIndex: find all T occurances in order in S + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- +*/ +class Solution { + public String minWindow(String s, String t) { + + int right = 0, tIndex = 0, start = -1; + int m = s.length(), n = t.length(), minLength = m; + char[] ss = s.toCharArray(), tt = t.toCharArray(); + + while (right < m) { + if(ss[right] == tt[tIndex]) { // char match + if(tIndex++ == n - 1) { // tIndex exhausted, start backtrack and find shortest start index + // backtrack last matching index `right` to 1st match position; which is just left + int left = backtrack(ss, tt, right); + + // calculate the start point and record + int len = right - left + 1; + if (len < minLength) { + minLength = len; + start = left; + } + // reset tIndex to 0 and right to left to restart; the curr candidate will expire once we drop the 1 valid character from left + tIndex = 0; + right = left; + } + } + right++; + } + return start == -1 ? "" : s.substring(start, start + minLength); + } + + /* + Backtracking to revisit all T characters, from t.end -> 0 index + Regardless of ss match, always reduce index + In the end, ss index will be at a desiredIndex - 1 position + So + 1 to set actual `left` starting point + */ + private int backtrack(char[] ss, char[] tt, int index) { + for (int i = tt.length - 1; i >= 0; i--) { + while(ss[index--] != tt[i]); + } + return index + 1; // index = 1st char match index - 1; ++ to reset + } +} + + +/* +- Since it requires sequence in substring, freqMap+counter+twoPointers approach is NOT applicable +- Simple two pointers: + - 1) move sIndex and tIndex: find all T occurances in order in S + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- +*/ +class Solution { + public String minWindow(String s, String t) { + + int sIndex = 0, tIndex = 0, start = -1; + int m = s.length(), n = t.length(), minLength = m; + char[] ss = s.toCharArray(), tt = t.toCharArray(); + + while (sIndex < m) { + if(ss[sIndex] == tt[tIndex]) { // char match + if(tIndex++ == n - 1) { // tIndex exhausted, process: + int end = sIndex + 1; // mark end of candidate + + // backtrack tIndex to 0, and backtrack sIndex to 1st matching position + while(tIndex-- > 0) { // final state: tIndex = -1 + while(ss[sIndex--] != tt[tIndex]); + } + sIndex++; // sIndex = 1st char match index - 1; ++ to reset + tIndex++; // reset to 0 + + // record the candidate + if (end - sIndex < minLength) { + minLength = end - sIndex; + start = sIndex; + } + } + } + sIndex++; + } + return start == -1 ? "" : s.substring(start, start + minLength); + } +} + +// Option2: Simpler way to write it +class Solution { + public String minWindow(String s, String t) { + + int sIndex = 0, tIndex = 0, start = -1; + int m = s.length(), n = t.length(), minLength = m; + char[] ss = s.toCharArray(), tt = t.toCharArray(); + + while (sIndex < m) { + if(ss[sIndex] == tt[tIndex]) { // char match + if(++tIndex == n) { // end + int end = sIndex + 1; + + while(--tIndex >= 0) { + while(ss[sIndex--] != tt[tIndex]); + } + sIndex++; + tIndex++; + + // record + if (end - sIndex < minLength) { + minLength = end - sIndex; + start = sIndex; + } + } + } + ++sIndex; + } + return start == -1 ? "" : s.substring(start, start + minLength); + } +} + +// Option3: another way to write: use for loop to backtrack and direct reset tIndex = 0; +class Solution { + public String minWindow(String s, String t) { + + int sIndex = 0, tIndex = 0, start = -1; + int m = s.length(), n = t.length(), minLength = m; + char[] ss = s.toCharArray(), tt = t.toCharArray(); + + while (sIndex < m) { + if(ss[sIndex] == tt[tIndex]) { // char match + if(tIndex++ == n - 1) { // tIndex exhausted, process it + int end = sIndex + 1; // mark end of candidate + // reset tIndex to 0 and backtrack sIndex to 1st match position + tIndex = 0; + sIndex = backtrack(ss, tt, sIndex); + + // record the candidate + if (end - sIndex < minLength) { + minLength = end - sIndex; + start = sIndex; + } + } + } + sIndex++; + } + return start == -1 ? "" : s.substring(start, start + minLength); + } + + private int backtrack(char[] ss, char[] tt, int sIndex) { + for (int i = tt.length - 1; i >= 0; i--) { + while(ss[sIndex--] != tt[i]); + } + return ++sIndex; // sIndex = 1st char match index - 1; ++ to reset + } +} + + +``` \ No newline at end of file diff --git a/Java/739. Daily Temperatures.java b/Java/739. Daily Temperatures.java new file mode 100755 index 0000000..ecd4b3b --- /dev/null +++ b/Java/739. Daily Temperatures.java @@ -0,0 +1,84 @@ +M +tags: Stack, Hash Table, Monotonous Stack +time: O(n) +space: O(n) + +#### Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) + +#### Method2: `Map `, kinda of like bucket sort +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: + - 1) record tempIndex[currTemp] = i; + - 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result +``` +/** +Given a list of daily temperatures T, return a list such that, for each day in the input, +tells you how many days you would have to wait until a warmer temperature. +If there is no future day for which this is possible, put 0 instead. + +For example, given the list of temperatures T = [73, 74, 75, 71, 69, 72, 76, 73], your output should be [1, 1, 4, 2, 1, 1, 0, 0]. + +Note: The length of temperatures will be in the range [1, 30000]. Each temperature will be an integer in the range [30, 100]. +*/ + +/* +Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) +*/ +class Solution { + public int[] dailyTemperatures(int[] T) { + int n = T.length; + int[] rst = new int[n]; + Stack stack = new Stack<>(); // note: store indexes + + for (int i = n - 1; i >= 0; i--) { + int num = T[i]; + // mono stack + while (!stack.isEmpty() && num >= T[stack.peek()]) stack.pop(); + // process right-side closest & higer number + if (!stack.isEmpty() && num < T[stack.peek()]) rst[i] = stack.peek() - i; + else rst[i] = 0; + + // add curr + stack.push(i); + } + + return rst; + } +} + +/* +#### Method2: Map +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: 1) record tempIndex[currTemp] = i; 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result +*/ +class Solution { + public int[] dailyTemperatures(int[] T) { + int n = T.length; + int[] rst = new int[n]; + Integer[] tempIndex = new Integer[101]; // assume top temp is 101 + + for (int i = n - 1; i >= 0; i--) { + int num = T[i], warmerIndex = Integer.MAX_VALUE;; + + for (int t = num + 1; t <= 100; t++) { // find the smallest index that has temp greater than num + if (tempIndex[t] != null && tempIndex[t] < warmerIndex) warmerIndex = tempIndex[t]; + } + + if (warmerIndex < Integer.MAX_VALUE) rst[i] = warmerIndex - i; // default rst[i] = 0 + tempIndex[num] = i; + } + + return rst; + } +} +``` \ No newline at end of file diff --git a/Java/741. Cherry Pickup.java b/Java/741. Cherry Pickup.java new file mode 100755 index 0000000..36950c5 --- /dev/null +++ b/Java/741. Cherry Pickup.java @@ -0,0 +1,154 @@ +H +tags: DP, DFS +time: O(n^3) +space: O(n^3), memo size + +special hint: `r1 + c1 = constant t = r2 + c2`, if the two points are moving at same time. + +#### DFS + Memo: TOP-DOWN +- Similar concept to Minimum Path Sum +- https://leetcode.com/problems/cherry-pickup/solution/ +- realize r1 + c1 = r2 + c2. Knowing 3 parameters can uniquely identify the 4th. +- assume there are 2 people starting from origin, and the 2 people can go total 4 directions + - perform DFS based on the 4 directions + - concern: do they visit the same spot? possible. when that happens, make sure we do not double count the grid[i][j] +- when is the end state? + - then anyone, for example, (r1,c1) reaches end (n-1, n-1). + - it means the other person also reaches end +- use memo: memo[r1][c1][r2], it records any given (r1, c1, r2, c2) state + + +``` + +/* +DFS + Memo: TOP-DOWN +- https://leetcode.com/problems/cherry-pickup/solution/ +- realize r1 + c1 = r2 + c2. Knowing 3 parameters can uniquely identify the 4th. +- assume there are 2 people starting from origin, and the 2 people can go total 4 directions + - perform DFS based on the 4 directions + - concern: do they visit the same spot? possible. when that happens, make sure we do not double count the grid[i][j] +- when is the end state? + - then anyone, for example, (r1,c1) reaches end (n-1, n-1). + - it means the other person also reaches end +- use memo: memo[r1][c1][r2], it records any given (r1, c1, r2, c2) state +*/ +class Solution { + Integer[][][] memo; + int n; + public int cherryPickup(int[][] grid) { + + n = grid.length; + memo = new Integer[n][n][n]; + return Math.max(0, dfs(grid, 0, 0, 0)); + } + + private int dfs(int[][] grid, int r1, int c1, int r2) { + int c2 = r1 + c1 - r2; + if (r1 == n || c1 == n || r2 == n || c2 == n || grid[r1][c1] == -1 || grid[r2][c2] == -1) return Integer.MIN_VALUE; + if (r1 == n -1 && c1 == n - 1) return grid[r1][c1]; + if (memo[r1][c1][r2] != null) return memo[r1][c1][r2]; + + int sum = grid[r1][c1]; + if (c1 != c2) sum += grid[r2][c2]; + + sum += Math.max(Math.max(dfs(grid, r1 + 1, c1, r2 + 1), dfs(grid, r1, c1 + 1, r2 + 1)), + Math.max(dfs(grid, r1 + 1, c1, r2), dfs(grid, r1, c1 + 1, r2))); + memo[r1][c1][r2] = sum; + return sum; + } + + +} + + +/* +WRONG SOLUTION: Greedy DFS, 2 Pass +- function to cal max with DFS +- rotate the grid +- perform DFS again +- problem with DP: dp does not trace the path; but dfs can mark down values after choosen + +[[1,1,1,1,0,0,0], +[0,0,0,1,0,0,0], +[0,0,0,1,0,0,1], +[1,0,0,1,0,0,0], +[0,0,0,1,0,0,0], +[0,0,0,1,0,0,0], +[0,0,0,1,1,1,1]] +*/ +class Solution { + Integer[][] memo; + Map> pathMemo; + public int cherryPickup(int[][] grid) { + + int n = grid.length, sum = 0; + memo = new Integer[n][n]; + pathMemo = new HashMap<>(); + List path = new ArrayList<>(); + sum += dfs(grid, path, 0, 0); + if (memo[n - 1][n - 1] == null) return 0; + updateGrid(grid, path); + + rotate(grid); + memo = new Integer[n][n]; + pathMemo = new HashMap<>(); + sum += dfs(grid, new ArrayList<>(), 0, 0); + + return sum; + } + + private int dfs(int[][] grid, List path, int i, int j) { + if (i < 0 || j < 0 || i >= grid.length || j >= grid[0].length || grid[i][j] == -1) return 0; + if (memo[i][j] != null) { + path.addAll(pathMemo.get(getKey(i, j))); + return memo[i][j]; + } + + int sum = grid[i][j]; // 0 or 1 + List rightPath = new ArrayList<>(), downPath = new ArrayList<>(); + + int right = dfs(grid, rightPath, i, j + 1); + int down = dfs(grid, downPath, i + 1, j); + + sum += Math.max(right, down); + path.add(new int[]{i, j}); + if (right >= down) path.addAll(rightPath); + else path.addAll(downPath); + + memo[i][j] = sum; + pathMemo.put(getKey(i,j), new ArrayList<>(path)); + return sum; + } + + private String getKey(int i, int j) { + return i + "@" + j; + } + + private void updateGrid(int[][] grid, List path) { + for (int[] pos : path) { + grid[pos[0]][pos[1]] = 0; + } + } + + private void rotate(int[][] grid) { + for (int i = 0; i < grid.length; i++) flipRow(grid[i]); + for (int j = 0; j < grid[0].length; j++) { + int start = 0, end = grid.length - 1; + while (start != end) { + int temp = grid[start][j]; + grid[start++][j] = grid[end][j]; + grid[end--][j] = temp; + } + } + } + + private void flipRow(int[] nums) { + int i = 0, j = nums.length - 1; + while (i != j) { + int temp = nums[j]; + nums[j--] = nums[i]; + nums[i++] = temp; + } + } +} +``` \ No newline at end of file diff --git a/Java/743. Network Delay Time.java b/Java/743. Network Delay Time.java new file mode 100755 index 0000000..01df4fe --- /dev/null +++ b/Java/743. Network Delay Time.java @@ -0,0 +1,124 @@ +M +tags: Heap, DFS, BFS, Graph, PQ +time: O(nlogn) +space: O(n) + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + +``` +/* +There are N network nodes, labelled 1 to N. + +Given times, a list of travel times as directed edges times[i] = (u, v, w), where u is the source node, v is the target node, and w is the time it takes for a signal to travel from source to target. + +Now, we send a signal from a certain node K. How long will it take for all nodes to receive the signal? If it is impossible, return -1. + + + +Example 1: + + + +Input: times = [[2,1,1],[2,3,1],[3,4,1]], N = 4, K = 2 +Output: 2 + + +Note: + +N will be in the range [1, 100]. +K will be in the range [1, N]. +The length of times will be in the range [1, 6000]. +All edges times[i] = (u, v, w) will have 1 <= u, v <= N and 0 <= w <= 100. +*/ + +/* +#### Method1: BFS with PQ on graph +- pick close node to vist, and add siblings back to PQ +- avoid visited +*/ +class Solution { + + public int networkDelayTime(int[][] times, int N, int K) { + + Map> graph = buildGraph(times); + PriorityQueue pq = new PriorityQueue<>(Comparator.comparing(record -> record[1])); // sort by travel time, + Set visited = new HashSet<>(); + pq.offer(new int[]{K, 0}); + int travelTime = 0; + + while (!pq.isEmpty()) { + int size = pq.size(); + while (size-- > 0) { + int[] record = pq.poll(); + int node = record[0]; + if (visited.contains(node)) continue; + visited.add(node); + travelTime = record[1]; // travel time to curr node + if (!graph.containsKey(node)) continue; + for (int[] next : graph.get(node)) { + pq.offer(new int[]{next[0], next[1] + travelTime}); + } + } + } + + return visited.size() != N ? -1 : travelTime; + } + + private Map> buildGraph(int[][] times) { + Map> graph = new HashMap<>(); + for (int[] record : times) { + graph.putIfAbsent(record[0], new ArrayList<>()); + graph.get(record[0]).add(new int[]{record[1], record[2]}); // map value: int[]{nextNode, travelTime to nextNode} + } + return graph; + } +} + +// #### Method2: DFS with Sort +class Solution { + Map timeMap = new HashMap<>(); // arrival time map + public int networkDelayTime(int[][] times, int N, int K) { + Map> graph = buildGraph(times); + for (int i = 1; i <= N; i++) timeMap.put(i, Integer.MAX_VALUE); + dfs(graph, K, 0); + int time = 0; + for (int timeElapsed : timeMap.values()) { + if (timeElapsed == Integer.MAX_VALUE) return -1; // not visited + time = Math.max(time, timeElapsed); + } + return time; + } + + private void dfs(Map> graph, int node, int timeElapsed) { + if (timeElapsed >= timeMap.get(node)) return; + timeMap.put(node, timeElapsed); + if (!graph.containsKey(node)) return; + List targets = graph.get(node); + Collections.sort(targets, Comparator.comparing(record -> record[2])); // sort by travel time + for (int[] record : graph.get(node)) { + int target = record[1], travelTime = record[2]; + dfs(graph, target, timeElapsed + travelTime); + } + } + + private Map> buildGraph(int[][] times) { + Map> graph = new HashMap<>(); + for (int[] record : times) { + graph.putIfAbsent(record[0], new ArrayList<>()); + graph.get(record[0]).add(record); + } + return graph; + } +} +``` \ No newline at end of file diff --git a/Java/745. Prefix and Suffix Search.java b/Java/745. Prefix and Suffix Search.java new file mode 100755 index 0000000..c4d5842 --- /dev/null +++ b/Java/745. Prefix and Suffix Search.java @@ -0,0 +1,101 @@ +H +tags: Trie +time: O(N + Q) +space: O(N) + +#### Chain `suffix # prefix` +- Build Trie for all combinations of `suffix#prefix`; all assigned with weight +- how does it make sure to return largest weight/index? + - when we build trie, always update weight for the path nodes it goes through + - yes, it overrides, but this problem does not care if some words are not found +- Time: + - build: go through all words O(n) * word length * 2 => O(n) + - query: O(1) tree height is just at most 20. +- Space: O(N) store all words + +``` +/* +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. + + +*/ + + +/* +- Build Trie for all combinations of `suffix#prefix`; all assigned with weight +- how does it make sure to return largest weight/index? + - when we build trie, always update weight for the path nodes it goes through + - yes, it overrides, but this problem does not care if some words are not found +*/ +class WordFilter { + public class TrieNode { + int weight; + Map children = new HashMap<>(); + public TrieNode (int weight) { + this.weight = weight; + } + } + + TrieNode root = new TrieNode(0); + public WordFilter(String[] words) { + for (int weight = 0; weight < words.length; weight++) { + String word = words[weight]; + StringBuffer prefix = new StringBuffer("#"); + + for (int i = 0; i < word.length(); i++) { + // Insert prefix only + prefix.append(word.charAt(i)); + insert(prefix.toString(), weight); + // Insert variations of suffix in front of "#" + StringBuffer combo = new StringBuffer(prefix.toString()); + for(int j = word.length() - 1; j >= 0; j--) { + combo.insert(0, word.charAt(j)); + insert(combo.toString(), weight); + } + } + } + } + + public int f(String prefix, String suffix) { + String word = suffix + "#" + prefix; + TrieNode node = root; + for (char c : word.toCharArray()) { + if (!node.children.containsKey(c)) return -1; + node = node.children.get(c); + } + return node.weight; + } + + private void insert(String word, int weight) { + TrieNode node = root; + node.weight = weight; + for (char c : word.toCharArray()) { + node.children.putIfAbsent(c, new TrieNode(weight)); + node = node.children.get(c); + node.weight = weight; + } + } +} + +/** + * Your WordFilter object will be instantiated and called as such: + * WordFilter obj = new WordFilter(words); + * int param_1 = obj.f(prefix,suffix); + */ +``` \ No newline at end of file diff --git a/Java/747. Largest Number At Least Twice of Others.java b/Java/747. Largest Number At Least Twice of Others.java new file mode 100755 index 0000000..20c10a9 --- /dev/null +++ b/Java/747. Largest Number At Least Twice of Others.java @@ -0,0 +1,67 @@ +E +tags: Array +O(n) + +多种简单操作: +- O(n) solution: 找最大值, 和第二大的值, 看是否符合题意, 就行了. +- O(2n) 最简单方法: 可以loop 两遍: 找最值; 作比较. +- O(2n) 举反例: 有一个不满足, 就够反对这个'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/76. Minimum Window Substring.java b/Java/76. Minimum Window Substring.java new file mode 100755 index 0000000..d278a05 --- /dev/null +++ b/Java/76. Minimum Window Substring.java @@ -0,0 +1,245 @@ +H +tags: Hash Table, Two Pointers, String, Sliding Window +time: O(n) +space: O(1) + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + +``` +/* +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). + +For example, +S = "ADOBECODEBANC" +T = "ABC" +Minimum window is "BANC". + +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. + + +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 +*/ + +// Method0: sliding window template +class Solution { + public String minWindow(String s, String t) { + int left = 0, right = 0, start = 0, counter = t.length(); + int minLen = Integer.MAX_VALUE, n = s.length(); + + // Starting state: build source freq freq based on t + int[] freq = new int[256]; + for (char c : t.toCharArray()) freq[c]++; + + while (right < n) { + // 1) expand right index + char head = s.charAt(right++); + if (freq[head] > 0) counter--; //counter-- whening meeting a valid char on right + freq[head]--; + + // 2) process, + while (counter == 0) { // counter == 0 indicates valid substring[start, start+minLen] + int len = right - left; + if (len < minLen) { // assign rst start + minLen = len; + start = left; + } + // 3) contract left + char tail = s.charAt(left++); + if (freq[tail] == 0) counter++; + freq[tail]++; // freq[x]++ to feed the char back and make freq map invalid + } + } + return minLen == Integer.MAX_VALUE ? "" : s.substring(start, start + minLen); // + } +} + +/** +Method1: Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- O(n) +*/ +class Solution { + public String minWindow(String s, String t) { + int end = 0, start = 0, head = 0, counter = t.length(); + int minLength = Integer.MAX_VALUE, n = s.length(); + String rst = ""; + + // Starting state: build source freq map based on t + int[] map = new int[256]; + for (char c : t.toCharArray()) map[c]++; + + while (end < n) { + if (map[s.charAt(end++)]-- > 0) counter--; //reduce counter -> 0 to find a valid end index + + while (counter == 0) { // counter == 0 indicates valid substring[head, head+minLen] + int len = end - start; + if (len < minLength) { // reassign rst head + minLength = len; + head = start; + } + // map[s.charAt(start)]==0 means: it was a positive count, and was reduced to 0 + if (map[s.charAt(start++)]++ == 0) counter++; // map[x]++ to feed the char back and make freq map invalid + } + } + return minLength == Integer.MAX_VALUE ? "" : s.substring(head, head + minLength); + } +} + +/* +Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map. +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +*/ +class Solution { + public String minWindow(String s, String t) { + int end = 0, start = 0, minLength = Integer.MAX_VALUE, n = s.length(); + String rst = ""; + + // Initialize source map for validation usage + int[] source = new int[256], target = new int[256]; + for (char c : t.toCharArray()) target[c]++; + + while (start < n) { + while (end < n && !valid(source, target)) { // maintain a valid source[] map + source[s.charAt(end)]++; + end++; + } + int length = end - start; + if (valid(source, target) && length < minLength) { + minLength = length; + rst = s.substring(start, end); + } + source[s.charAt(start++)]--; // move forward, skip a head char + } + return rst; + } + + /* + Validate if the count of source map matches targetMap. + source[i] < target[i]: not enough char count, false + */ + private boolean valid(int[] source, int[] target) { + for (int i = 0; i < 256; i++) { + if (source[i] < target[i]) return false; + } + return true; + } +} + +// Option2: Use Mapclass Solution { + Map source = new HashMap<>(), target = new HashMap<>(); + public String minWindow(String s, String t) { + int end = 0, start = 0, minLength = Integer.MAX_VALUE, n = s.length(); + String rst = ""; + + // Initialize source map for validation usage + for (char c : t.toCharArray()) target.put(c, target.getOrDefault(c, 0) + 1); + + while (start < n) { + while (end < n && !valid()) { // maintain a valid source[] map + char c = s.charAt(end); + source.put(c, source.getOrDefault(c, 0) + 1); + end++; + } + int length = end - start; + if (valid() && length < minLength) { + minLength = length; + rst = s.substring(start, end); + } + char c = s.charAt(start++); + source.put(c, source.get(c) - 1); // move forward, skip a head char + } + return rst; + } + + /* + Validate if the count of source map matches targetMap. + source[i] < target[i]: not enough char count, false + */ + private boolean valid() { + for (char c : target.keySet()) { + if (!source.containsKey(c) || source.get(c) < target.get(c)) return false; + } + return true; + } +} + +``` \ No newline at end of file diff --git a/Java/760. Find Anagram Mappings.java b/Java/760. Find Anagram Mappings.java new file mode 100755 index 0000000..5d02f24 --- /dev/null +++ b/Java/760. Find Anagram Mappings.java @@ -0,0 +1,61 @@ +E +tags: Hash Table +time: O(n) +space: O(n) + +- HashMap 存index list +- 遍历一遍数组A, 列举出所有元素 + +``` +/* +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: +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; + } + + int[] result = new int[A.length]; + Map> map = new HashMap<>(); + + // Prepare the map + for (int i = 0; i < B.length; i++) { + map.putIfAbsent(B[i], new ArrayList<>()); + map.get(B[i]).add(i); + } + + for (int i = 0; i < A.length; i++) { + 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/766. Toeplitz Matrix.java b/Java/766. Toeplitz Matrix.java new file mode 100755 index 0000000..4022094 --- /dev/null +++ b/Java/766. Toeplitz Matrix.java @@ -0,0 +1,87 @@ +E +tags: Array +time: O(mn) +space: O(1) + +#### Check diagonal +- 似乎没什么算法特点, 就是array基本运算, 然后分割成一个helper function去做重复计算, 剪短代码. +- 注意check MxN 的分界线. + +#### Simpler Solution +- the goal is to check [i][j] == [i+1][j+1] for every i and j. + +``` +/* +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, m = matrix[0].length; + for (int i = 0; i < n; i++) { + if(!check(i, 0, matrix)) return false; + } + + for (int j = 0; j < m; j++) { + if(!check(0, j, matrix)) return false; + } + + return true; + } + + private boolean check(int x, int y, int[][] matrix) { + int n = matrix.length, 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; + } +} + +// simplier code +class Solution { + public boolean isToeplitzMatrix(int[][] matrix) { + for (int i = 0; i < matrix.length - 1; i++) { + for (int j = 0; j < matrix[i].length - 1; j++) { + if (matrix[i][j] != matrix[i + 1][j + 1]) return false; + } + } + return true; + } +} + +``` \ No newline at end of file diff --git a/Java/767. Reorganize String.java b/Java/767. Reorganize String.java new file mode 100755 index 0000000..b367d77 --- /dev/null +++ b/Java/767. Reorganize String.java @@ -0,0 +1,230 @@ +M +tags: String, Heap, Greedy, Sort, Hash Table +time: O(m), m = # of unique letters +space: O(nLogm), n = length + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + +``` +/* +Given a string S, check if the letters can be rearranged so that two characters that are adjacent to each other are not the same. + +If possible, output any possible result. If not possible, return the empty string. + +Example 1: + +Input: S = "aab" +Output: "aba" +Example 2: + +Input: S = "aaab" +Output: "" +Note: + +S will consist of lowercase letters and have length in range [1, 500]. + +*/ +// Method, Option0. Similar to `621. Task Scheduler` +class Solution { + public String reorganizeString(String S) { + if (S == null || S.length() == 0) return ""; + + Map map = new HashMap<>(); + for (char c : S.toCharArray()) { + map.putIfAbsent(c, new Letter(c, 0)); + map.get(c).count += 1; + if (map.get(c).count > (S.length() + 1) / 2) return ""; + } + + PriorityQueue pq = new PriorityQueue<>((a, b) -> (b.count - a.count)); + pq.addAll(map.values()); + + StringBuilder sb = new StringBuilder(); + while (!pq.isEmpty()) { + int k = 2; // two slots to fill + Queue buffer = new LinkedList<>(); + while (k > 0 && !pq.isEmpty()) { + Letter letter = pq.poll(); + sb.append(letter.c); + letter.count -= 1; + if (letter.count > 0) buffer.offer(letter); + k--; + } + + pq.addAll(buffer); + } + return sb.length() == S.length() ? sb.toString() : ""; + } + + class Letter { + char c; + int count; + public Letter(char c, int count) { + this.c = c; + this.count = count; + } + } +} + + +/** +Method1: K(k=2) seats apart problem +Option1 + */ +class Solution { + public String reorganizeString(String S) { + if (S == null || S.length() == 0) return ""; + Map map = new HashMap<>(); + for (char c : S.toCharArray()) { + map.put(c, map.getOrDefault(c, 0) + 1); + } + PriorityQueue> pq = new PriorityQueue<>((a, b) -> (b.getValue() - a.getValue())); + pq.addAll(map.entrySet()); + StringBuilder sb = new StringBuilder(); + Queue> buffer = new LinkedList<>(); + while (!pq.isEmpty()) { + Map.Entry entry = pq.poll(); + sb.append(entry.getKey()); + entry.setValue(entry.getValue() - 1); + buffer.offer(entry); + if (buffer.size() == 2) { + Map.Entry temp = buffer.poll(); + if (temp.getValue() > 0) pq.offer(temp); + } + } + return sb.length() == S.length() ? sb.toString() : ""; + } +} + +// Method1.Option2: Slight change by using object +class Solution { + public String reorganizeString(String S) { + if (S == null || S.length() == 0) return ""; + + Map map = new HashMap<>(); + for (char c : S.toCharArray()) { + map.putIfAbsent(c, new Letter(c, 0)); + map.get(c).count += 1; + if (map.get(c).count > (S.length() + 1) / 2) return ""; + } + + PriorityQueue pq = new PriorityQueue<>((a, b) -> (b.count - a.count)); + pq.addAll(map.values()); + + StringBuilder sb = new StringBuilder(); + Queue buffer = new LinkedList<>(); + while (!pq.isEmpty()) { + Letter letter = pq.poll(); + sb.append(letter.c); + letter.count -= 1; + buffer.offer(letter); + if (buffer.size() > 1) { + Letter temp = buffer.poll(); + if (temp.count > 0) pq.offer(temp); + } + } + return sb.length() == S.length() ? sb.toString() : ""; + } + + class Letter { + char c; + int count; + public Letter(char c, int count) { + this.c = c; + this.count = count; + } + } +} + + +// Method2: Sanitize the input when building the queue, and process it like merging k lists + +// priority queue with comparator is a bit faster +PriorityQueue queue = new PriorityQueue<>(new Comparator(){ + public int compare(int[] a, int[] b){ + return a[1] > b[1] ? -1 : 1; + } +}); + + + +class Solution { + public String reorganizeString(String S) { + if (S == null || S.length() <= 1) return S; + + // convert and build queue (eleminate false) + Map map = buildCountMap(S); + PriorityQueue queue = buildCharQueue(map); + StringBuffer sb = new StringBuffer(); + + // Assume the input is sanitized: largest population won't cause additional tail + while(!queue.isEmpty()) { + int[] curr = queue.poll(); + // not duplicating, add! + if (sb.length() == 0 || curr[0] != sb.charAt(sb.length() - 1)) { + sb.append((char) curr[0]); + if (--curr[1] > 0) queue.add(curr); + } else { + // found duplicate, pick another one + int[] next = queue.poll(); + sb.append((char) next[0]); + if (--next[1] > 0) queue.add(next); + + // not used, add it back + queue.add(curr); + } + } + return sb.toString(); + } + + private Map buildCountMap(String S) { + Map map = new HashMap<>(); + for (char c : S.toCharArray()) { + int count = map.getOrDefault(c, 0) + 1; + if (count > (S.length() + 1) / 2) return new HashMap<>(); // c appear more than half, fail + map.put(c, count); + } + return map; + } + + // Greedy: always consume the highest count first + // Use priority queue to sort int[]{char, c} + private PriorityQueue buildCharQueue (Map map) { + PriorityQueue queue = new PriorityQueue<>((a,b) -> b[1] - a[1]); + for (char c : map.keySet()) { + queue.add(new int[]{c, map.get(c)}); + } + return queue; + } +} + +``` \ No newline at end of file diff --git a/Java/771. Jewels and Stones.java b/Java/771. Jewels and Stones.java new file mode 100755 index 0000000..5654581 --- /dev/null +++ b/Java/771. Jewels and Stones.java @@ -0,0 +1,54 @@ +E +tags: Hash Table +time: O(n) +space: O(n) + +- 给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/78. Subsets.java b/Java/78. Subsets.java new file mode 100755 index 0000000..6309cf5 --- /dev/null +++ b/Java/78. Subsets.java @@ -0,0 +1,177 @@ +M +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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + +``` +/* +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 +/* +DFS: pick or no pick. track level. when level == nums.length, output. +*/ +class Solution { + public List> subsets(int[] nums) { + List> result = new ArrayList<>(); + if (nums == null || nums.length == 0) return result; + + Queue> queue = new LinkedList<>(); + queue.offer(new ArrayList<>()); + + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { + List indexRow = queue.poll(); + result.add(buildResult(indexRow, nums)); // record result + + // 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); // backtrack + } + } + } + return result; + } + + private List buildResult(List indexRow, int[] nums) { + List list = new ArrayList<>(); + for (int index : indexRow) list.add(nums[index]); + return list; + } +} + +``` \ No newline at end of file diff --git a/Java/785. Is Graph Bipartite.java b/Java/785. Is Graph Bipartite.java new file mode 100644 index 0000000..1877651 --- /dev/null +++ b/Java/785. Is Graph Bipartite.java @@ -0,0 +1,115 @@ +M +tags: Garph, DFS, BFS +time: O(n) +space: O(n) + +#### DFS marking each node with a state +- `bipartite` require each node to be in exact 1 party, which means it only has 1 state +- DFS to mark node with one state; and mark its edges as reversed state + - If any node state has been assigned by different from desired one, return false. + +#### BFS, Queue +- Use `Boolean states[i]` to represent visted & state +- Try all nodes with for loop, and skip visited nodes (similar validation rules as in dfs) +- In `int next : graph[curr]`, test next level first before adding. + +``` +/* +Given an undirected graph, return true if and only if it is bipartite. + +Recall that a graph is bipartite if we can split it's set of nodes into two independent subsets A and B such that every edge in the graph has one node in A and another node in B. + +The graph is given in the following form: graph[i] is a list of indexes j for which the edge between nodes i and j exists. Each node is an integer between 0 and graph.length - 1. There are no self edges or parallel edges: graph[i] does not contain i, and it doesn't contain any element twice. + +Example 1: +Input: [[1,3], [0,2], [1,3], [0,2]] +Output: true +Explanation: +The graph looks like this: +0----1 +| | +| | +3----2 +We can divide the vertices into two groups: {0, 2} and {1, 3}. +Example 2: +Input: [[1,2,3], [0,2], [0,1,3], [0,2]] +Output: false +Explanation: +The graph looks like this: +0----1 +| \ | +| \ | +3----2 +We cannot find a way to divide the set of nodes into two independent subsets. + + +Note: + +graph will have length in range [1, 100]. +graph[i] will contain integers in range [0, graph.length - 1]. +graph[i] will not contain i or duplicate values. +The graph is undirected: if any element j is in graph[i], then i will be in graph[j]. +*/ + +/* +The solution is unique: when traversing over all nodes, make sure to exhaust all edges before moving on to next node. +Validation: use a state map to record state, and validate at run time +time: O(n), visit all nodes once +space: O(n), store all states, and worst case O(n) stack +*/ + +class Solution { + public boolean isBipartite(int[][] graph) { + + int n = graph.length; + int[] states = new int[n]; + for (int i = 0; i < n; i++) { + if (states[i] != 0) continue; + if (!dfs(graph, states, 1, i)) return false; + } + + return true; + } + + // traverse and validate + private boolean dfs(int[][] graph, int[] states, int state, int node) { + if (states[node] != 0) return states[node] == state; + if (graph[node].length == 0) return true; + + states[node] = state; + + for (int val : graph[node]) { + if (!dfs(graph, states, - state, val)) return false; + } + return true; + + } +} + +// Method2: BFS +class Solution { + public boolean isBipartite(int[][] graph) { + int n = graph.length; + Boolean[] states = new Boolean[n]; + + for (int i = 0; i < n; i++) { // some nodes has 0 edge, so try all + if (states[i] != null) continue; // skip visited starting node + Queue queue = new LinkedList(); + queue.offer(i); + states[i] = true; + while (!queue.isEmpty()) { + int curr = queue.poll(); + for (int next : graph[curr]) { + if (states[next] != null) { + if (states[next] == states[curr]) return false; + continue; + } + states[next] = !states[curr]; + queue.offer(next); + } + } + } + return true; + } +} +``` \ No newline at end of file diff --git a/Java/788. Rotated Digits.java b/Java/788. Rotated Digits.java new file mode 100755 index 0000000..bd825de --- /dev/null +++ b/Java/788. Rotated Digits.java @@ -0,0 +1,67 @@ +E +tags: String, Basic Implementation +time: O(n) +space: O(n) + +#### Basic Implementation of the rules +- [3,4,7] -> cannot rotate, failures. Must NOT have. set1 +- 2,5,6,9 -> good candidates. Must have 1. set2 +- [0,1,8] -> goes back to itself. can have +- loop over [1, N], count=int[10] appearance. + - set1 meet 0 + - set2 meet at least 1 + +``` +/* +X is a good number if after rotating each digit individually by 180 degrees, we get a valid number that is different from X. Each digit must be rotated - we cannot choose to leave it alone. + +A number is valid if each digit remains a digit after rotation. 0, 1, and 8 rotate to themselves; 2 and 5 rotate to each other; 6 and 9 rotate to each other, and the rest of the numbers do not rotate to any other number and become invalid. + +Now given a positive number N, how many numbers X from 1 to N are good? + +Example: +Input: 10 +Output: 4 +Explanation: +There are four good numbers in the range [1, 10] : 2, 5, 6, 9. +Note that 1 and 10 are not good numbers, since they remain unchanged after rotating. +Note: + +N will be in range [1, 10000]. +*/ + +/* +- [3,4,7] -> cannot rotate, failures. Must NOT have. set1 +- 2,5,6,9 -> good candidates. Must have 1. set2 +- [0,1,8] -> goes back to itself. can have +- loop over [1, N], count=int[10] appearance. + - set1 meet 0 + - set2 meet at least 1 +*/ +class Solution { + public int rotatedDigits(int N) { + + int start = 1; + int count = 0; + while (start++ < N) { + count += validate(freq(start)) ? 1 : 0; + } + return count; + } + + private int[] freq(int num) { + int[] count = new int[10]; + while (num != 0) { + count[num%10]++; + num /= 10; + } + return count; + } + private boolean validate(int[] count) { + int failure = count[3] + + count[4] + count[7]; + if (failure != 0) return false; + int success = count[2] + + count[5] + count[6] + count[9]; + return success != 0; + } +} +``` \ No newline at end of file diff --git a/Java/796. Rotate String.java b/Java/796. Rotate String.java new file mode 100755 index 0000000..5381662 --- /dev/null +++ b/Java/796. Rotate String.java @@ -0,0 +1,113 @@ +E +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) + +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/Java/8. String to Integer (atoi).java b/Java/8. String to Integer (atoi).java new file mode 100755 index 0000000..e8ab1c9 --- /dev/null +++ b/Java/8. String to Integer (atoi).java @@ -0,0 +1,149 @@ +M +tags: Math, String +time: O(n) +space: O(n) + +#### String +- Handling use cases +- Parse steps: + - 0. trim space + - 1 parse operator + - 2 trim leading zero + - 3. get number string +- Validation: + - 1. max length over max integer length + - 2. exceed min/max value +- Alternatively, regular expression, but not applicable in interview: 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. + +*/ +/* +- cut space +- validate if operator is prefixed or if letters +- finds all digits into sb +- convert +*/ +class Solution { + public int myAtoi(String str) { + if (str == null || str.length() == 0 || str.trim().length() == 0) return 0; + + String s = str.trim(); + int i = 0, n = s.length(), maxIntLen = String.valueOf(Integer.MAX_VALUE).length(); + char operator = ' '; + + // step1: parse operator + if (i < n && (s.charAt(i) == '+' || s.charAt(i) == '-')) operator = s.charAt(i++); + // step2: parse zero + while (i < n && s.charAt(i) == '0') i++; // parse 0 + // step3: get number string + StringBuffer sb = getNumStr(s, i); + + // validation1: max length over max integer length + if (sb.length() > maxIntLen) return operator == '-' ? Integer.MIN_VALUE : Integer.MAX_VALUE; + // validateion2: exceed min/max value + long num = Long.parseLong(sb.toString()) * (operator == '-' ? -1 : 1); + if (num > Integer.MAX_VALUE) return Integer.MAX_VALUE; + if (num < Integer.MIN_VALUE) return Integer.MIN_VALUE; + return (int) num; + } + + private boolean isDigit(char c) { + return c >= '0' && c <= '9'; + } + + private StringBuffer getNumStr(String s, int i) { + StringBuffer sb = new StringBuffer(); + if (i >= s.length() || !isDigit(s.charAt(i))) { + sb.append(0); // leading char validation, return 0 if invalid + return sb; + } + while (i < s.length()) { + char c = s.charAt(i++); + if (!isDigit(c)) break; + sb.append(c); + } + return sb; + } +} + +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/80.Remove Duplicates from Sorted Array II.java b/Java/80.Remove Duplicates from Sorted Array II.java new file mode 100755 index 0000000..3aeffe2 --- /dev/null +++ b/Java/80.Remove Duplicates from Sorted Array II.java @@ -0,0 +1,79 @@ +M +1526348390 +tags: Array, Two Pointers + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Basic +- 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其实就足够. + +#### Follow up: k duplicates, Two Pointers +- when index i and i-1 are diff, use count=1 to start +- in while loop, keep count++ until count==k +- reset when next diff comes in + +``` +/* +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; // skip index 0 because that can always exist + for (int i = 2; i < nums.length; i++) { + if (nums[i] != nums[index] || (nums[i] == nums[index] && nums[i] != nums[index - 1])) { + nums[++index] = nums[i]; + } + } + return index + 1; // return length + } +} +``` \ No newline at end of file diff --git a/Java/824. Goat Latin.java b/Java/824. Goat Latin.java new file mode 100755 index 0000000..29a84f6 --- /dev/null +++ b/Java/824. Goat Latin.java @@ -0,0 +1,73 @@ +E +tags: String, Basic Implementation +time: O(n) +space: O(1) + +``` + +/* +We would like to convert the sentence to "Goat Latin" (a made-up language similar to Pig Latin.) + +The rules of Goat Latin are as follows: + +If a word begins with a vowel (a, e, i, o, or u), append "ma" to the end of the word. +For example, the word 'apple' becomes 'applema'. + +If a word begins with a consonant (i.e. not a vowel), remove the first letter and append it to the end, then add "ma". +For example, the word "goat" becomes "oatgma". + +Add one letter 'a' to the end of each word per its word index in the sentence, starting with 1. +For example, the first word gets "a" added to the end, the second word gets "aa" added to the end and so on. +Return the final sentence representing the conversion from S to Goat Latin. + +Example 1: + +Input: "I speak Goat Latin" +Output: "Imaa peaksmaaa oatGmaaaa atinLmaaaaa" +Example 2: + +Input: "The quick brown fox jumped over the lazy dog" +Output: "heTmaa uickqmaaa rownbmaaaa oxfmaaaaa umpedjmaaaaaa overmaaaaaaa hetmaaaaaaaa azylmaaaaaaaaa ogdmaaaaaaaaaa" + + +Notes: + +S contains only uppercase, lowercase and spaces. Exactly one space between each word. +1 <= S.length <= 150. + */ + +/* +1. check letter and apply ruleA/B. +2. use index to append # of 'a' to end of string. + +- split into array +- build all back together +*/ +class Solution { + Character[] chars = {'a', 'e', 'i', 'o', 'u', 'A', 'E', 'I', 'O', 'U'}; + HashSet set = new HashSet<>(Arrays.asList(chars)); + + public String toGoatLatin(String S) { + StringBuffer sb = new StringBuffer(); + String[] arr = S.split(" "); + for (int i = 0; i < arr.length; i++) { + sb.append(convert(arr[i], i)); + sb.append(" "); + } + return sb.toString().trim(); + } + + public String convert(String s, int index) { + StringBuffer sb = new StringBuffer(s); + char c = s.charAt(0); + + if (!set.contains(s.charAt(0))) { + sb.deleteCharAt(0); + sb.append(c); + } + sb.append("ma"); + for (int i = 0; i <= index; i++) sb.append("a"); + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/83. Remove Duplicates from Sorted List.java b/Java/83. Remove Duplicates from Sorted List.java new file mode 100755 index 0000000..29664db --- /dev/null +++ b/Java/83. Remove Duplicates from Sorted List.java @@ -0,0 +1,120 @@ +E +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会非常清晰 + +``` +/* +Given a sorted linked list, delete all duplicates such that each element appear only once. + +Example 1: + +Input: 1->1->2 +Output: 1->2 +Example 2: + +Input: 1->1->2->3->3 +Output: 1->2->3 +*/ + + +/** + * Definition for ListNode + * public class ListNode { + * int val; + * ListNode next; + * ListNode(int x) { + * val = x; + * next = null; + * } + * } + */ +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 + * @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/Java/843. Guess the Word.java b/Java/843. Guess the Word.java new file mode 100755 index 0000000..827a603 --- /dev/null +++ b/Java/843. Guess the Word.java @@ -0,0 +1,170 @@ +H +tags: MiniMax +time: TODO +space: TODO + +TODO: revist time/space complexity + +#### Minimax, find target, and use it to eliminate +- `擒贼先擒王`: find the candidate that has largest set of correlations with the rest candidates, and eliminate based on this candidate. + - `approach A`: count the candidate that has 0 overlaps, find min of this poll + - `approach B`: count the candidate that has largest # of connections +- cross-compare, count `match==0` : find candidates that has 0 overlap with others + - pick `min-count candidate A`: it is a candidate that has overlaps with most strings (since 0-match-count is lowest) + - the above candidate will help to **eliminate** a largerset of overlapped candidates + - guess A, return matchCount. +- filter set with matchCount: eliminateCandidate + +``` +/* +This problem is an interactive problem new to the LeetCode platform. + +We are given a word list of unique words, each word is 6 letters long, and one word in this list is chosen as secret. + +You may call master.guess(word) to guess a word. The guessed word should have type string and must be from the original list with 6 lowercase letters. + +This function returns an integer type, representing the number of exact matches (value and position) of your guess to the secret word. Also, if your guess is not in the given wordlist, it will return -1 instead. + +For each test case, you have 10 guesses to guess the word. At the end of any number of calls, if you have made 10 or less calls to master.guess and at least one of these guesses was the secret, you pass the testcase. + +Besides the example test case below, there will be 5 additional test cases, each with 100 words in the word list. The letters of each word in those testcases were chosen independently at random from 'a' to 'z', such that every word in the given word lists is unique. + +Example 1: +Input: secret = "acckzz", wordlist = ["acckzz","ccbazz","eiowzz","abcczz"] + +Explanation: + +master.guess("aaaaaa") returns -1, because "aaaaaa" is not in wordlist. +master.guess("acckzz") returns 6, because "acckzz" is secret and has all 6 matches. +master.guess("ccbazz") returns 3, because "ccbazz" has 3 matches. +master.guess("eiowzz") returns 2, because "eiowzz" has 2 matches. +master.guess("abcczz") returns 4, because "abcczz" has 4 matches. + +We made 5 calls to master.guess and one of them was the secret, so we pass the test case. +Note: Any solutions that attempt to circumvent the judge will result in disqualification. +*/ + + + +// Minimax ApproachA: +/** + * // This is the Master's API interface. + * // You should not implement it, or speculate about its implementation + * interface Master { + * public int guess(String word) {} + * } + */ +/* +- pick a word A, if return 3 matches: eliminate all that has less than 3 matches with A +- pick another word, return x matches, eliminate all that has less than x matches +- repeat until found +*/ +class Solution { + public void findSecretWord(String[] wordlist, Master master) { + Set set = new HashSet<>(); + for (String ss : wordlist) set.add(ss); + for(int i = 0; i < 10; i++) { + String target = pickCandidate(set); + int count = master.guess(target); + set = eliminateCandidate(set, target, count); + if (set.isEmpty()) break; + } + } + + // Pick candidate that has largest correlation with the rest: but calculating `0-match-count` + // Use it to eliminate its friends. + private String pickCandidate(Set words) { + Map matchCount = new HashMap<>(); + for (String a : words) { + matchCount.putIfAbsent(a, 0); + for (String b : words) { + if (a.equals(b)) continue; + if (countMatch(a, b) == 0) matchCount.put(a, matchCount.get(a) + 1); + } + } + // find min-0-match-count candidate: that has overlaps with most words + int min = Integer.MAX_VALUE; + String candidate = null; + for (String s : matchCount.keySet()) { + if (matchCount.get(s) < min) { + min = matchCount.get(s); + candidate = s; + } + } + return candidate; + } + + public Set eliminateCandidate(Set words, String target, int count) { + Set set = new HashSet<>(); + for (String s : words) { + if (s.equals(target)) continue; + if (countMatch(s, target) == count) set.add(s); + } + return set; + } + + private int countMatch(String a, String b) { + int count = 0; + for (int i = 0; i < 6; i++) { + if (a.charAt(i) == b.charAt(i)) count++; + } + return count; + } +} + + +// Minimax ApproachB: +class Solution { + public void findSecretWord(String[] wordlist, Master master) { + Set set = new HashSet<>(); + for (String ss : wordlist) set.add(ss); + for(int i = 0; i < 10; i++) { + String target = pickCandidate(set); + int count = master.guess(target); + set = eliminateCandidate(set, target, count); + if (set.isEmpty()) break; + } + } + + // Pick candidate that has largest/max number of connections with the rest, but calculating `match-count` + // Use it to eliminate its friends. + private String pickCandidate(Set words) { + Map matchCount = new HashMap<>(); + for (String a : words) { + matchCount.putIfAbsent(a, 0); + for (String b : words) { + if (a.equals(b)) continue; + if (countMatch(a, b) != 0) matchCount.put(a, matchCount.get(a) + 1); + } + } + // find min count candidate + int max = Integer.MIN_VALUE; + String candidate = null; + for (String s : matchCount.keySet()) { + if (matchCount.get(s) > max) { + max = matchCount.get(s); + candidate = s; + } + } + return candidate; + } + + public Set eliminateCandidate(Set words, String target, int count) { + Set set = new HashSet<>(); + for (String s : words) { + if (s.equals(target)) continue; + if (countMatch(s, target) == count) set.add(s); + } + return set; + } + + private int countMatch(String a, String b) { + int count = 0; + for (int i = 0; i < 6; i++) { + if (a.charAt(i) == b.charAt(i)) count++; + } + return count; + } +} + +``` \ No newline at end of file diff --git a/Java/844. Backspace String Compare.java b/Java/844. Backspace String Compare.java new file mode 100755 index 0000000..3897d15 --- /dev/null +++ b/Java/844. Backspace String Compare.java @@ -0,0 +1,99 @@ +E +tags: Two Pointers, Stack +time: O(n) +space: O(1) + +#### Method1: Two pointers to backtack from end of string +- time: O(n) +- space: O(1) + +#### Method2: Stack +- need to remove entity just added +- use stack to hold array content; pop if # is found + +``` +/* +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? +*/ + +// Method1: Two pointers to backtack from end of string +class Solution { + public boolean backspaceCompare(String s, String t) { + + int i = s.length() - 1, j = t.length() - 1; + while (i >= 0 || j >= 0) { + i = backtrack(s, i); + j = backtrack(t, j); + if (i >= 0 && j >= 0 && s.charAt(i) != t.charAt(j)) return false; + if ((i >= 0) != (j >= 0)) return false; + i--; + j--; + } + return true; + } + + private int backtrack(String s, int index) { + int i = index, count = 0; + while (i >= 0) { + if (s.charAt(i) == '#') { + count++; + i--; + } else if (count > 0) { + count--; + i--; + } else break; + } + return i; + } +} + +/* +Method2: Stack: use stack to hold array content; pop if # if found +*/ +class Solution { + public boolean backspaceCompare(String s, String t) { + + return build(s).equals(build(t)); + } + + private String build(String s) { + Stack stack = new Stack<>(); + for (char c : s.toCharArray()) { + if (c != '#') stack.push(c); + else if (!stack.isEmpty()) stack.pop(); + } + StringBuffer sb = new StringBuffer(); + while (!stack.isEmpty()) sb.insert(0, stack.pop()); + return sb.toString(); + } +} +``` \ No newline at end of file diff --git a/Java/849. Maximize Distance to Closest Person.java b/Java/849. Maximize Distance to Closest Person.java new file mode 100755 index 0000000..6a31117 --- /dev/null +++ b/Java/849. Maximize Distance to Closest Person.java @@ -0,0 +1,74 @@ +E +tags: Array, Basic Implementation, Two Pointers +time: O(n) +space: O(1) + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, Two Pointers: 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; // switch to find new range + } + } + + // 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/850. Rectangle Area II.java b/Java/850. Rectangle Area II.java new file mode 100755 index 0000000..088482b --- /dev/null +++ b/Java/850. Rectangle Area II.java @@ -0,0 +1,115 @@ +H +tags: Segment Tree, Sweep Line +time: O(n^2) +space: O(n) + +#### Sweep Line + Merge Interval concept +- Inspired by: https://leetcode.com/problems/rectangle-area-ii/discuss/137941/Java-TreeMap-solution-inspired-by-Skyline-and-Meeting-Room +- First consider regular sweep line and realize problem: each vertical line has multiple block segments + - Easy: take a list of vertical dots, and calculate the height diff + - We can use a TreeMap with y-coordinate as key, so to `natural sort by y-coordinate` +- Trick: can NOT remove used y coordinate from map, because the rectangle may continue to expand to right side. +- apply simple equation to calc area: `(long)preY * (p.x - preX)` +- time: + - sort initial queue: O(nlogn) + - process queue: O(n) + - TreeMap insertion: O(logn) + - TreeMap traversal: O(n) + - overall, process queue can be O(n^2) +- space: O(n) + +#### Sweep Line concept, bottom->top sweep +- https://leetcode.com/problems/rectangle-area-ii/discuss/137914/C%2B%2BPython-Discretization-and-O(NlogN) + +#### Segment Tree +- TODO lol + +``` + +/* +We are given a list of (axis-aligned) rectangles. Each rectangle[i] = [x1, y1, x2, y2] , where (x1, y1) are the coordinates of the bottom-left corner, and (x2, y2) are the coordinates of the top-right corner of the ith rectangle. + +Find the total area covered by all rectangles in the plane. Since the answer may be too large, return it modulo 10^9 + 7. + + + +Example 1: + +Input: [[0,0,2,2],[1,0,2,3],[1,0,3,1]] +Output: 6 +Explanation: As illustrated in the picture. +Example 2: + +Input: [[0,0,1000000000,1000000000]] +Output: 49 +Explanation: The answer is 10^18 modulo (10^9 + 7), which is (10^9)^2 = (-7)^2 = 49. +Note: + +1 <= rectangles.length <= 200 +rectanges[i].length = 4 +0 <= rectangles[i][j] <= 10^9 +The total area covered by all rectangles will never exceed 2^63 - 1 and thus will fit in a 64-bit signed integer. +*/ + + +/* +#### Sweep Line +- Seems that we can sweep vertically like Meeting Room, Skyline; we we can sweep horizontally from bottom->top + + +*/ + +class Solution { + class Point { + int x, y, count; // count used for vertical merge sort + Point(int x, int y, int count) { + this.x = x; + this.y = y; + this.count = count; + } + } + public int rectangleArea(int[][] rectangles) { + int mod = 1000000007; + PriorityQueue pq = new PriorityQueue<>((a, b) -> (a.x == b.x) ? b.y - a.y : a.x - b.x); + for (int[] r : rectangles) { + pq.offer(new Point(r[0], r[1], 1)); + pq.offer(new Point(r[0], r[3], -1)); + pq.offer(new Point(r[2], r[1], -1)); + pq.offer(new Point(r[2], r[3], 1)); + } + TreeMap map = new TreeMap<>(); + int preX = 0, preY = 0, result = 0; + + // O(n) + while (!pq.isEmpty()) { + Point p = pq.peek(); + while (!pq.isEmpty() && pq.peek().x == p.x) { + p = pq.poll(); + map.put(p.y, map.getOrDefault(p.y, 0) + p.count); + } + // preX/preY=0 initialy; wait till 2nd cycle, because we ewant `p.x` to reference next x + result += ((long)preY * (p.x - preX)) % mod; + result %= mod; + preY = calcHeightDiff(map); + preX = p.x; + } + + return result; + } + + // Use TreeMap sort entry by key by natrual order of Y-coordinate + // merge interval vertically and produce a height + // WHY not using a PQ to hold? The y values stays in map until later use; PQ will consume and drop the record. + // O(nlogn) + private int calcHeightDiff(TreeMap map) { + int height = 0, pre = 0, count = 0; + for (Map.Entry e : map.entrySet()) { + if (count > 0) height += e.getKey() - pre; + count += e.getValue(); + pre = e.getKey(); + } + return height; + } +} + +``` \ No newline at end of file diff --git a/Java/852. Peak Index in a Mountain Array.java b/Java/852. Peak Index in a Mountain Array.java new file mode 100755 index 0000000..76be791 --- /dev/null +++ b/Java/852. Peak Index in a Mountain Array.java @@ -0,0 +1,55 @@ +E +tags: Binary Search +time: O(logn) +space: O(1) + +#### Binary Search +- binary search to find A[i-1] < A[i] < A[i+1] + - if [mid-1] < [mid+1], on left slope, start = mid + - if [mid-1] > [mid+1], on right slope, end = mid +- init: start == 1, end = n - 2; + +``` +/** +Let's call an array A a mountain if the following properties hold: + +A.length >= 3 +There exists some 0 < i < A.length - 1 such that A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1] +Given an array that is definitely a mountain, return any i such that A[0] < A[1] < ... A[i-1] < A[i] > A[i+1] > ... > A[A.length - 1]. + +Example 1: + +Input: [0,1,0] +Output: 1 +Example 2: + +Input: [0,2,1,0] +Output: 1 +Note: + +3 <= A.length <= 10000 +0 <= A[i] <= 10^6 +A is a mountain, as defined above. + */ + +/* +- binary search to find A[i-1] < A[i] < A[i+1] +- init: start == 1, end = n - 2; +- if [mid-1] < [mid+1], on left slope, start = mid +- if [mid-1] > [mid+1], on right slope, end = mid +*/ +class Solution { + public int peakIndexInMountainArray(int[] A) { + int start = 1, end = A.length - 2; + int mid = start + (end - start)/2; + while (start + 1 < end) { + mid = start + (end - start)/2; + if (A[mid-1] < A[mid] && A[mid] > A[mid+1]) return mid; + else if (A[mid-1] < A[mid+1]) start = mid; + else if (A[mid-1] > A[mid+1]) end = mid; + } + return A[start] < A[end] ? end : start; + } +} + +``` \ No newline at end of file diff --git a/Java/855. Exam Room.java b/Java/855. Exam Room.java new file mode 100755 index 0000000..b36e145 --- /dev/null +++ b/Java/855. Exam Room.java @@ -0,0 +1,191 @@ +M +tags: PriorityQueue, Sort, TreeMap, TreeSet +time: O(logn) +space: O(n) + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + +``` +/* +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)); + } +} + + +// Method2: Ordered Map +class RangeModule { + + class Interval implements Comparable { + int start, end; + public Interval(int start, int end) { + this.start = start; + this.end = end; + } + + public int compareTo(Interval target) { + if (this.end == target.end) return this.start - target.start; // if same end, sort by start + return this.end - target.end; // otherwise, sort by start + } + } + + TreeSet ranges; + public RangeModule() { + ranges = new TreeSet<>(); + } + + public void addRange(int start, int end) { + Iterator iterator = ranges.tailSet(new Interval(0, start - 1)).iterator(); // make sure it overlaps on `start-1`, since [start-1, start] can merge into 1 interval + while (iterator.hasNext()) { + Interval interval = iterator.next(); + if (end < interval.start) break; // out of range + // remove the curr element & mark new range + iterator.remove(); + start = Math.min(start, interval.start); + end = Math.max(end, interval.end); + } + ranges.add(new Interval(start, end)); + } + + public boolean queryRange(int start, int end) { + Interval interval = ranges.higher(new Interval(0, start)); // find range larger or equal to [0, start) + return interval != null && interval.start <= start && end <= interval.end; + } + + public void removeRange(int start, int end) { + Iterator iterator = ranges.tailSet(new Interval(0, start)).iterator(); // find [start, ...] elements + List newRanges = new ArrayList<>(); + while (iterator.hasNext()) { + Interval interval = iterator.next(); + if (end < interval.start) break; // out of range + // remove the curr element & break down into new range + iterator.remove(); + if (interval.start < start) newRanges.add(new Interval(interval.start, start)); + if (end < interval.end) newRanges.add(new Interval(end, interval.end)); + } + for (Interval interval : newRanges) ranges.add(interval); + } +} + + +/** + * 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/875. Koko Eating Bananas.java b/Java/875. Koko Eating Bananas.java new file mode 100755 index 0000000..a359b26 --- /dev/null +++ b/Java/875. Koko Eating Bananas.java @@ -0,0 +1,77 @@ +M +tags: Binary Search +time: O(n*logM) +space: O(1) + + +#### Binary Search +- Bianry serach on the min/max value range +- The mid value is calcualted with helper function `calc(piples, k)` +- find celing: `count += (i - 1) / k + 1`, faster than `Math.ceil(i / k)` +- time: O(logm) to find the best velocity, assume total range is m; O(n) for each `calc` call + +``` +/* +Koko loves to eat bananas. There are N piles of bananas, the i-th pile has piles[i] bananas. The guards have gone and will come back in H hours. + +Koko can decide her bananas-per-hour eating speed of K. Each hour, she chooses some pile of bananas, and eats K bananas from that pile. If the pile has less than K bananas, she eats all of them instead, and won't eat any more bananas during this hour. + +Koko likes to eat slowly, but still wants to finish eating all the bananas before the guards come back. + +Return the minimum integer K such that she can eat all the bananas within H hours. + + + +Example 1: + +Input: piles = [3,6,7,11], H = 8 +Output: 4 +Example 2: + +Input: piles = [30,11,23,4,20], H = 5 +Output: 30 +Example 3: + +Input: piles = [30,11,23,4,20], H = 6 +Output: 23 + + +Note: + +1 <= piles.length <= 10^4 +piles.length <= H <= 10^9 +1 <= piles[i] <= 10^9 + +*/ +class Solution { + public int minEatingSpeed(int[] piles, int H) { + + int start = 1, end = 1_000_000_000; + int max = end; + + while (start + 1 < end) { + int mid = start + (end - start) / 2; + int count = calc(piles, mid); + if (count > H) start = mid; + else { + if (mid + 1 < max && calc(piles, mid + 1) <= H) { + end = mid; + continue; + } + return start; + } + } + + if (calc(piles, start) <= H) return start; + return end; + } + + private int calc(int[] piles, double k) { + int count = 0; + for (int i : piles) { + count += Math.ceil(i / k); + } + return count; + } +} +``` \ No newline at end of file diff --git a/Java/876. Middle of Linked List.java b/Java/876. Middle of Linked List.java new file mode 100755 index 0000000..afbc313 --- /dev/null +++ b/Java/876. Middle of Linked List.java @@ -0,0 +1,86 @@ +E +tags: Linked List + +找Linked List的中间node + +#### 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + +``` + +/** +LeetCode +Given a non-empty, singly linked list with head node head, return a middle node of linked list. + +If there are two middle nodes, return the second middle node. + + + +Example 1: + +Input: [1,2,3,4,5] +Output: Node 3 from this list (Serialization: [3,4,5]) +The returned node has value 3. (The judge's serialization of this node is [3,4,5]). +Note that we returned a ListNode object ans, such that: +ans.val = 3, ans.next.val = 4, ans.next.next.val = 5, and ans.next.next.next = NULL. +Example 2: + +Input: [1,2,3,4,5,6] +Output: Node 4 from this list (Serialization: [4,5,6]) +Since the list has two middle nodes with values 3 and 4, we return the second one. +*/ + + +/* +LintCode +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/Java/88. Merge Sorted Array.java b/Java/88. Merge Sorted Array.java new file mode 100755 index 0000000..21410cf --- /dev/null +++ b/Java/88. Merge Sorted Array.java @@ -0,0 +1,103 @@ +E +tags: Array, Two Pointers +time: O(n) +space: O(1) + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素: 从尾部,是大数字优先排末尾的. +- Deal with remaining: + - When A values are used up, put remian of B into it + - When B values are finished, there is nothing todo. The remain of A is already in place. + +``` +/* +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. + +Example: + +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 + +*/ + + +/* +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 { + public void merge(int[] A, int m, int[] B, int n) { + // write your code here + int index = m + n - 1; + while (m > 0 && n > 0) { + if (A[m - 1] > B[n - 1]) { + A[index--] = A[--m]; + } else { + A[index--] = B[--n]; + } + }//While + + // Put remain of B + while (n > 0) { + A[index--] = B[--n]; + } + } +} + +// Some variations of the same concept: + +// similar, two pointer moving, with using combined end `int index = m + n - 1;` +class Solution { + public void merge(int[] nums1, int m, int[] nums2, int n) { + 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; + } + } + } +} + + +// two pointer, with end index, and for loop +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--]; + } + } + } +} + + +``` diff --git a/Java/88. Search in Rotated Sorted Array II.java b/Java/88. Search in Rotated Sorted Array II.java new file mode 100755 index 0000000..52155c0 --- /dev/null +++ b/Java/88. Search in Rotated Sorted Array II.java @@ -0,0 +1,58 @@ +M +tags: Array, Binary Search +time: O(logn), worst O(n) +space: O(1) + +#### Binary Search +- Also most identical to `33. Search in Rotated Sorted Array`: + - find where nums[mid] lands by comparing to nums[start]. i.e., if nums[mid] < nums[start], on right half of the array + - when `nums[mid] == nums[start]`: duplicate. Shift by start++ +- the worst case of `nums[mid] == nums[start]` willl cause O(n), +- but if duplicate is not entire array, should be O(logn) + +``` +/* +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? + + +*/ + + +class Solution { + public boolean search(int[] nums, int target) { + if (nums == null || nums.length == 0) return false; + int start = 0, end = nums.length - 1; + while (start + 1 < end) { + int mid = start + (end - start) / 2; + if (nums[mid] == target) return true; + if (nums[start] < nums[mid]) { //Land in 1st continous section + if (nums[start] <= target && target <= nums[mid]) end = mid; + else start = mid; + } else if (nums[start] > nums[mid]) { //Land in 2nd continous section + if (nums[mid] <= target && target <= nums[end]) start = mid; + else end = mid; + } else start++; // nums[start] == nums[mid], duplicate val, shift it to end + } + return nums[start] == target || nums[end] == target; + } +} + +``` \ No newline at end of file diff --git a/Java/893. Groups of Special-Equivalent Strings.java b/Java/893. Groups of Special-Equivalent Strings.java new file mode 100755 index 0000000..7b1295e --- /dev/null +++ b/Java/893. Groups of Special-Equivalent Strings.java @@ -0,0 +1,82 @@ +E +1557809555 +tags:String, Basic Implementation + +Mark # of characters can be useful to print string signature + +``` +/* +You are given an array A of strings. + +Two strings S and T are special-equivalent if after any number of moves, S == T. + +A move consists of choosing two indices i and j with i % 2 == j % 2, and swapping S[i] with S[j]. + +Now, a group of special-equivalent strings from A is a non-empty subset S of A such that any string not in S is not special-equivalent with any string in S. + +Return the number of groups of special-equivalent strings from A. + + + +Example 1: + +Input: ["a","b","c","a","c","c"] +Output: 3 +Explanation: 3 groups ["a","a"], ["b"], ["c","c","c"] +Example 2: + +Input: ["aa","bb","ab","ba"] +Output: 4 +Explanation: 4 groups ["aa"], ["bb"], ["ab"], ["ba"] +Example 3: + +Input: ["abc","acb","bac","bca","cab","cba"] +Output: 3 +Explanation: 3 groups ["abc","cba"], ["acb","bca"], ["bac","cab"] +Example 4: + +Input: ["abcd","cdab","adcb","cbad"] +Output: 1 +Explanation: 1 group ["abcd","cdab","adcb","cbad"] + + +Note: + +1 <= A.length <= 1000 +1 <= A[i].length <= 20 +All A[i] have the same length. +All A[i] consist of only lowercase letters. + */ + +/* +brutle: n^2: compare each one against all + +`i % 2 == j % 2` means on the odd or on even indexes, anagram should be the same + +*/ +class Solution { + public int numSpecialEquivGroups(String[] A) { + if (A == null || A.length == 0) { + return 0; + } + HashSet set = new HashSet<>(); + + for (String s : A) { + int[] odd = new int[256]; + int[] even = new int[256]; + for (int i = 0; i < s.length(); i++) { + int c = s.charAt(i); + if (i % 2 == 0) { + even[c]++; + } else { + odd[c]++; + } + } + String signature = Arrays.toString(odd) + Arrays.toString(even); + set.add(signature); + } + + return set.size(); + } +} +``` diff --git a/Java/896. Monotonic Array.java b/Java/896. Monotonic Array.java new file mode 100755 index 0000000..7dfd768 --- /dev/null +++ b/Java/896. Monotonic Array.java @@ -0,0 +1,65 @@ +E +tags: Array + +basic implementation + +``` + +/** +An array is monotonic if it is either monotone increasing or monotone decreasing. + +An array A is monotone increasing if for all i <= j, A[i] <= A[j]. An array A is monotone decreasing if for all i <= j, A[i] >= A[j]. + +Return true if and only if the given array A is monotonic. + + + +Example 1: + +Input: [1,2,2,3] +Output: true +Example 2: + +Input: [6,5,4,4] +Output: true +Example 3: + +Input: [1,3,2] +Output: false +Example 4: + +Input: [1,2,4,5] +Output: true +Example 5: + +Input: [1,1,1] +Output: true + + +Note: + +1 <= A.length <= 50000 +-100000 <= A[i] <= 100000 + */ + + +class Solution { + public boolean isMonotonic(int[] A) { + return verify(A, true) || verify(A, false); + } + + private boolean validate(int a, int b, boolean incline) { + if (incline) return a <= b; + return a >= b; + } + + private boolean verify(int[] A, boolean incline) { + for (int i = 0; i < A.length - 1; i++) { + if (!validate(A[i], A[i + 1], incline)) { + return false; + } + } + return true; + } +} +``` \ No newline at end of file diff --git a/Java/9. Palindrome Number.java b/Java/9. Palindrome Number.java new file mode 100755 index 0000000..c244c89 --- /dev/null +++ b/Java/9. Palindrome Number.java @@ -0,0 +1,52 @@ +E +tags: Math + +#### Reverse half of the number +- build reversed integer 直到midpoint, where x <= reverse +- 如果input双数: x == reverse +- 如果input单数 (而且x>reverse): x == reverse/10 + +#### Consider palindrome +- optionA: compare digit by digit +- optionB: reverse half of the string/int, and compare with other half. + + + + +``` + +/* +Determine whether an integer is a palindrome. An integer is a palindrome when it reads the same backward as forward. + +Example 1: + +Input: 121 +Output: true +Example 2: + +Input: -121 +Output: false +Explanation: From left to right, it reads -121. From right to left, it becomes 121-. Therefore it is not a palindrome. +Example 3: + +Input: 10 +Output: false +Explanation: Reads 01 from right to left. Therefore it is not a palindrome. +Follow up: + +Coud you solve it without converting the integer to a string? + */ + +// Build reverse until mid point +class Solution { + public boolean isPalindrome(int x) { + if (x < 0 || (x != 0 && x % 10 == 0)) return false; + int reverse = 0; + while (x > reverse){ + reverse = reverse * 10 + x % 10; + x = x/10; + } + return (x == reverse || x == reverse / 10); + } +} +``` \ No newline at end of file diff --git a/Java/91. Decode Ways.java b/Java/91. Decode Ways.java new file mode 100755 index 0000000..7f7e810 --- /dev/null +++ b/Java/91. Decode Ways.java @@ -0,0 +1,159 @@ +M +tags: String, DP, Partition DP +time: O(n) +space: O(n) + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Method1: DP, Bottom-Up by calculating base case first +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- there can be 2 final states at dp[i]: single digit or double digit. + - validate if `single digit`, dp[i] += dp[i - 1]. Last 1 digit can be decoded. + - validate if `double digit`, dp[i] += dp[i - 2]. Last 2 digits can be decoded. +- note: + - get number from char: `c - '0'` + - validatae single digit != '0', 因为'0' 不在条件之中(A-Z) +- Option1: dp[i] as # ways to decode at index i, including letter i + - The validation is done on: 1) single digit i, or 2) double digit [i-1, i] +- Option2: Partition DP, dp[i] as # ways to decode for first i letters (excluding letter i) + - 定义`dp[i] = 前i个digits最多有多少种decode的方法`: new dp[n + 1]. + - dp[i] += dp[i - x], where x = 1, 2 + - The validation is done on: 1) single digit [i-1], or 2) double digit [i-2, i-1] + - Option2 is better in terms of clean coding. We assume `dp[0]=1` as 1 way to decode 0 digits. + - No need to specially handle length == 1, because it is covered later + - No need to manualy init the first 2-digit case as in Option1 + - Return of `dp[n]` is clean +- 引申: 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +#### Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 + - especially when i = n - 2, handle 2-digt edge case carefully: + - 1) check if two-digit range [i, i+1] is valid + - 2) check if single-digit [i] is valid; if so, += dfs(s, i + 1) +- memo[i]: # ways to decode from [i, n). init with `memo[i]=-1` + +``` +/* +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. +*/ + + + + +/* +Method1, Option1: +Treating dp[i] as # ways to decode at index i, including letter i +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, including letter 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] +*/ +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]; + dp[0] = s.charAt(0) != '0' ? 1 : 0; + dp[1] = s.charAt(1) != '0' ? dp[0] : 0; + dp[1] += check(s, 1) ? 1 : 0; + + for (int i = 2; i < n; i++) { + if (s.charAt(i) != '0') dp[i] = dp[i - 1]; // possible to decode with just letter at i + if (check(s, i)) dp[i] += dp[i - 2]; // possible to decode with (i-1, i) + } + return dp[n - 1]; + } + + private boolean check(String s, int i) { + int twoDigit = (s.charAt(i - 1) - '0') * 10 + (s.charAt(i) - '0'); + return twoDigit <= 26 && twoDigit >= 10; + } +} + +/* +Method1, Option2: +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(); + 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++) { + if (s.charAt(i - 1) != '0') dp[i] += dp[i - 1]; + if (check(s, i - 1)) dp[i] += dp[i - 2]; + } + return dp[n]; + } + + private boolean check(String s, int i) { + int twoDigit = (s.charAt(i - 1) - '0') * 10 + (s.charAt(i) - '0'); + return twoDigit <= 26 && twoDigit >= 10; + } +} + + +/* +Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 +- memo[i]: # ways to decode from [i, n) + +*/ +class Solution { + int[] memo; + + public int numDecodings(String s) { + if (s == null) return 0; + memo = new int[s.length()]; + for (int i = 0; i < s.length(); i++) memo[i] = -1; + return dfs(s, 0); + } + + private int dfs(String s, int i) { + if (i >= s.length()) return 0; + if (memo[i] != -1) return memo[i]; + + if (i == s.length() - 1) return (s.charAt(i) != '0') ? 1 : 0; + if (i == s.length() - 2) { + int sum = check(s, i) ? 1 : 0; + if (s.charAt(i) != '0') sum += dfs(s, i + 1); + return sum; + } + + int sum = (s.charAt(i) != '0') ? dfs(s, i + 1) : 0; + sum += check(s, i) ? dfs(s, i + 2) : 0; + + memo[i] = sum; + return memo[i]; + } + + private boolean check(String s, int i) { + if (i + 1 >= s.length()) return false; + int twoDigit = (s.charAt(i) - '0') * 10 + (s.charAt(i + 1) - '0'); + return twoDigit <= 26 && twoDigit >= 10; + } +} +``` \ No newline at end of file diff --git a/Java/921. Minimum Add to Make Parentheses Valid.java b/Java/921. Minimum Add to Make Parentheses Valid.java new file mode 100644 index 0000000..95d10e9 --- /dev/null +++ b/Java/921. Minimum Add to Make Parentheses Valid.java @@ -0,0 +1,83 @@ +M +tags: +time: O(n) +space: O(1) + + +#### Method1: Stack +- use stack to verify the input/output of '(' and ')' +- time, space: O(n) + +#### Method1: Simpilfy stack with open parentheses count +- time:(n), space: O(1) + +``` +/* +Given a string S of '(' and ')' parentheses, we add the minimum number of parentheses ( '(' or ')', and in any positions ) so that the resulting parentheses string is valid. + +Formally, a parentheses string is valid if and only if: + +It is the empty string, or +It can be written as AB (A concatenated with B), where A and B are valid strings, or +It can be written as (A), where A is a valid string. +Given a parentheses string, return the minimum number of parentheses we must add to make the resulting string valid. + + + +Example 1: + +Input: "())" +Output: 1 +Example 2: + +Input: "(((" +Output: 3 +Example 3: + +Input: "()" +Output: 0 +Example 4: + +Input: "()))((" +Output: 4 + + +Note: + +S.length <= 1000 +S only consists of '(' and ')' characters. +*/ + +// Method1: stack +class Solution { + public int minAddToMakeValid(String s) { + if (s == null || s.length() == 0) { + return 0; + } + Stack stack = new Stack<>(); + int count = 0; + for (char c : s.toCharArray()) { + if (c == '(') stack.push(c); + else if (stack.isEmpty()) count++; + else stack.pop(); + } + return count + stack.size(); + } +} + +// Method: simplify with openCount: open parentheses count +class Solution { + public int minAddToMakeValid(String s) { + if (s == null || s.length() == 0) { + return 0; + } + int count = 0, openCount = 0; + for (char c : s.toCharArray()) { + if (c == '(') openCount++; + else if (openCount == 0) count++; + else openCount--; + } + return count + openCount; + } +} +``` \ No newline at end of file diff --git a/Java/938. Range Sum of BST.java b/Java/938. Range Sum of BST.java new file mode 100755 index 0000000..95113c8 --- /dev/null +++ b/Java/938. Range Sum of BST.java @@ -0,0 +1,95 @@ +E +tags: Tree, Recursion, BST + +#### DFS based on BST rules +- sum up the matching L & R + - Find (L,R) on left child + - Find (L,R) on right child + - Find (L,R) covering the root node +- space O(n), worst case O(logn), height of dfs. +- time O(n) to find all nodes between (L, R) + +#### Iterative +- Using stack, or queue, list: any data structure (we are not doing ordered search) +- space O(n) +- time O(n) + +``` +/* +Given the root node of a binary search tree, return the sum of values of all nodes with value between L and R (inclusive). + +The binary search tree is guaranteed to have unique values. + + + +Example 1: + +Input: root = [10,5,15,3,7,null,18], L = 7, R = 15 +Output: 32 +Example 2: + +Input: root = [10,5,15,3,7,13,18,1,null,6], L = 6, R = 10 +Output: 23 + + +Note: + +The number of nodes in the tree is at most 10000. +The final answer is guaranteed to be less than 2^31. +*/ + + +/** + * Definition for a binary tree node. + * public class TreeNode { + * int val; + * TreeNode left; + * TreeNode right; + * TreeNode(int x) { val = x; } + * } + */ + +// Recursive +class Solution { + public int rangeSumBST(TreeNode root, int L, int R) { + if (root == null) return 0; + if (root.val < L) return rangeSumBST(root.right, L, R); + if (root.val > R) return rangeSumBST(root.left, L, R); + return root.val + rangeSumBST(root.right, L, R) + rangeSumBST(root.left, L, R); + } + +} + +// Iterative: stack +class Solution { + public int rangeSumBST(TreeNode root, int L, int R) { + Stack stack = new Stack<>(); + stack.push(root); + int sum = 0; + while (!stack.isEmpty()) { + TreeNode n = stack.pop(); + if (n == null) continue; + if (n.val < R) stack.push(n.right); // search into right child + if (n.val > L) stack.push(n.left); // search into left child + if (L <= n.val && n.val <= R) sum += n.val; + } + return sum; + } +} +// Iterative: queue +class Solution { + public int rangeSumBST(TreeNode root, int L, int R) { + Queue list = new LinkedList<>(); + list.offer(root); + int sum = 0; + while (!list.isEmpty()) { + TreeNode n = list.poll(); + if (n == null) continue; + if (n.val < R) list.offer(n.right); // search into right child + if (n.val > L) list.offer(n.left); // search into left child + if (L <= n.val && n.val <= R) sum += n.val; + } + return sum; + } +} +``` \ No newline at end of file diff --git a/Java/94. Binary Tree Inorder Traversal.java b/Java/94. Binary Tree Inorder Traversal.java new file mode 100755 index 0000000..c6ff2ec --- /dev/null +++ b/Java/94. Binary Tree Inorder Traversal.java @@ -0,0 +1,110 @@ +E +tags: Hash Table, Stack, Tree +time: O(n) +space: O(logn) + +Inorder traverse Binary Tree + +#### Method1: DFS +- option1: dfs + rst list to carry results +- option2: Divide and Conquer, 在自己的基础上recursive, 不用helper function +- O(n) time + +#### Method2: Iterative, Stack inorder traversal +- 1) Add root.leftPath all the way to leaf, 2) process curr 3) Move to right if applicable 4) add all right.leftPath +- O(n) time, O(h) space + + +``` +/* +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 + +*/ +// Method1 option1: DFS with helper +public class Solution { + public List inorderTraversal(TreeNode root) { + List rst = new ArrayList(); + dfs(rst, root); + return rst; + } + + public void dfs(List rst, TreeNode node) { + if (node == null) return; + dfs(rst, node.left); + rst.add(node.val); + dfs(rst, node.right); + } +} + +/* +// Method1 Option2: DFS w/o helper +Recursive, append left, itself, then right +*/ +class Solution { + public List inorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + if (root == null) return rst; + List left = inorderTraversal(root.left); + List right = inorderTraversal(root.right); + + rst.addAll(left); + rst.add(root.val); + rst.addAll(right); + return rst; + } +} + + +/* +Method2: 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 node.right's left children to stack +*/ +class Solution { + public List inorderTraversal(TreeNode root) { + List rst = new ArrayList<>(); + Stack stack = new Stack<>(); + TreeNode node = root; + addLeftPath(stack, node); // Dive deep to left path till leaf + + while (!stack.isEmpty()) { + node = stack.pop(); // Add to rst + rst.add(node.val); + // Add node.right and all its left children + if (node.right != null) { + node = node.right; + addLeftPath(stack, node); + } + } + return rst; + } + + private void addLeftPath(Stack stack, TreeNode node) { + while (node != null) { + stack.push(node); + node = node.left; + } + } +} + + +``` \ No newline at end of file diff --git a/Java/953. Verifying an Alien Dictionary.java b/Java/953. Verifying an Alien Dictionary.java new file mode 100755 index 0000000..bf4c880 --- /dev/null +++ b/Java/953. Verifying an Alien Dictionary.java @@ -0,0 +1,100 @@ +E +tags: Hash Table +time: O(nm) +space: O(1) + +#### Hash Table +- mark the char position +- check adjacent words +- Optimization + - a) If s1 equals s2, just return true, no need to continue. + - b) if s2 (app) is a substring of s1(apple), just return false. + + +``` +/* +In an alien language, surprisingly they also use english lowercase letters, but possibly in a different order. The order of the alphabet is some permutation of lowercase letters. + +Given a sequence of words written in the alien language, and the order of the alphabet, return true if and only if the given words are sorted lexicographicaly in this alien language. + + + +Example 1: + +Input: words = ["hello","leetcode"], order = "hlabcdefgijkmnopqrstuvwxyz" +Output: true +Explanation: As 'h' comes before 'l' in this language, then the sequence is sorted. +Example 2: + +Input: words = ["word","world","row"], order = "worldabcefghijkmnpqstuvxyz" +Output: false +Explanation: As 'd' comes after 'l' in this language, then words[0] > words[1], hence the sequence is unsorted. +Example 3: + +Input: words = ["apple","app"], order = "abcdefghijklmnopqrstuvwxyz" +Output: false +Explanation: The first three characters "app" match, and the second string is shorter (in size.) According to lexicographical rules "apple" > "app", because 'l' > '∅', where '∅' is defined as the blank character which is less than any other character (More info). + + +Note: + +1 <= words.length <= 100 +1 <= words[i].length <= 20 +order.length == 26 +All characters in words[i] and order are english lowercase letters. + +*/ +// O(MN). M = words size, N = individual words size +class Solution { + public boolean isAlienSorted(String[] words, String order) { + if (words == null || order == null) return false; + + int[] orderIndex = new int[256]; + for(int i = 0; i < order.length(); i++) { + orderIndex[order.charAt(i)] = i; // preserve order # for each char. + } + + for (int i = 0; i < words.length - 1; i++) { + if (!compareStr(words[i], words[i+1], orderIndex)) return false; + } + return true; + } + + private boolean compareStr(String s1, String s2, int[] orderIndex) { + if (s1.indexOf(s2) >= 0) return false; + + int size = Math.min(s1.length(), s2.length()); + for (int i = 0; i < size; i++) { + int c1 = s1.charAt(i), c2 = s2.charAt(i); + if (orderIndex[c1] == orderIndex[c2]) continue; + return orderIndex[c1] < orderIndex[c2]; + } + return true; + } +} + +/** +Leetcode discussion solution https://leetcode.com/problems/verifying-an-alien-dictionary/discuss/203185/JavaC%2B%2BPython-Mapping-to-Normal-Order + +Uses integer to compare. If s1.char[0] - s2.char[0] > 0, retrun false +It's same idea as my solution. +I think mine is easier to read. +*/ +class Solution { + public boolean isAlienSorted(String[] words, String order) { + // ... same + + for (int i = 0; i < words.length - 1; i++) { + if (compareStr(words[i], words[i+1], orderIndex) > 0) return false; + } + return true; + } + + private int compareStr(String s1, String s2, int[] orderIndex) { + // ... + // return corresponding s1/s2 index diff + } +} + + +``` \ No newline at end of file diff --git a/Java/973. K Closest Points to Origin.java b/Java/973. K Closest Points to Origin.java new file mode 100755 index 0000000..207bbe9 --- /dev/null +++ b/Java/973. K Closest Points to Origin.java @@ -0,0 +1,81 @@ +M +tags: Heap, Sort, Divide and Conquer +time: O(klogk) +space: O(k) + +#### PriorityQueue +- Create customized Point{} class +- Sort by distance +- Maintain queue size <= K + +#### Divide and Conquer +- ?, select sort? + +``` +/* +We have a list of points on the plane. Find the K closest points to the origin (0, 0). + +(Here, the distance between two points on a plane is the Euclidean distance.) + +You may return the answer in any order. The answer is guaranteed to be unique (except for the order that it is in.) + + + +Example 1: + +Input: points = [[1,3],[-2,2]], K = 1 +Output: [[-2,2]] +Explanation: +The distance between (1, 3) and the origin is sqrt(10). +The distance between (-2, 2) and the origin is sqrt(8). +Since sqrt(8) < sqrt(10), (-2, 2) is closer to the origin. +We only want the closest K = 1 points from the origin, so the answer is just [[-2,2]]. +Example 2: + +Input: points = [[3,3],[5,-1],[-2,4]], K = 2 +Output: [[3,3],[-2,4]] +(The answer [[-2,4],[3,3]] would also be accepted.) + + +Note: + +1 <= K <= points.length <= 10000 +-10000 < points[i][0] < 10000 +-10000 < points[i][1] < 10000 +*/ + + +class Solution { + class Point { + int x, y, distance; + public Point(int x, int y) { + this.x = x; + this.y = y; + this.distance = x * x + y * y; + } + } + + public int[][] kClosest(int[][] points, int K) { + if (points == null || K < 0 ) return new int[][]{}; + + PriorityQueue queue = new PriorityQueue<>( + Comparator.comparing((Point p) -> p.distance).reversed()); + + for (int[] point : points) { + queue.offer(new Point(point[0], point[1])); + if (queue.size() > K) queue.poll(); + } + return buildResult(queue); + } + + private int[][] buildResult(PriorityQueue queue) { + int[][] result = new int[queue.size()][2]; + while(!queue.isEmpty()) { + Point p = queue.poll(); + result[queue.size()] = new int[]{p.x, p.y}; + } + return result; + } +} + +``` \ No newline at end of file diff --git a/Java/977. Squares of a Sorted Array.java b/Java/977. Squares of a Sorted Array.java new file mode 100755 index 0000000..9430deb --- /dev/null +++ b/Java/977. Squares of a Sorted Array.java @@ -0,0 +1,60 @@ +E +tags: Array, Two Pointers +time: O(n) +space: O(n) + +#### Two Pointers +- negative index i, positive index j + +``` + +/* +Given an array of integers A sorted in non-decreasing order, return an array of the squares of each number, also in sorted non-decreasing order. + +Example 1: + +Input: [-4,-1,0,3,10] +Output: [0,1,9,16,100] +Example 2: + +Input: [-7,-3,2,3,11] +Output: [4,9,9,49,121] + + +Note: + +1 <= A.length <= 10000 +-10000 <= A[i] <= 10000 +A is sorted in non-decreasing order. +*/ + +class Solution { + public int[] sortedSquares(int[] A) { + if (A == null || A.length == 0) return A; + int n = A.length; + int i = findPos(A), j = i + 1; + int index = 0; + int[] result = new int[n]; + + while (i >= 0 || j < n) { + int squareI = i < 0 ? Integer.MAX_VALUE : A[i] * A[i]; + int squareJ = j >= n ? Integer.MAX_VALUE : A[j] * A[j]; + if (squareI < squareJ) { + result[index++] = squareI; + i--; + } else { + result[index++] = squareJ; + j++; + } + } + return result; + } + + public int findPos(int[] A) { + for (int i = 0; i < A.length; i++) { + if (A[i] >= 0) return i - 1; + } + return 0; + } +} +``` diff --git a/Java/98. Validate Binary Search Tree.java b/Java/98. Validate Binary Search Tree.java new file mode 100755 index 0000000..cd3b144 --- /dev/null +++ b/Java/98. Validate Binary Search Tree.java @@ -0,0 +1,68 @@ +M +tags: Tree, DFS, BST, Divide and Conquer +time: O(n) +space: O(logn) + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + +``` + +/* +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. +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 +Divide and Conquer Recursion 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; } + * } + */ +/* +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); + } + + private boolean dfs(TreeNode node, long min, long max) { + if (node == null) return true; + if (node.val <= min || node.val >= max) return false; + return dfs(node.left, min, node.val) && dfs(node.right, node.val, max); + } +} + +``` \ No newline at end of file diff --git a/Java/981. Time Based Key-Value Store.java b/Java/981. Time Based Key-Value Store.java new file mode 100755 index 0000000..e049667 --- /dev/null +++ b/Java/981. Time Based Key-Value Store.java @@ -0,0 +1,150 @@ +M +tags: Binary Search, Hash Table, TreeMap +time: set O(1), get(logn) +space: O(n) + +#### Method1: Binary Search +- use hash to store +- binary serach on list of values + +#### Method2: TreeMap +- use hash to store > +- treemap.floorKey(timestamp) finds the top item below certain timestamp + +``` +/* +Create a timebased key-value store class TimeMap, that supports two operations. + +1. set(string key, string value, int timestamp) + +Stores the key and value, along with the given timestamp. +2. get(string key, int timestamp) + +Returns a value such that set(key, value, timestamp_prev) was called previously, with timestamp_prev <= timestamp. +If there are multiple such values, it returns the one with the largest timestamp_prev. +If there are no values, it returns the empty string (""). + + +Example 1: + +Input: inputs = ["TimeMap","set","get","get","set","get","get"], inputs = [[],["foo","bar",1],["foo",1],["foo",3],["foo","bar2",4],["foo",4],["foo",5]] +Output: [null,null,"bar","bar",null,"bar2","bar2"] +Explanation: +TimeMap kv; +kv.set("foo", "bar", 1); // store the key "foo" and value "bar" along with timestamp = 1 +kv.get("foo", 1); // output "bar" +kv.get("foo", 3); // output "bar" since there is no value corresponding to foo at timestamp 3 and timestamp 2, then the only value is at timestamp 1 ie "bar" +kv.set("foo", "bar2", 4); +kv.get("foo", 4); // output "bar2" +kv.get("foo", 5); //output "bar2" + +Example 2: + +Input: inputs = ["TimeMap","set","set","get","get","get","get","get"], inputs = [[],["love","high",10],["love","low",20],["love",5],["love",10],["love",15],["love",20],["love",25]] +Output: [null,null,null,"","high","high","low","low"] + + +Note: + +All key/value strings are lowercase. +All key/value strings have length in the range [1, 100] +The timestamps for all TimeMap.set operations are strictly increasing. +1 <= timestamp <= 10^7 +TimeMap.set and TimeMap.get functions will be called a total of 120000 times (combined) per test case. +*/ + +/* +- the same key maps to multiple values, marked by timestamp. + - input timestamp always increasing: we have a increasing list + - query: O(logn) binary search to find target timstamp +- record {value; timestamp} +- sort collection by timestamp; +- find with binary search +- time: set O(1), get(logn) +*/ +class TimeMap { + class Record { + String value; + int timestamp; + public Record (String val, int time) { + this.value = val; + this.timestamp = time; + } + } + + Map> map; + /** Initialize your data structure here. */ + public TimeMap() { + map = new HashMap<>(); + } + + public void set(String key, String value, int timestamp) { + List list = map.getOrDefault(key, new ArrayList<>()); + list.add(new Record(value, timestamp)); + map.put(key, list); + } + + public String get(String key, int timestamp) { + if (!map.containsKey(key)) return ""; + List list = map.get(key); + return binarySearch(list, timestamp); + } + + + /** + // Optional, if not using : + int i = Collections.binarySearch(list, new Record("{", timestamp), + (a, b) -> Integer.compare(a.timestamp, b.timestamp)); + if (i >= 0) return list.get(i).value; + else if (i == -1) return ""; + else return list.get(-i-2).value; // item not exist i = (-(insertion point) - 1); want best available which is insertion point - 1 = - i - 1 - 1 = -i - 2 + */ + private String binarySearch(List list, int timestamp) { + int start = 0, end = list.size() - 1; + if (timestamp < list.get(start).timestamp) return ""; + if (timestamp > list.get(end).timestamp) return list.get(end).value; + while (start + 1 < end) { + int mid = start + (end - start) / 2; + if (list.get(mid).timestamp <= timestamp) { + if (mid + 1 > end) return list.get(mid).value; + if (mid + 1 <= end) { + if (timestamp < list.get(mid + 1).timestamp) return list.get(mid).value; + start = mid; + } + } else end = mid; + } + if (list.get(end).timestamp <= timestamp) return list.get(end).value; + return list.get(start).value; + } +} + + +// method2 tree map +class TimeMap { + Map> map; + /** Initialize your data structure here. */ + public TimeMap() { + map = new HashMap<>(); + } + + public void set(String key, String value, int timestamp) { + TreeMap treeMap = map.getOrDefault(key, new TreeMap<>()); + treeMap.put(timestamp, value); + map.put(key, treeMap); + } + + public String get(String key, int timestamp) { + if (!map.containsKey(key)) return ""; + TreeMap treeMap = map.get(key); + Integer t = treeMap.floorKey(timestamp); + return t != null ? treeMap.get(t) : ""; + } +} + +/** + * Your TimeMap object will be instantiated and called as such: + * TimeMap obj = new TimeMap(); + * obj.set(key,value,timestamp); + * String param_2 = obj.get(key,timestamp); + */ +``` \ No newline at end of file diff --git a/Java/986. Interval List Intersections.java b/Java/986. Interval List Intersections.java new file mode 100755 index 0000000..b1c58f9 --- /dev/null +++ b/Java/986. Interval List Intersections.java @@ -0,0 +1,175 @@ +M +tags: Two Pointers +time: O(n) +space: O(1) + + + +#### Method1: Merge Interval +- There can be 1 overlapping on any interval, calculate the inner intersection: lo(A[i][0], B[j][0]), hi(A[i][1], B[j][1]) + - if low <= hi, a valid intersection exist; add + - also, if A[i][1] < B[j][1]; that is A[i].end < B[j].end, then i++; otherwise j++ + - because the further-away `end` has been used, so move on. +- O(n) + +#### Method2: Sweep line +- code is much more complex (pq, Point, process code... etc) than method1 +- we can use point to track open/close, also want to specify if point belongs to A/B +- mark 2 global parameters: aOpen, bOpen. + - process when A/B close, record if (aOpen, bOpen) has overlaop + - clear up corresponding global parameter after A/B closes +- sort all pointers in priority queue by index +- Point: {boolean isOpen; int index} +- process the queue and remember to clean up all items on same index +- time: O(nlogn) +- space: O(n) + + +``` +/* +Given two lists of closed intervals, each list of intervals is pairwise disjoint and in sorted order. + +Return the intersection of these two interval lists. + +(Formally, a closed interval [a, b] (with a <= b) denotes the set of real numbers x with a <= x <= b. +The intersection of two closed intervals is a set of real numbers that is either empty, or can be represented as a closed interval. +For example, the intersection of [1, 3] and [2, 4] is [2, 3].) + + + +Example 1: + + + +Input: A = [[0,2],[5,10],[13,23],[24,25]], B = [[1,5],[8,12],[15,24],[25,26]] +Output: [[1,2],[5,5],[8,10],[15,23],[24,24],[25,25]] +Reminder: The inputs and the desired output are lists of Interval objects, and not arrays or lists. + + +Note: + +0 <= A.length < 1000 +0 <= B.length < 1000 +0 <= A[i].start, A[i].end, B[i].start, B[i].end < 10^9 +NOTE: input types have been changed on April 15, 2019. Please reset to default code definition to get new method signature. +*/ + +/* +#### Method1 Merge Interval +- There can be 1 overlapping on any interval: lo(start points) <= hi (end points) +- move up which ever interval.end is the left, because that must have been processed +- O(n) +*/ +class Solution { + public int[][] intervalIntersection(int[][] A, int[][] B) { + List list = new ArrayList<>(); + + int i = 0, j = 0; + int m = A.length, n = B.length; + while (i < m && j < n) { + int lo = Math.max(A[i][0], B[j][0]); + int hi = Math.min(A[i][1], B[j][1]); + if (lo <= hi) list.add(new int[]{lo, hi}); + + if (A[i][1] < B[j][1]) i++; + else j++; + } + + int[][] rst = new int[list.size()][2]; + for (int k = 0; k < list.size(); k++) { + rst[k] = list.get(k); + } + + return rst; + } + +} + + +/* +Method2: Sweep line +- we can use point to track open/close, also want to specify if point belongs to A/B +- we'll mark 2 global parameters: aOpen, bOpen. + - process when A/B close, record if (aOpen, bOpen) has overlaop + - clear up corresponding global parameter after A/B closes +- sort all pointers in priority queue by index +- Point: {boolean isOpen; int index} +- process the queue and remember to clean up all items on same index +- time: O(nlogn) +- space: O(n) +*/ +class Solution { + + class Point { + boolean isOpen; + int index; + char mark; + public Point(int index, boolean isOpen, char mark) { + this.index = index; + this.isOpen = isOpen; + this.mark = mark; + } + } + Integer openA = null, openB = null; + public int[][] intervalIntersection(int[][] A, int[][] B) { + List list = new ArrayList<>(); + + PriorityQueue queue = buildQueue(A, B); + + while (!queue.isEmpty()) { + List points = new ArrayList<>(); + Point p = queue.poll(); + points.add(p); + while (!queue.isEmpty() && queue.peek().index == p.index) { + points.add(queue.poll()); + } + int[] output = processPoints(points); + if (output != null) list.add(output); + } + + int[][] rst = new int[list.size()][2]; + for (int i = 0; i < list.size(); i++) { + rst[i] = list.get(i); + } + + return rst; + } + + public int[] processPoints(List points) { + Integer end = null, start = null; + int[] output = null; + + // process + for (Point p : points) { + int index = p.index; + if (p.mark == 'A' && p.isOpen) openA = index; + else if (p.mark == 'B' && p.isOpen) openB = index; + else if (!p.isOpen) end = index; + } + if (openA != null && openB != null && end != null) { + output = new int[]{Math.max(openA, openB), end}; + } + // clean up + for (Point p : points) { + if (p.mark == 'A' && !p.isOpen) openA = null; + if (p.mark == 'B' && !p.isOpen) openB = null; + } + return output; + } + + public PriorityQueue buildQueue(int[][] A, int[][] B) { + PriorityQueue queue = new PriorityQueue<>(Comparator.comparing(p -> p.index)); + populate(queue, A, 'A'); + populate(queue, B, 'B'); + return queue; + } + + private void populate(PriorityQueue queue, int[][] arr, char mark) { + for (int i = 0; i < arr.length; i++) { + queue.offer(new Point(arr[i][0], true, mark)); + queue.offer(new Point(arr[i][1], false, mark)); + } + } + +} +``` \ No newline at end of file diff --git a/Java/987. Vertical Order Traversal of a Binary Tree.java b/Java/987. Vertical Order Traversal of a Binary Tree.java new file mode 100755 index 0000000..0b2d5c0 --- /dev/null +++ b/Java/987. Vertical Order Traversal of a Binary Tree.java @@ -0,0 +1,185 @@ +M +tags: DFS, BFS, Tree, Binary Tree, Hash Table +times: O(nlogn) +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + +``` +/* +Given a binary tree, return the vertical order traversal of its nodes values. + +For each node at position (X, Y), its left and right children respectively will be at positions (X-1, Y-1) and (X+1, Y-1). + +Running a vertical line from X = -infinity to X = +infinity, whenever the vertical line touches some nodes, we report the values of the nodes in order from top to bottom (decreasing Y coordinates). + +If two nodes have the same position, then the value of the node that is reported first is the value that is smaller. + +Return an list of non-empty reports in order of X coordinate. Every report will have a list of values of nodes. + + + +Example 1: + + + +Input: [3,9,20,null,null,15,7] +Output: [[9],[3,15],[20],[7]] +Explanation: +Without loss of generality, we can assume the root node is at position (0, 0): +Then, the node with value 9 occurs at position (-1, -1); +The nodes with values 3 and 15 occur at positions (0, 0) and (0, -2); +The node with value 20 occurs at position (1, -1); +The node with value 7 occurs at position (2, -2). +Example 2: + + + +Input: [1,2,3,4,5,6,7] +Output: [[4],[2],[1,5,6],[3],[7]] +Explanation: +The node with value 5 and the node with value 6 have the same position according to the given scheme. +However, in the report "[1,5,6]", the node value of 5 comes first since 5 is smaller than 6. + + +Note: + +The tree will have between 1 and 1000 nodes. +Each node's value will be between 0 and 1000. +*/ + +// Method1: DFS +class Solution { + Map>> map = new HashMap<>(); // map>> + int min = 0, max = 0; + + class Node { + int offset, level, val; + public Node (int offset, int level, int val) { + this.offset = offset; + this.level = level; + this.val = val; + } + } + public List> verticalTraversal(TreeNode root) { + dfs(root, 0, 0); + return flattenMap(); + } + + private void dfs(TreeNode node, int offset, int level) { + if (node == null) return; + + map.putIfAbsent(offset, new HashMap<>()); + map.get(offset).putIfAbsent(level, new ArrayList<>()); + map.get(offset).get(level).add(node.val); + + min = Math.min(min, offset); + max = Math.max(max, offset); + + dfs(node.left, offset - 1, level + 1); + dfs(node.right, offset + 1, level + 1); + } + + private List> flattenMap() { + List> rst = new ArrayList<>(); + for (int offset = min; offset <= max; offset++) { + Map> levelMap = map.get(offset); + List levels = new ArrayList<>(levelMap.keySet()); + List list = new ArrayList<>(); + Collections.sort(levels); + for (int level : levels) { + Collections.sort(levelMap.get(level)); + list.addAll(levelMap.get(level)); + } + rst.add(list); + } + return rst; + } +} + + +// Method2: BFS +/** + * 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<>(); // map>> + + class Node { + int offset; + TreeNode treeNode; + public Node (int offset, TreeNode treeNode) { + this.offset = offset; + this.treeNode = treeNode; + } + } + public List> verticalTraversal(TreeNode root) { + Queue queue = new LinkedList<>(); + queue.offer(new Node(0, root)); + + int min = 0, max = 0, level = 0; + while (!queue.isEmpty()) { + int size = queue.size(); + while (size-- > 0) { + Node node = queue.poll(); + int offset = node.offset; + populateMap(node, offset, level); + + if (node.treeNode.left != null) queue.offer(new Node(offset - 1, node.treeNode.left)); + if (node.treeNode.right != null) queue.offer(new Node(offset + 1, node.treeNode.right)); + + min = Math.min(min, offset); + max = Math.max(max, offset); + } + level++; + } + + return flattenMap(min, max); + } + + private void populateMap(Node node, int offset, int level) { + map.putIfAbsent(offset, new HashMap<>()); + map.get(offset).putIfAbsent(level, new ArrayList<>()); + map.get(offset).get(level).add(node.treeNode.val); + } + + private List> flattenMap(int min, int max) { + List> rst = new ArrayList<>(); + for (int offset = min; offset <= max; offset++) { + Map> levelMap = map.get(offset); + List levels = new ArrayList<>(levelMap.keySet()); + List list = new ArrayList<>(); + Collections.sort(levels); + for (int level : levels) { + Collections.sort(levelMap.get(level)); + list.addAll(levelMap.get(level)); + } + rst.add(list); + } + 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/Add Binary.java b/Java/Add Binary.java deleted file mode 100644 index e8d1f35..0000000 --- a/Java/Add Binary.java +++ /dev/null @@ -1,41 +0,0 @@ -/* -Given two binary strings, return their sum (also a binary string). - -Example -a = 11 - -b = 1 - -Return 100 - -Tags Expand -String Binary - - - -//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); - } -} diff --git a/Java/Add Digits.java b/Java/Add Digits.java new file mode 100755 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/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 100755 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 100755 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 100755 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 100755 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/Basic Calculator.java b/Java/Basic Calculator.java new file mode 100755 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 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 100755 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 100755 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/Binary Gap.java b/Java/Binary Gap.java new file mode 100755 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 deleted file mode 100644 index 0333f5a..0000000 --- a/Java/Binary Search Tree Iterator.java +++ /dev/null @@ -1,92 +0,0 @@ -理解binary search tree inorder traversal的规律。 -都是先找left.left.left ....left. 为top。 -然后再找parent,然后再right. - -这个题目里面找到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] - - 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 -*/ - -/* - 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: - * 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 - * } - */ -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; - } -} - - - - - - - - - - -``` \ 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 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 Longest Consecutive Sequence II.java b/Java/Binary Tree Longest Consecutive Sequence II.java new file mode 100755 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/Brick Wall.java b/Java/Brick Wall.java new file mode 100755 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 100755 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 100755 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/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/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/Coins in a Line II.java b/Java/Coins in a Line II.java new file mode 100755 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 100755 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 III.java b/Java/Combination Sum III.java new file mode 100755 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 100755 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/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 Version Numbers.java b/Java/Compare Version Numbers.java new file mode 100755 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 100755 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 100755 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 100755 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..41c2a2e --- 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,17 @@ +M +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 +33,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,12 +62,8 @@ 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) { + + public TreeNode constructFromPrePost(int[] inorder, int[] postorder) { if (inorder.length != postorder.length) { return null; } @@ -85,3 +95,5 @@ public int findMid(int[] arr, int start, int end, int key) { } } + +``` \ No newline at end of file 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 III.java b/Java/Contains Duplicate III.java new file mode 100755 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/Contiguous Array.java b/Java/Contiguous Array.java new file mode 100755 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/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 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 100755 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 100755 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/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 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 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/Counting Bits.java b/Java/Counting Bits.java new file mode 100755 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/Cracking the Safe.java b/Java/Cracking the Safe.java new file mode 100755 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 100755 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/Design Search Autocomplete System.java b/Java/Design Search Autocomplete System.java new file mode 100755 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/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/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 100755 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 Reverse Polish Notation.java b/Java/Evaluate Reverse Polish Notation.java new file mode 100755 index 0000000..ce364b6 --- /dev/null +++ b/Java/Evaluate Reverse Polish Notation.java @@ -0,0 +1,88 @@ +M +tags: Stack +time: O(n) +space: O(n) + +给一个 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 + */ + +/* +- Each operator needs 2 nums to calc a result: + - put tokens (only number) into stack, such that the stack top is closer to the next potential operator + - when facign an operator, pick top 2 items from the Stack to use + - put calculate val back to stak +*/ +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)) stack.push(Long.parseLong(s)); + else { + long numB = stack.pop(), numA = stack.pop(); + stack.push(eval(numA, numB, s)); + } + } + return stack.pop().intValue(); + } + + private boolean isOperator(String s) { + if (s.length() != 1) return false; + char c = s.charAt(0); + return c == '+' || c == '-' || c == '/' || c == '*'; + } + + private long eval(long a, long b, String s) { + long rst = 0; + char operator = s.charAt(0); + if (operator == '+') rst = a + b; + if (operator == '-') rst = a - b; + if (operator == '*') rst = a * b; + if (operator == '/') rst = a / b; + return rst; + } +} + + +``` \ 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/Expression Add Operators.java b/Java/Expression Add Operators.java new file mode 100755 index 0000000..5572cdb --- /dev/null +++ b/Java/Expression Add Operators.java @@ -0,0 +1,186 @@ +H +tags: String, Divide and Conquer, DFS, Backtracking +time: O(4^n) +space: O(4^n) + +给一个数字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' +*/ + + +// Append string +class Solution { + String s; + int target; + public List addOperators(String num, int target) { + List rst = new ArrayList<>(); + if (num == null || num.length() == 0) return rst; + this.s = num; + this.target = target; + // dfs from index = 0, sum = 0, productFactor = 0, + dfs(rst, "", 0, 0, 0); + return rst; + } + + private void dfs(List rst, + String buffer, + int index, + long sum, + long productFactor) { + if (index >= s.length() && 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, curr, i + 1, currValue, currValue); + continue; + } + + dfs(rst, buffer + "+" + curr, i + 1, sum + currValue, currValue); + dfs(rst, buffer + "-" + curr, i + 1, sum - currValue, - currValue); + long product = productFactor * currValue; + dfs(rst, buffer + "*" + curr, i + 1, sum - productFactor + product, product); + } + } +} + +/* +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); + } +} + + + +``` \ 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/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 old mode 100644 new mode 100755 index 1629c93..f981ce5 --- 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 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 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/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/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/Flood Fill.java b/Java/Flood Fill.java new file mode 100755 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 100755 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 100755 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/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 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 100755 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/Hamming Distance.java b/Java/Hamming Distance.java new file mode 100755 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/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..5c22f43 --- /dev/null +++ b/Java/HashWithCustomizedClass(LinkedList).java @@ -0,0 +1,175 @@ +M +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/Heaters.java b/Java/Heaters.java new file mode 100755 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/Implement Queue using Stacks.java b/Java/Implement Queue using Stacks.java new file mode 100755 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/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 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/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/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/Jump Game II.java b/Java/Jump Game II.java old mode 100644 new mode 100755 index 26720f0..dbc4cce --- a/Java/Jump Game II.java +++ b/Java/Jump Game II.java @@ -1,5 +1,37 @@ +H +tags: Array, Greedy, DP, Coordinate DP +time: O(n) +space: O(1) + +给一串数字 是可以跳的距离. goal: 跳到最后的index 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 +40,53 @@ 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 +*/ + +// Method1: Greedy: tracking farest we can go . 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, n = nums.length; + for (int i = 0; i < n - 1; i++) { + maxRange = Math.max(maxRange, i + nums[i]); + if (i == farest) { + count++; + farest = maxRange; + } + } + return count; + } +} + +// Method2: 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]; + dp[0] = 0; + for (int i = 1; i < n; i++) { + dp[i] = Integer.MAX_VALUE; + for (int j = 0; j < i; j++) { + if (j + nums[j] >= i) dp[i] = Math.min(dp[i], dp[j] + 1); + } + } + return dp[n - 1]; + } +} + + + + +//// Other Greedy impls, similar concepts +/* + 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 +94,63 @@ 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; + } +} +``` \ No newline at end of file diff --git a/Java/K Edit Distance.java b/Java/K Edit Distance.java new file mode 100755 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 100755 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 100755 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 100755 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/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/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 100755 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/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 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 100755 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 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 Univalue Path.java b/Java/Longest Univalue Path.java new file mode 100755 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 100755 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/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 100755 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 Sum of Rectangle No Larger Than K.java b/Java/Max Sum of Rectangle No Larger Than K.java new file mode 100755 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 100755 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/Maximum Average Subarray I.java b/Java/Maximum Average Subarray I.java new file mode 100755 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 100755 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 Size Subarray Sum Equals k.java b/Java/Maximum Size Subarray Sum Equals k.java new file mode 100755 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 Vacation Days.java b/Java/Maximum Vacation Days.java new file mode 100755 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 100755 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/Merge Two Binary Trees.java b/Java/Merge Two Binary Trees.java new file mode 100755 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/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 100755 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 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 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 100755 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/Missing Number.java b/Java/Missing Number.java new file mode 100755 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/My Calendar I.java b/Java/My Calendar I.java new file mode 100755 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/Next Closest Time.java b/Java/Next Closest Time.java new file mode 100755 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/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/Number Of Corner Rectangles.java b/Java/Number Of Corner Rectangles.java new file mode 100755 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 100755 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 100755 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 Longest Increasing Subsequence.java b/Java/Number of Longest Increasing Subsequence.java new file mode 100755 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 100755 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/Orderly Queue.java b/Java/Orderly Queue.java new file mode 100755 index 0000000..4e2f927 --- /dev/null +++ b/Java/Orderly Queue.java @@ -0,0 +1,59 @@ +H +1536248915 +tags: Math, String +``` + +/* +A string S of lowercase letters is given. Then, we may make any number of moves. + +In each move, we choose one of the first K letters (starting from the left), remove it, and place it at the end of the string. + +Return the lexicographically smallest string we could have after any number of moves. + + + +Example 1: + +Input: S = "cba", K = 1 +Output: "acb" +Explanation: +In the first move, we move the 1st character ("c") to the end, obtaining the string "bac". +In the second move, we move the 1st character ("b") to the end, obtaining the final result "acb". +Example 2: + +Input: S = "baaca", K = 3 +Output: "aaabc" +Explanation: +In the first move, we move the 1st character ("b") to the end, obtaining the string "aacab". +In the second move, we move the 3rd character ("c") to the end, obtaining the final result "aaabc". + + +Note: + +1 <= K <= S.length <= 1000 +S consists of lowercase letters only. +*/ + + +/* +Thoughts: +Figure out: +when k = 1, we can simply rotate and compare +when k > 1, we can just sort entire string +*/ +class Solution { + public String orderlyQueue(String S, int K) { + if (K > 1) { + char[] arr = S.toCharArray(); + Arrays.sort(arr); + return new String(arr); + } + String res = S; + for (int i = 1; i < S.length(); i++) { // move front section, one letter at a time + String tmp = S.substring(i) + S.substring(0, i); + if (res.compareTo(tmp) > 0) res = tmp; + } + return res; + } +} +``` \ 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/Palindrome Pairs.java b/Java/Palindrome Pairs.java new file mode 100755 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 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/Palindromic Substrings.java b/Java/Palindromic Substrings.java new file mode 100755 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/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 100755 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 100755 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/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 100755 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/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 100755 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 100755 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/Queue Reconstruction by Height.java b/Java/Queue Reconstruction by Height.java new file mode 100755 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..8ffd155 --- a/Java/QuickSort.java +++ b/Java/QuickSort.java @@ -1,49 +1,50 @@ -代码是不难的. +M +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 +61,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/Rearrange String k Distance Apart.java b/Java/Rearrange String k Distance Apart.java new file mode 100755 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 100755 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/Redundant Connection II.java b/Java/Redundant Connection II.java new file mode 100755 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 100755 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/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 100755 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 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 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 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/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 100755 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 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 deleted file mode 100644 index fde66e4..0000000 --- a/Java/Reverse Linked List.java +++ /dev/null @@ -1,60 +0,0 @@ -/* -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 -*/ - -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 || head.next == null) { - return head; - } - - ListNode dummy = new ListNode(0); - - 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; - } - - 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) { - return head; - } - ListNode reversedList = null; - while (head != null) { - ListNode cutOffPart = head.next; - head.next = reversedList; - reversedList = head; - head = cutOffPart; - } - return reversedList; - } -} \ No newline at end of file diff --git a/Java/Reverse String.java b/Java/Reverse String.java new file mode 100755 index 0000000..c4e7695 --- /dev/null +++ b/Java/Reverse String.java @@ -0,0 +1,35 @@ +E +tags: Two Pointers, String + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + +``` +/* +Write a function that reverses a string. The input string is given as an array of characters char[]. + +Do not allocate extra space for another array, you must do this by modifying the input array in-place with O(1) extra memory. + +You may assume all the characters consist of printable ascii characters. +*/ + +/* +Thoughts: +Obvious: new StringBuilder().reverse(). +Or, turn into charArray and reverse +*/ +class Solution { + public void reverseString(char[] s) { + if (s == null || s.length <= 1) { + return; + } + + int n = s.length; + for (int i = 0; i < n / 2; i++) { + char temp = s[i]; + s[i] = s[n - i - 1]; + s[n - i - 1] = temp; + } + } +} +``` \ 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/Robot Room Cleaner.java b/Java/Robot Room Cleaner.java new file mode 100755 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/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/Russian Doll Envelopes.java b/Java/Russian Doll Envelopes.java new file mode 100755 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/Scramble String.java b/Java/Scramble String.java new file mode 100755 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/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/Shortest Distance from All Buildings.java b/Java/Shortest Distance from All Buildings.java new file mode 100755 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 100755 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/Shuffle an Array.java b/Java/Shuffle an Array.java new file mode 100755 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/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/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 100755 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 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 100755 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/Spiral Matrix.java b/Java/Spiral Matrix.java new file mode 100755 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/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 100755 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 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/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 II.java b/Java/Subarray Sum II.java new file mode 100755 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/Submatrix Sum.java b/Java/Submatrix Sum.java new file mode 100755 index 0000000..f0fa75e --- /dev/null +++ b/Java/Submatrix Sum.java @@ -0,0 +1,81 @@ +M +1529974964 +tags: Array, PreSum, Hash Table + +给一个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` + +``` +/* +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/Subtree of Another Tree.java b/Java/Subtree of Another Tree.java new file mode 100755 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 100755 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/Target Sum.java b/Java/Target Sum.java new file mode 100755 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/The Maze II.java b/Java/The Maze II.java new file mode 100755 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 100755 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 100755 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 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 100755 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/Total Hamming Distance.java b/Java/Total Hamming Distance.java new file mode 100755 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/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 100755 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 100755 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/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 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 Parentheses.java b/Java/Valid Parentheses.java deleted file mode 100644 index ef8e128..0000000 --- a/Java/Valid Parentheses.java +++ /dev/null @@ -1,58 +0,0 @@ -剥皮形态。 -左边的外皮在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 -*/ - -/* - 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/Java/Walls and Gates.java b/Java/Walls and Gates.java new file mode 100755 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 100755 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 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..c5f3caf --- a/Java/Word Search.java +++ b/Java/Word Search.java @@ -1,70 +1,130 @@ +M +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. -Have you met this question in a real interview? Yes -Example -Given board = +Example: +board = [ - "ABCE", - "SFCS", - "ADEE" + ['A','B','C','E'], + ['S','F','C','S'], + ['A','D','E','E'] ] -word = "ABCCED", -> returns true, -word = "SEE", -> returns true, -word = "ABCB", -> returns false. -Tags Expand -Backtracking +Given word = "ABCCED", return true. +Given word = "SEE", return true. +Given word = "ABCB", return false. + +*/ + +/* +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 || 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 (dfs(board, word, i, j, 0)) return true; + } + } + return false; + } + + private boolean dfs(char[][] board, String s, int x, int y, int index) { + if (validate(board, x, y, s.charAt(index))) return false; + if (index == s.length() - 1) return true; + board[x][y] = '#'; + for (int i = 0; i < dx.length; i++) { + if (dfs(board, s, x + dx[i], y + dy[i], index + 1)) return true; + } + board[x][y] = s.charAt(index); + return false; + } + + private boolean validate(char[][] board, int x, int y, char c) { + return x < 0 || x >= board.length || y < 0 || y >= board[0].length + || board[x][y] != c || board[x][y] == '#'; + } + +} + +/* 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; } } + + + + +``` \ No newline at end of file diff --git a/Java/Word Squares.java b/Java/Word Squares.java new file mode 100755 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/[HackerRank]. Change to Anagram.java b/Java/[HackerRank]. Change to Anagram.java new file mode 100755 index 0000000..d3c43c2 --- /dev/null +++ b/Java/[HackerRank]. Change to Anagram.java @@ -0,0 +1,117 @@ +E +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/[lint]. 2 Sum II.java b/Java/[lint]. 2 Sum II.java new file mode 100755 index 0000000..07ce3f3 --- /dev/null +++ b/Java/[lint]. 2 Sum II.java @@ -0,0 +1,126 @@ +M +1516439472 +tags: Array, Two Pointers, Binary Search, Lint + +与 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/[lint]. 3 Sum Closest.java b/Java/[lint]. 3 Sum Closest.java new file mode 100755 index 0000000..57df528 --- /dev/null +++ b/Java/[lint]. 3 Sum Closest.java @@ -0,0 +1,65 @@ +M +1516610949 +tags: Array, Two Pointers, Lint + +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: +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(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 { + start++; + } + 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/[lint]. Anagrams.java b/Java/[lint]. Anagrams.java new file mode 100755 index 0000000..402340d --- /dev/null +++ b/Java/[lint]. Anagrams.java @@ -0,0 +1,71 @@ +M +tags: Array, Hash Table, Lint +time: O(n) +space: O(n) + +把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. + + +``` +/* +LintCode +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 + + +*/ +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; + } +} + +``` \ No newline at end of file diff --git a/Java/[lint]. Compare Strings.java b/Java/[lint]. Compare Strings.java new file mode 100755 index 0000000..2124565 --- /dev/null +++ b/Java/[lint]. Compare Strings.java @@ -0,0 +1,82 @@ +E +tags: String, Lint + +看StringA是不是包括所有 StringB的字符. Anagram + +#### 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. + +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 + +*/ + +/* +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 { + 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; + } +} + + +``` \ No newline at end of file diff --git a/Java/[lint]. HashHeap.java b/Java/[lint]. HashHeap.java new file mode 100755 index 0000000..6c37719 --- /dev/null +++ b/Java/[lint]. HashHeap.java @@ -0,0 +1,175 @@ +H +tags: HashHeap, Heap, Lint + +非题.是从九章找来的HashHeap implementation. + +#### HashHeap +- An efficient implementation of a priority queue. +- The linear hash function monotonically maps keys to buckets, and each bucket is a heap +- https://xlinux.nist.gov/dads/HTML/hashheap.html + +``` +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 表示最大堆 + 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/[lint]. Heapify.java b/Java/[lint]. Heapify.java new file mode 100755 index 0000000..4859640 --- /dev/null +++ b/Java/[lint]. Heapify.java @@ -0,0 +1,154 @@ +M +tags: Heap, MinHeap, HashHeap, Lint + +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]. + +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/hashheap/ +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, + child = left. + else + child = right +Check if curr.val < child.val + if so, break, we are good. + 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 { + public void heapify(int[] A) { + if (A == null || A.length == 0) { + return; + } + int child = 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]) { + child = leftId; + } else { + child = rightId; + } + if (A[currId] <= A[child]) { + break; + } else { + int temp = A[currId]; + A[currId] = A[child]; + A[child] = temp; + } + currId = child; + }//end while + + }//end for + } +} + + + + + +``` \ No newline at end of file diff --git a/Java/[lint]. Longest Words.java b/Java/[lint]. Longest Words.java new file mode 100755 index 0000000..063cfa9 --- /dev/null +++ b/Java/[lint]. Longest Words.java @@ -0,0 +1,74 @@ +E +tags: Hash Table, String, Lint + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + +``` +/* +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 + +*/ + +/* +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; + } + Map> map = new HashMap<>(); + int max = 0; + + 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(max); + } +} +``` \ No newline at end of file diff --git a/Java/[lint]. Lowest Common Ancestor II.java b/Java/[lint]. Lowest Common Ancestor II.java new file mode 100755 index 0000000..eed3c78 --- /dev/null +++ b/Java/[lint]. Lowest Common Ancestor II.java @@ -0,0 +1,144 @@ +E +tags: Tree, Hash Table, Lint + +给一个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! + + +``` +/* +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: + 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, 其实算是一个优化,更节省空间。 + 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 { + 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); + } +} + + + +``` \ No newline at end of file diff --git a/Java/[lint]. Merge k Sorted Arrays.java b/Java/[lint]. Merge k Sorted Arrays.java new file mode 100755 index 0000000..07e72df --- /dev/null +++ b/Java/[lint]. Merge k Sorted Arrays.java @@ -0,0 +1,142 @@ +M +tags: Heap, PriorityQueue, MinHeap +time: O(nlogk) +space: O(k) + +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. + +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 +*/ +//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} + 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/Java/[lint]. Nth to Last Node in List.java b/Java/[lint]. Nth to Last Node in List.java new file mode 100755 index 0000000..9395ece --- /dev/null +++ b/Java/[lint]. Nth to Last Node in List.java @@ -0,0 +1,65 @@ +E +tags: Linked List, Lint + +#### Linked List +- 先找到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/[lint]. Product of Array Exclude Itself.java b/Java/[lint]. Product of Array Exclude Itself.java new file mode 100755 index 0000000..fcd9032 --- /dev/null +++ b/Java/[lint]. Product of Array Exclude Itself.java @@ -0,0 +1,73 @@ +M +tags: Array, Lint + + +``` +/* +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. + +Example +For A = [1, 2, 3], return [6, 3, 2]. + +Tags Expand +Forward-Backward Traversal LintCode Copyright + +Thought: +Trivial way would be first calculate the zigma(A[0]* ... A[n-1]) then divide by B[i]. However, not allowed in this question. + +The other way: do for loop again and again? that will be n^2 time. + +*/ + + + +public class Solution { + /** + * @param A: Given an integers array A + * @return: A Long array B and B[i]= A[0] * ... * A[i-1] * A[i+1] * ... * A[n-1] + */ + public ArrayList 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; + } +} + + + +``` \ No newline at end of file diff --git a/Java/[lint]. Recover Rotated Sorted Array.java b/Java/[lint]. Recover Rotated Sorted Array.java new file mode 100755 index 0000000..09490b7 --- /dev/null +++ b/Java/[lint]. Recover Rotated Sorted Array.java @@ -0,0 +1,76 @@ +E +tags: Array, Lint + +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/Java/[lint]. Segment Tree Build II.java b/Java/[lint]. Segment Tree Build II.java new file mode 100755 index 0000000..df629a4 --- /dev/null +++ b/Java/[lint]. Segment Tree Build II.java @@ -0,0 +1,95 @@ +M +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, 自底向上建立起的。 + +``` +/* +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. + +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) + +*/ + +/* +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; + if (start == end) return new SegmentTreeNode(start, end, A[end]); + int mid = start + (end - start) / 2; + SegmentTreeNode left = buildHelper(start, mid, A); + SegmentTreeNode right = buildHelper(mid + 1, end, A); + + SegmentTreeNode node = new SegmentTreeNode(start, end, Math.max(left.max, right.max)); + node.left = left; + node.right = right; + return node; + } +} +``` \ No newline at end of file diff --git a/Java/[lint]. Segment Tree Build.java b/Java/[lint]. Segment Tree Build.java new file mode 100755 index 0000000..953cf95 --- /dev/null +++ b/Java/[lint]. Segment Tree Build.java @@ -0,0 +1,100 @@ +M +tags: Segment Tree, Binary Tree, Divide and Conquer, Lint + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- 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. + +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. + +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: + + [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: start value. + * @param end: end value. + * @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); + int mid = start + (end - start) / 2; + node.left = build(start, mid); // recursive on the rest of the range + node.right = build(mid + 1, 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/[lint]. Segment Tree Query.java b/Java/[lint]. Segment Tree Query.java new file mode 100755 index 0000000..33a04ff --- /dev/null +++ b/Java/[lint]. Segment Tree Query.java @@ -0,0 +1,86 @@ +M +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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, 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. + +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. +*/ +public class Solution { + /** + * @param root: The root of segment tree. + * @param start: start value. + * @param end: end value. + * @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); + } +} + +/** + * 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; + * } + * } + */ +``` \ No newline at end of file diff --git a/Java/[lint]. Subarray Sum.java b/Java/[lint]. Subarray Sum.java new file mode 100755 index 0000000..1b04533 --- /dev/null +++ b/Java/[lint]. Subarray Sum.java @@ -0,0 +1,66 @@ +E +tags: Array, Hash Table, PreSum, Subarray, Lint +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. + +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 +*/ +public class Solution { + public List subarraySum(int[] nums) { + List rst = new ArrayList<>(); + if (nums == null || nums.length == 0) { + return rst; + } + 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++) { + preSum += nums[i]; + if (map.containsKey(preSum)) { + rst.add(map.get(preSum) + 1); + rst.add(i); + return rst; + } + 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, a 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; + } +} + +/* + 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/[tool] Quick Select - Median.java b/Java/[tool] Quick Select - Median.java new file mode 100755 index 0000000..26ce7d1 --- /dev/null +++ b/Java/[tool] Quick Select - Median.java @@ -0,0 +1,99 @@ +E +tags: Quick Sort, Quick Select, Array, Two Pointers, Lint +time: O(n) +space: O(logN) + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + +``` +/* +https://www.lintcode.com/problem/median/description +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 + + +*/ + +/* +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 { + 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); + } + + /* + target = (n-1)/2, the median index + - end state: the returned partition pivot equals target + - verify `index < target` and continue quick select + */ + 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); + } + + /* + Partition the array and return the adjusted pivot index. + */ + private int partition(int[] nums, int low, int high) { + int pivotIndex = high; // end + int pivot = nums[pivotIndex]; + + while (low < high) { + while (low < high && nums[low] < pivot) low++; // stop when nums[low] >= pivot + while (low < high && nums[high] >= pivot) high--; // stop when nums[high] < pivot + swap(nums, low, high); + } + + swap(nums, low, pivotIndex); + + return low; + } + + 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/[tool]. Hash Function.java b/Java/[tool]. Hash Function.java new file mode 100755 index 0000000..ad2d5cc --- /dev/null +++ b/Java/[tool]. Hash Function.java @@ -0,0 +1,85 @@ +E +tags: Hash Table, Lint +time: O(1) get +space: O(n) store map + +In general, there is no universal recipe to stick to when it comes to implementing hashCode(). +https://www.baeldung.com/java-hashcode + +#### 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 +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* 33^3 + 98 * 33^2 + 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; + } +}; + + +``` \ No newline at end of file diff --git a/Java/[tool]. MergeSort.java b/Java/[tool]. MergeSort.java new file mode 100755 index 0000000..ec4164b --- /dev/null +++ b/Java/[tool]. MergeSort.java @@ -0,0 +1,101 @@ +M +tags: Sort, Merge Sort, Lint +time: O(mlogn) +space: O(n) + +#### 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); // copy remaining of left segment (in case it didn't reach end) + System.arraycopy(nums, j, temp, index, end - j + 1); // copy remaining of right segment (in case it didn't reach end) + System.arraycopy(temp, start, nums, start, end - start + 1); // copy whole temp[start,end] to original + } + + 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/[tool]. Topological Sorting.java b/Java/[tool]. Topological Sorting.java new file mode 100755 index 0000000..10660a4 --- /dev/null +++ b/Java/[tool]. Topological Sorting.java @@ -0,0 +1,158 @@ +M +tags: Topological Sort, BFS, DFS, Lint +time: O(V + E) +space: O(V + E) + +#### 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: + +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; + * List neighbors; + * DirectedGraphNode(int x) { label = x; neighbors = new List(); } + * }; + */ +public class Solution { + /** + * @param graph: A list of Directed graph node + * @return: Any topological order for the given graph. + */ + public List topSort(List graph) { + List rst = new List(); + if (graph == null || graph.size() == 0) { + return graph; + } + //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 (!inDegree.containsKey(keyN)) { + inDegree.put(keyN, 0); + } + inDegree.put(keyN, inDegree.get(keyN) + 1); + } + } + + //BFS: Add root node. + Queue queue = new LinkedList<>(); + for (DirectedGraphNode node : graph) { + 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; + inDegree.put(label, inDegree.get(label) - 1); + if (inDegree.get(label) == 0) { + rst.add(n); + queue.offer(n); + } + } + } + return rst; + } +} + + +// topo sort function from +// 1203. Sort Items by Groups Respecting Dependencies +/* +Input: +- Map> graph: reverse map where key is the sink node; +- set: target set of items to sort +- a special handling on `if (!set.contains(node)) continue;` which is specific for the problem; can ignore. +*/ +private List topoSort(Map> graph, Set set) { + Map indegree = new HashMap<>(); + for (int item : set) { // init all elements from the set + indegree.put(item, 0); + } + for (int node : graph.keySet()) { // sink node + if (!set.contains(node)) continue; // ignore the dependency on nodes outside of the target set + for (int parent : graph.get(node)) { // parent node + indegree.put(parent, indegree.get(parent) + 1); + } + } + Queue queue = new LinkedList<>(); + for (int key : indegree.keySet()) { + if (indegree.get(key) == 0) queue.offer(key); + } + + List rst = new ArrayList<>(); + while (!queue.isEmpty()) { + int node = queue.poll(); + rst.add(node); + if (!graph.containsKey(node)) continue; + for (int parent : graph.get(node)) { + indegree.put(parent, indegree.get(parent) - 1); + if (indegree.get(parent) == 0) queue.offer(parent); + } + } + + return rst; +} + +``` \ No newline at end of file diff --git a/Java/[tool]. UnionFind.java b/Java/[tool]. UnionFind.java new file mode 100755 index 0000000..6a6718e --- /dev/null +++ b/Java/[tool]. UnionFind.java @@ -0,0 +1,110 @@ +M +tags: Union Find, Lint +time: O(n), with Path Compression O(mN), with Union by Rank O(logN) +space: O(n) + +#### Method1: Union Find with Array +- union(), find() +- Path Compresion: store skip father after found, which makes find O(1) + +#### Method2: Union Find with HashMap + + + +``` +class Solution { + // Method1: Union Find with Array + 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--; + } + } + + // Alternative: union with rank[] + public void union(int x, int y) { + int rootX = find(x); + int rootY = find(y); + if (rootX != rootY) { + if (rank[rootX] > rank[rootY]) { + father[rootY] = rootX; + } else if (rank[rootX] < rank[rootY]) { + father[rootX] = rootY; + } else { // rank[rootX] == rank[rootY] + father[rootY] = rootX; + rank[rootX]++; + } + count--; + } + } + + + public int query() { + return count; + } + + public void setCount(int value) { + count = value; + } + + private int find(int x) { + if (father[x] == x) return x; // found root father + return father[x] = find(father[x]); + } + } + + // Method1: Union Find with HashMap + 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); + } + } + } + +} + + +``` \ No newline at end of file diff --git a/Java/k Sum.java b/Java/k Sum.java new file mode 100755 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..b0884c8 --- /dev/null +++ b/KnowledgeHash.md @@ -0,0 +1,1603 @@ + +Table of Contents +================= + + * [Table of Contents](#table-of-contents) + * [基础知识](#基础知识) + * [1. 套路](#1-套路) + * [2. Java Collections](#2-java-collections) + * [Collections.sort()](#collectionssort) + * [ArrayList](#arraylist) + * [3. String](#3-string) + * [Instinct](#instinct) + * [String Functions](#string-functions) + * [Characters](#characters) + * [StringBuffer](#stringbuffer) + * [4. Customized Data Model](#4-customized-data-model) + * [5. BigO](#5-bigo) + * [6. Time Complexity of graph/dfs block](#6-time-complexity-of-graphdfs-block) + * [7. Math](#7-math) + * [Math Functions](#math-functions) + * [Numbers](#numbers) + * [Probability theory](#probability-theory) + * [Combinatorics](#combinatorics) + * [subset time&&space](#subset-timespace) + * [8. Bit Manipulation](#8-bit-manipulation) + * [9. Threads](#9-threads) + * [Basic Data Structure](#basic-data-structure) + * [1. Array](#1-array) + * [Honorable Problems](#honorable-problems) + * [2. Hash Table](#2-hash-table) + * [HashTable](#hashtable) + * [HashSet](#hashset) + * [HashMap](#hashmap) + * [3. Heap](#3-heap) + * [Insert](#insert) + * [Extract Minimum Element](#extract-minimum-element) + * [如何想到用 Min/Max Heap](#如何想到用-minmax-heap) + * [Example](#example) + * [4. Stack](#4-stack) + * [Functions](#functions) + * [基本用法](#基本用法) + * [Monotonous Stack](#monotonous-stack) + * [Example](#example-1) + * [5. Queue](#5-queue) + * [Functions](#functions-1) + * [6. Linked List](#6-linked-list) + * [考法](#考法) + * [7. Deque](#7-deque) + * [Advanced Data Structure (A): I am groot, a tree](#advanced-data-structure-a-i-am-groot-a-tree) + * [1. Types of Tree, Basic knowledge](#1-types-of-tree-basic-knowledge) + * [Definition](#definition) + * [Balanced bianry tree](#balanced-bianry-tree) + * [Complete binary tree](#complete-binary-tree) + * [Full Binary Tree](#full-binary-tree) + * [Perfect Binary Tree](#perfect-binary-tree) + * [2. Traversal](#2-traversal) + * [Binary Tree Traversal](#binary-tree-traversal) + * [Inorder Traversal](#inorder-traversal) + * [3. Binary Search Tree (BST)](#3-binary-search-tree-bst) + * [Is it Binary Tree!?](#is-it-binary-tree) + * [TreeSet](#treeset) + * [Traversal](#traversal) + * [4. Expression Tree](#4-expression-tree) + * [Example](#example-2) + * [Build Tree](#build-tree) + * [5. Trie](#5-trie) + * [用法/考点](#用法考点) + * [Compare with HashMap](#compare-with-hashmap) + * [Code Model/ Sample Functions](#code-model-sample-functions) + * [6. Binary Indexed Tree](#6-binary-indexed-tree) + * [7. Segment Tree](#7-segment-tree) + * [基本知识](#基本知识) + * [functions](#functions-2) + * [用法](#用法) + * [8. Red Black Tree](#8-red-black-tree) + * [基本知识](#基本知识-1) + * [特点](#特点) + * [引申特点](#引申特点) + * [用法](#用法-1) + * [9. B-Tree](#9-b-tree) + * [基本知识](#基本知识-2) + * [10. AVL Tree](#10-avl-tree) + * [基本知识](#基本知识-3) + * [更多细节特点](#更多细节特点) + * [用途](#用途) + * [优点](#优点) + * [Advanced Data Structure (B): Graph](#advanced-data-structure-b-graph) + * [构建Graph](#构建graph) + * [Popular algorithms](#popular-algorithms) + * [Adjacency List](#adjacency-list) + * [Adjacency Matrices](#adjacency-matrices) + * [Graph Search](#graph-search) + * [DFS](#dfs) + * [BFS](#bfs) + * [Bidirectional Search](#bidirectional-search) + * [Basic Algorithms](#basic-algorithms) + * [1. Two Pointers](#1-two-pointers) + * [2. Binary Search](#2-binary-search) + * [Template](#template) + * [中二思想](#中二思想) + * [3. Sort](#3-sort) + * [常用](#常用) + * [Merge Sort](#merge-sort) + * [Heap Sort](#heap-sort) + * [Quick Sort](#quick-sort) + * [Quick Select](#quick-select) + * [Comparator for Arrays, Collections](#comparator-for-arrays-collections) + * [4. BFS/DFS Search](#4-bfsdfs-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) + * [regular primitive dfs](#regular-primitive-dfs) + * [object dfs](#object-dfs) + * [Tree DFS](#tree-dfs) + * [5. Backtracking](#5-backtracking) + * [6. Greedy](#6-greedy) + * [When to use](#when-to-use) + * [Examples](#examples) + * [7. Divide and Conquer](#7-divide-and-conquer) + * [8. Recursion](#8-recursion) + * [Advanced Algorithm (A): Union Find](#advanced-algorithm-a-union-find) + * [Basics](#basics) + * [UnionFind基础操作](#unionfind基础操作) + * [UnionFind follow up](#unionfind-follow-up) + * [Advanced Algorithm (B): Topological Sort](#advanced-algorithm-b-topological-sort) + * [建立Graph InDegree](#建立graph--indegree) + * [Topological Sort - BFS](#topological-sort---bfs) + * [Topological Sort - DFS](#topological-sort---dfs) + * [Advanced Algorithm (C): Bucket Sort](#advanced-algorithm-c-bucket-sort) + * [Advanced Algorithm (D): DP, Dynamic Programming](#advanced-algorithm-d-dp-dynamic-programming) + * [1. Basics](#1-basics) + * [判断](#判断) + * [四个步骤:](#四个步骤) + * [确定状态](#确定状态) + * [转移方程](#转移方程) + * [初始条件/边界情况](#初始条件边界情况) + * [计算顺序](#计算顺序) + * [Technique](#technique) + * [基本原理](#基本原理) + * [2. 分类](#2-分类) + * [a. 网格坐标 (Coordinate)](#a-网格坐标-coordinate) + * [b. 序列 (Sequence)](#b-序列-sequence) + * [特点](#特点-1) + * [性质](#性质) + * [关键点](#关键点) + * [序列加状态](#序列加状态) + * [c. 划分(Partition)](#c-划分partition) + * [性质](#性质-1) + * [经验](#经验) + * [解决方法](#解决方法) + * [d. 博弈类 (Game Theory)](#d-博弈类-game-theory) + * [e. 背包类 (Backpack)](#e-背包类-backpack) + * [多种问法](#多种问法) + * [方法策略](#方法策略) + * [放入的物品没有顺序](#放入的物品没有顺序) + * [f. 区间类(Interval DP)](#f-区间类interval-dp) + * [特点](#特点-2) + * [三把斧](#三把斧) + * [难点](#难点) + * [g. Bitwise Operation DP](#g-bitwise-operation-dp) + * [3. DP典型罗列](#3-dp典型罗列) + * [Minimax/MaxiMin](#minimaxmaximin) + * [Optimization problems](#optimization-problems) + * [Double Sequence](#double-sequence) + * [存状态](#存状态) + * [4. 记忆化搜索 Memoization DP](#4-记忆化搜索-memoization-dp) + * [什么时候用记忆化搜索](#什么时候用记忆化搜索) + * [特点](#特点-3) + * [缺点](#缺点) + * [时间空间复杂度的节省](#时间空间复杂度的节省) + * [Advanced Algorithm (E): Sweep Line](#advanced-algorithm-e-sweep-line) + * [问题类型分类](#问题类型分类) + * [1. Combinatorics 组合](#1-combinatorics-组合) + * [DFS 思想](#dfs-思想) + * [DFS 注意](#dfs-注意) + * [2. Permutation 排列](#2-permutation-排列) + * [原理](#原理) + * [Backtracking DFS (Recursive)](#backtracking-dfs-recursive) + * [插入法 (iterative)](#插入法-iterative) + * [3. 回文串 Palindrome](#3-回文串-palindrome) + * [4. Windows Problem](#4-windows-problem) + * [5. Sum, PrefixSum](#5-sum-prefixsum) + * [知识储备](#知识储备) + * [0. Brainteaser](#0-brainteaser) + * [1. Operating system](#1-operating-system) + * [2. Java Garbage Collections](#2-java-garbage-collections) + * [Heap](#heap) + * [Garbage Collection Roots](#garbage-collection-roots) + * [Example](#example-3) + * [Marking and Sweeping Carbage](#marking-and-sweeping-carbage) + * [3. Pain Point](#3-pain-point) + * [4. NP-Complete problems](#4-np-complete-problems) + * [wiki](#wiki) + * [Knapsack](#knapsack) + * [Travelling salesman](#travelling-salesman) + * [5. Java Design Pattern](#5-java-design-pattern) + * [Uknowns](#uknowns) + * [Geometry](#geometry) + * [Advanced Algorithm (F): Reservoir Sampling](#advanced-algorithm-f-reservoir-sampling) + + + +# 基础知识 +## 1. 套路 +- **Code accuracy, speed, test, maintanability** +- **Try to solve new problem: don't try to link with old problems** +- **Analyze** + - Draw examples & walk through use cases: + - edge cases? ask if we want to return empty to throw exception? [Don't over do. Maybe your code already handles it] + - Does the example assemble certain pattern? sort, search, O(1) lookup? + - Does the example requires `graph` relationship: i.e., `from x to y`. + - Summarize a rough idea of how to approach/solve +- **Time Complexity it ASAP** + - Analyze BigO of the proposed solution before jumping into coding +- **Data Modeling**: given a vague problem, model it with data structure. Rule of thumb: + 1. keep it simple. (i.e. `int[2]{x,y}` can be sufficient a lot times) + 2. don't be shy from creating an class/object (CC does it all the time) +- **List a few approaches/algorithms** + 1. DO NOT dive into 1 approach too fast + 2. Also DO NOT come up with too many redundant approaches + 3. Keep in mind: we just want to compare approach, and pick a better one. +- **Communication** + 1. FIRST: share & illustrate workflow on side + 2. `: colon`, `; semicolon`, `! exclamation mark`, `{curly bracket}`, `[square bracket]`, `(parentheses)` +- **Instincts** + 1. O(1) read/write: `hash`; edge/relationship: `graph`; enumeration: `BFS or DFS` and sometimes with `memo` + 1. When solving max/min num, possibility ... with recursive, keep in mind about `memo` and `dp` + 1. Shortest path: likely BFS + 1. Besides regular list, map; keep queue & stack in mind. + 1. If the function is designed for large scale problem (calling it 100k times): any resource to optimize? + 1. Precompute resource: if a server machine (w/ storage, cache, or enough memory) exists, it indicates: 1) precompute, 2) use the result/resource repeatedly. +- **mistakes** + - When you mean to say `table`, don't say `database` + - When you mean you want to store the data in ordered structure (like a TreeMap in java, basically BST): DON'T say you want to sort it. + - When people ask for concurrency and transtional, that leads to SQL; keep in mind to rethink if that is the database you want to choose. + +## 2. Java Collections +### Collections.sort() +- Sort sub list: `Collections.sort(list.subList(x, y))`. [x, y), exclusive at index y. + +### ArrayList +- Create list from array: `new ArrayList(Arrays.asList(1, 2, 3))` +- Remove range: `list.removeRange[x, y)` +- List Insertion Time O(1) on average + 1. When list fills up: the insertion cause the arrayList to: 1) doubles the size, 2) copy all contents to a new array. + 2. Overall: the insertion cost is 1 + 2 + 4 + ... n/8 + n/4 + n/2 = O(n) + 3. Therefore, the average insertion time is O(n)/ n = O(1) + +## 3. String +### Instinct +- Analyze string pattern and parse (i.e. `(A|B)&(C|D)`, letter order) +- Pay attention to repeated strings +### String Functions +- lexicographically字典order排序: `String.compareTo("XYZ")` +- split by: `str.split("\\ ")`, 需要用 "\\" regular expression +- remove space: `s.trim()` +### Characters + 1. `s.toCharArray()` + 2. count characters with int[256](skip `c-'a'`) + 3. `String.valueOf(charArrary)` + 4. `Character.isDigit(x)` +### StringBuffer +- regular: `sb.reverse()`, `sb.append()`, `sb.length()` +- index manipulation: `sb.deleteCharAt(i)`, `sb.setCharAt(i, c)`, `sb.insert(0, "xyz")` +- replace range of string: `sb.replace(i, j, "replacement string")` +### Sliding Window +- refert to the algorithm section for sliding window + +### Palindromic String/Subsequence +- 516. Longest Palindromic Subsequence. Use DP to calc subsequence +- 5. Longest Palindromic Substring. Use same DP template, but restrict by substring rules. +- template +``` +// 1) init +int/boolean dp[][] = new int/boolean[n][n]; +// 2) process from back with `i = n -1`, `j = i + 1` +for (int i = n - 1; i >= 0; i--) { + // 3) aways init dp[i][j]: single char usually has default property. + dp[i][i] = xxx; + for (int j = i + 1; j < n; j++) { + // 4) handle match, or not match cases + if (s.charAt(i) == s.charAt(j)) { + // utilize dp[i + 1][j - 1], + // sometimes dp[i][j - 1] or dp[i + 1][j] + } else { + // do someting else if not matching + } + } +} +``` + +## 4. Customized Data Model +- **common use case**: in dfs or other algorithm, need to pass around more data than a single premitive variable +- **example**: `Binary Tree Maximum Path Sum`: need to track single path, and combinded path. 把每个node的status, 存在一个 customized object 里面, pass around +``` +private class PathSum { + int singlePathMax; + int combinedPathMax; + PathSum(int singlePathMax, int combinedPathMax) { + this.singlePathMax = singlePathMax; + this.combinedPathMax = combinedPathMax; + } +} +``` + +## 5. BigO +- **O(logN)**: when space/runtime cut in halved iteration, it indicates runtime of O(logN) +- **O(x ^ N)**: Recursive Runtime, when the recursive call makes x branches, the runtime is likely to be O(x ^ N). [it's not always] + - Important: the base x of the recursive runtime DOES MATTER! 2^n is very different from 8^n = 2^3n +- With recursion: if we use `memoization` to stores n results and return at revisiting + - overall runtime can be O(N) + - we only calculate the specific N node and N's children nodes exactly ONCE. + +## 6. Time Complexity of graph/dfs +- dfs of graph: 每个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)` +- Again, sometimes with `memoization`, we can reduce runtime to `O(n)` in certain problems + +## 7. Math +- 转换成character: `'0' + num` +- % 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") +- Sometimes it's good to use `Integer[] var` because `var[i] can be null` +- 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)` + + +## 8. 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 + +## 9. Threads +Two approaches: +- Implement the java.lang.Runnable interface +- Extend the java.lang.Thread class + + + +# Basic Data Structure +## 1. Array +- Sorted? value boundary? +- 遇到需要排序: Arrays.sort() +- 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) + +## 2. 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();` + +## 3. 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) + - PQ can be used to sort on multiple attributes (i.e., `Pair {int dist, workerIndex, bikeIndex}` in 1057. Campus Bike). + +### 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 + + +## 4. Stack + +### Functions +- peek(), pop(), push() + +### 基本用法 +- store something on top and you may remove soon +- 翻转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) + + +## 5. 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. + +## 6. 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的规则, 可能链接方法不同) + +## 7. 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 + +## 8. TreeMap and TreeSet +### TreeMap +- https://www.tutorialspoint.com/Difference-between-TreeMap-HashMap-and-LinkedHashMap-in-Java +- TreeMap is implemented as a self-balanced tree, but basic operations (lookup,insertion) are all O(logn) +- log(n) search to find floor(key), ceiling(key) + - ceilingKey(K key): Returns the least key greater than or equal to the given key, or null if there is no such key. + - floorKey(K key): Returns the greatest key less than or equal to the given key, or null if there is no such key. + +### TreeSet +- https://dzone.com/articles/hashset-vs-treeset-vs +- TreeSet is implemented as tree, basic operation is O(logn) +- Useful for using higher(x), lower(x) function + - tailSet(E fromElement): Returns a view of the portion of this set whose elements are greater than or equal to fromElement. + + + +# Advanced Data Structure (A): I am groot, a tree + +## 1. Types of Tree, Basic knowledge + +### Definition +- 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, 没孩子 + +### Algorithm Approaches +- Top-Down Recursive (1026, 1008): + - parent/root node carries some values and pass down to sub recursive functions + - sometimes it is easy to write: pass value as dfs parameters +- Bottom-Up Recursive: + - find certain value, or certain set of value from the root -> up + - like segment tree updates from bottom + +### 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 + +## 2. Traversal + +### 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. + + + +## 3. Binary Search Tree (BST) +- 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 + +### Is it Binary Tree!? +- 一定要问清楚, 是Binary Tree (双孩子而已), 还是 Binary Search Tree. 非常重要!!! +- 一个child tree的nodes总量是 2 ^ h - 1; 那么一个child tree + root 的nodes 总量就是 2 ^ h了. + +### 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 + +## 4. 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. + +## 5. 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 + +## 6. Binary Indexed Tree +- some clue here: https://www.geeksforgeeks.org/binary-indexed-tree-or-fenwick-tree-2/ + +## 7. Segment Tree +### 基本知识 +- Also called, Interval Tree. Top-Down的一个tree (https://en.wikipedia.org/wiki/Segment_tree, https://en.wikipedia.org/wiki/Interval_tree) + - Build: O(n). 做出来是一个balanced的tree (因而是logn查找) + - Update: O(logn) + - Range Query: O(logn + k) +- range query: 想到segment tree +- NOT working for: "增加元素", segment tree在build之后无法变更 +``` +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; // sum, max, min + 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 + +## 8. 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` + + +## 9. 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嘛, 简单. + +## 10. 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 + +# Advanced Data Structure (B): 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 + +## 构建Graph +- 建立edges: 1. 创建所有node -> neighbor list; 2. 把满足条件的neighbor 填进去(每个题目不同) + - adjacency list graph: `Map>` +- 如果是做Topological Sort, 还要建立 `inDegree`: `Map`, 或者就是 `int[]` + +## 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: +- 261. Graph Valid Tree +- 很多题目里面都是用 `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; + } +``` + +### 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) + +# Basic Algorithms +## 1. Two Pointers +- Two pointers in array +- Two pointers in Linked List + +### Two Pointers from both sides, narrowing to middle + +### Start catching up to End pointer +- End moves early/faster +- Start will catch up to end +- Ex: 76. Minimum Window Substring + +### Source Pointer and Target Pointer +- Find target `subsequence` in source by comparing sIndex and tIndex +- Backtrack both pointers: tIndex to 0, sIndex to 1st valid matched char +- Ex: 727. Minimum Window Subsequence + +### Sliding Window +- refer to the sliding window section + +## 2. Binary Search +### Template +- 二分的template +- 按值二分,需要怎么二分性 +- Steps: + - 1. 找到可能的解的mid; + - 2. 猜答案; + - 3. 检验答案 (match/if/else ...); + - 4. 调整搜索范围 + - *** 画图, 标记mid的位置, 帮助自己写 if/else *** +- Find Peak Element II + +### 中二思想 +- 往往不会有个数组让我们二分, 但是同样是要去找满足某个条件的最大/最小值 +- 二分是个思想, 不是简单的array二分公式. 忌讳不要死板地套用nums[mid] + - 有时候在index上二分, `mid是index` + - 有时候, 会在数值上二分, `mid就是value` + + +## 3. Sort +### 常用 +- Merge Sort. Runtime: O(nlogn) average/worst. Memory: depends. +- Quick Sort. Runtime: O(nlogn) average, O(n^2) worst. Memory: O(nlogn). +- Bucket Sort. +- Heap Sort +- Radix Sort. Runtime: O(nk) +- 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 + +### Bucket Sort +- Usually there is a bounded range so that we can create an bounded array + - 1) Iterate over the input, and put item to its corresponding bucket index + - 2) Iterate over the bucket to process the sorted items by index. + +### 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(nlogn)的时间复杂度降到O(n); worst case O(n^2) +- 主要步骤: partition, dfs, only recur on one part of the array + - 1) 用 end index作为pivot, 每次根据nums[pivot]排序之后, 在 < low的位置, 都小于 nums[pivot] + - 2) 把nums[pivot]换到low的位置, 那么换好之后, 所有在low前面的值, 都是小于 nums[pivot]了 + - 3) 那么其实 nums[low]也就是 kth 最小值 + +``` +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; +} +``` + +### Comparator for Arrays, Collections +``` +public int compare(x, y) { + return x - y; // or x.compareTo(y) +} +``` + +## 4. BFS/DFS 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. + +#### regular primitive dfs +- usually object = int, string +- 每次dfs都会 build on 这个return value + +#### 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 + +#### 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` + +## 5. 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)` + +## 6. 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 + +## 7. Divide and Conquer + +## 8. 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) + +## 9. Sliding Window +- Sliding window aims to establish a window with certain balance (i.e. `counter==0`); + - use right pointer to expand the window and reach the balance + - use left pointer to contract/shrink the window; sometimes to break the balance +- In any `sliding window` based problem we have two pointers. + - `Right pointer`: expand the current window + - `Left pointer`: shrink/contract a given window. +- At any point in time only one of these pointers move and the other one remains fixed. +- Example + - Sliding window to keep simply keep window size: `1004. Max Consecutive Ones III` + - Sliding window to maintain valid anagram char count: `438. Find All Anagrams in a String` + - Sliding window till matching point; backtrack all the way to find smallest window (closest left starting point to right) + - `727. Minimum Window Subsequence`: subsequence requires keeping the order of target chars +- Template + - 1) Move `right pointer`, one step at a time. + - Sometime, need to reduce `char counter` (if applicable). `counter==0` is usually used to mark a valid candidate + - always reduce freq[c]-- to mark a character being used + - 2) when a valid case is build (i.e., `counter==0`) process as needed + - 3) shrink/contract the window by moving `left pointer`. + - sometimes this occurs within the `2) step` if applicable; + - but the `window shrink` remains +- Given a string and need to find a substring of it which satisfy some restrictions +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +``` +int findSubstring(string s){ + vector map(128,0); + int counter; // check whether the substring is valid + int begin=0, end=0; //two pointers, one point to tail and one head + int d; //the length of substring + + for() { /* initialize the hash map here */ } + + while(end`, 也可能是 `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是否存在 + + +# Advanced Algorithm (C): Bucket Sort +- Bucket sort is simple: rather than sorting the data, assume boundary of the dataset, and put data into bucket slot +- IMPORTANT: bucket slots may be null; make sure to double check null when consuming bucket + + + +# Advanced Algorithm (D): DP, Dynamic Programming +## 1. Basics +### 判断 +- `计数`: how many. 加法原理 +- `求最大/最小值`: min/max +- `求存在性`: 能否, AND/OR dp=true/false +- 4 Steps: 1) 确定状态, 2) 转移方程, 3) 初始条件/边界情况, 4) 计算顺序 + +##### 确定状态 +- What to save in DP[] + - consider last choice: 遍历最后一步可以取的情况 + - sub problem: 切掉最后一块, 剩下的问题跟原问题应该是一样的 + +##### 转移方程 +- 数学表达式来判断 dp[x]的结果 +- 纸面上工作结束 + +##### 初始条件/边界情况 +- 非常重要, 必须有初始条件, 才可以让dp计算出正确结果. + +##### 计算顺序 +- 递推: 从小到大, 从 dp[0], dp[1] 开始 +- 记忆化: 从大到小, 先算一遍 dp[n-1] + +### Technique +- 滚动数组 +- 记忆化搜索 + +### 基本原理 +- 加法原理: `无重复(no overlap last step)`, `无遗漏(cover all possible last steps)` +- dp = new int[n] 还是 dp = new int[n + 1]: 看角标是否表示坐标, 还是前i items的状态 + +## 2. 分类 +- 网格坐标 * 20% +- 序列类 * 20% +- 划分类 * 20% +- 区间类(interval DP) +- 背包类 +- 双序列 +- 博弈 +- combos +- Bitwise Operation动态规划 + +### a. 网格坐标 (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的题目 + +Special case: +- 最长序列型动态规划: 恰恰是坐标类 +- 以i点结束, 很可能可以考虑坐标型 +- Longest Increasing Subsequence (祖师爷级别老题目) + + +### b. 序列 (Sequence) + +#### 特点 +- 变种多 +- 没有固定模板 +- 可以选择让: DP[i]存的是以 1-based index的状态, 求前i个. +- 那么, 需要知道dp[n] 的状态, 但是最大坐标是[n-1], 所以int[n+1]. +- 初始化(简单): dp[0]往往是有特殊状态的: 0 index有时候初始化就是0 +- 边界情况(简单): 0 是虚拟的位, 所以大多数时候, 就留成0, 不太有所谓. + +#### 性质 +- `dp[i]种, 表示 '前i个元素a[0], a[1] ... a[i - 1] 的某种性质`. (坐标类:dp[i]就代表a[i]结尾的性质) +- dp[0] 表示空序列的性质 (坐标类: dp[0]表示以a[0]结尾的性质) +- 题目中遇到 前i个, '并且': 序列 + 状态 + +#### 关键点 +- 不关心前面n-1是怎么组合出的状态, 但是可以先确定末尾的状态 +- 及时思考的时候从结尾, 在代码写的时候, 其实是模拟末尾是i = [0 ~ n] 的情况, 一圈圈计算. +- 最后再给出 dp[n] 的末尾解答. + +#### 序列加状态 +- 当思考动态规划最后一步时, 这一步的选择依赖于 前一步 的某种状态: 一维 + 状态 +- 序列+状态:如果n-1步有多种状态, 且n步也有多种可能, 添加一种状态记录, 变成2D dp[sequence index][状态] +- Example: Paint House + + +### c. 划分(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 + +### d. 博弈类 (Game Theory) +- 常常问: 先手必胜的情况 +- 必胜状态/必败状态的分析 +- 通常从第一步分析, 从简单的来分析 (唯一一个需要考虑第一步的 ) +- 必胜: 在当下局面走出一步, 让对手无路可逃. 只要对手有一种必败的路线, 就是我必胜. + +### e. 背包类 (Backpack) + +#### 多种问法 +- 前i个物品能不能拼出重量W. 是 可行性 问题: OR , AND +- 填一个什么包, 有一个条件, 重量不超过M +- 不撑爆背包的前提下: +- 装下最多重量物品 +- 装下最大价值的物品 +- 有多少种方式带走满满一书包物品 +- 注意: 如果同一种物品可以拿无限次, 那么`dp[i][w] 表示: 前i 种 物品, 装weight w 的性质`. + +#### 方法策略 +- 还有几个物品 +- 还剩多少跟**总承重**有关 +- 用总承重M的大小来开数组. +- 不管几维数组, 总有一维是总承重M +- 比如: dp[i][w] = 能否用前i个物品, 拼出重量W (true/false) + +#### 放入的物品没有顺序 +- 放入的物品没有顺序: 最后一个背包内的物品是哪个 +- 放入的物品有顺序: +- 1. 最后一个背包内的物品是哪个 +- 2. 最后一个物品有没有进背包 + +### f. 区间类(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; + ... + ... + } +} +``` + +### g. Bitwise Operation DP + + +## 3. 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, 输/赢 +- 通常加上一个维度 + + +## 4. 记忆化搜索 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 + + +# Advanced Algorithm (E): Sweep Line +- 见到区间需要排序就可以考虑扫描线 +- 事件往往是以区间的形式存在 +- 区间两端代表事件的开始和结束 +- 需要排序 +- Meeting Room I, II + + +# + + +# 问题类型分类 +## 1. 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 + +## 2. Permutation 排列 +- Great Example: 44. Permutations. It has a few types of recursive and iterative approach +### 原理 +- 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 + + +## 3. 回文串 Palindrome +- 奇数串, 中间有个unique字符 +- 偶数, 中间开始有两个相同的字符 +- 找到所有的回文串, 只需要 O(n^2): isPalin[i][j]表示 S[i...j] 是否是palindrome + +## 4. Windows Problem +- 加一个数 +- 删一个数 + +## 5. 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 + + + +# 知识储备 + +## 0. Brainteaser +- TO FILL: + +## 1. 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. + +## 2. 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. + + +## 3. Pain Point +- For any array access, make sure to check the boundary!!! +- subsequence: not continuous, can skip candidate! +- subarray: continous! + +## 4. 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 + + +## 5. 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 + +## API Rate Limiting +- https://nordicapis.com/everything-you-need-to-know-about-api-rate-limiting/ + +# Uknowns +## Geometry +?? + +## Advanced Algorithm (F): Reservoir Sampling +?? \ No newline at end of file diff --git a/LevelReviewPage.md b/LevelReviewPage.md new file mode 100644 index 0000000..8062bb2 --- /dev/null +++ b/LevelReviewPage.md @@ -0,0 +1,11836 @@ + + + +## Easy (182) +**0. [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 + + + +--- + +**1. [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) + + + +--- + +**2. [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) 太慢, 不合题意 + + + +--- + +**3. [Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)** Level: Easy Tags: [String, Two Pointers] + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + + + +--- + +**4. [Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)** Level: Easy Tags: [Math] + + + +--- + +**5. [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一一对应。 + + + +--- + +**6. [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. + + + +--- + +**7. [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. + + + +--- + +**8. [Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Hamming%20Distance.java)** Level: Easy Tags: [] + +bit: XOR, &, shift>> + + + +--- + +**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. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + + +#### 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题,想不到,就是搞不出。 + + + + +--- + +**11. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**12. [Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)** Level: Easy Tags: [Bit Manipulation] + + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + + + +--- + +**13. [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 + + + +--- + +**14. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + + + +--- + +**15. [IndexMatch.java](https://github.com/awangdev/LintCode/blob/master/Java/IndexMatch.java)** Level: Easy Tags: [] + +有序, 假设有这样的数字:target. +target 左边的数字,一定不比index大,target右边的数字,一定比index大。 +这样可以binary search.O(logn) + + + +--- + +**16. [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 + + + +--- + +**17. [Reshape the Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Reshape%20the%20Matrix.java)** Level: Easy Tags: [] + +读例子理解题意. +理清counter case. Basic implementation + + + +--- + +**18. [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] + + + +--- + +**19. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + +--- + +**20. [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()里, 每次换水,查看末尾项. + + + + +--- + +**21. [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 + + + +--- + +**22. [HashWithArray.java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithArray.java)** Level: Easy Tags: [] + + + + +--- + +**23. [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 + + + +--- + +**24. [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. + + + +--- + +**25. [Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)** Level: Easy Tags: [Basic Implementation] + +根据 Cosine Similarity 的公式, basic implementation + + + +--- + +**26. [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作比较;求的所有情况的最大值嘛。 + + + +--- + +**27. [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, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**28. [Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)** Level: Easy Tags: [Design] + +让一个class 是 singleton + + + +--- + +**29. [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】上位,所踏过的所有小命啊! + +我这解释太生动了。因为耗费了好长时间思考... + + + +--- + +**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)** 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. + + + +--- + +**31. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**32. [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负数补位. + + + +--- + +**33. [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找合适的数字. + + + +--- + +**34. [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,也是类似做法 + + + +--- + +**35. [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的基础上, 比对左左,左右,右左,右右 + + + +--- + +**36. [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规律。 + + + +--- + +**37. [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 + + + +--- + +**38. [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. + + + + +--- + +**39. [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完 + + + +--- + +**40. [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.有点耐心 + + + +--- + +**41. [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 + + + +--- + +**42. [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的判断 + + + +--- + +**43. [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整除. 一步到位. + + + +--- + +**44. [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的情况。要把遍历的例子写写 + + + +--- + +**45. [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去做 + + + +--- + +**46. [[HackerRank]. Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/[HackerRank].%20Change%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. + + + +--- + +**47. [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了. + + + + +--- + +**48. [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 公式 + + + +--- + +**49. [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 + + + +--- + +**50. [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) + + + +--- + +**51. [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 + + + +--- + +**52. [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` + + + +--- + +**53. [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; + + + + +--- + +**54. [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) + + + +--- + +**55. [Search Insert Position.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Insert%20Position.java)** Level: Easy Tags: [] + +一般的binary search. +在结尾判断该return 哪个position。 + + +--- + +**56. [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. + + + +--- + +**57. [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> +- 存最长值, 最后map.get(max) + + + +--- + +**64. [[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)** Level: Easy Tags: [Array, Lint, 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) + + + + +--- + +**65. [[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Lint, 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! + + + + +--- + +**66. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**67. [[lint]. Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array, Lint] + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**68. [[tool]. Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Hash%20Function.java)** Level: Easy Tags: [Hash Table, Lint] + + +In general, there is no universal recipe to stick to when it comes to implementing hashCode(). +https://www.baeldung.com/java-hashcode + +#### 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会变得太大,所以不能算完和 再 %... + + + +--- + +**69. [36. Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/36.%20Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited row/col/block. + - 在nest for loop里面validate row,col,and block. + - Special: validate block要利用i 和 j 增长的规律 +- i, j are [0~n) can build block boundary in a 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 +- 可能代码稍微复杂一点 + + + +--- + +**70. [359. Logger Rate Limiter.java](https://github.com/awangdev/LintCode/blob/master/Java/359.%20Logger%20Rate%20Limiter.java)** Level: Easy Tags: [Design, Hash Table] + + +#### HashMap +- map: avoid duplicate message, records timestamp for validation +- time: O(1) +- space: O(n) + +#### Queue + Set +- 1) keep a trimmed queue and set (all tasks to be within 10 secs); +- 2) use set to O(1) check if incoming message exists. +- time: O(x), trimQueue() +- space: O(n) + + + +--- + +**71. [198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +搜刮房子, 相邻的不能碰. 每个房子里有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虽然抽象, 但是更加实用. + +#### Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- calculation + - dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]). The prior house can be either state. + - dp[i][1] = dp[i - 1][0] + nums[i]. The prior house must be `NOT ROBBED`. +- return `Math.max(dp[n - 1][0], dp[n - 1][1])` + + + +--- + +**72. [21. Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/21.%20Merge%20Two%20Sorted%20Lists.java)** Level: Easy Tags: [Linked List] + + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + + + +--- + +**73. [788. Rotated Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/788.%20Rotated%20Digits.java)** Level: Easy Tags: [Basic Implementation, String] + + +#### Basic Implementation of the rules +- [3,4,7] -> cannot rotate, failures. Must NOT have. set1 +- 2,5,6,9 -> good candidates. Must have 1. set2 +- [0,1,8] -> goes back to itself. can have +- loop over [1, N], count=int[10] appearance. + - set1 meet 0 + - set2 meet at least 1 + + + +--- + +**74. [237. Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/237.%20Delete%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 + + + +--- + +**75. [448. Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/448.%20Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)** Level: Easy Tags: [Array, Bucket Sort] + + +#### Method1: Bucket Sort concept, set val to its correct position +- Given: values are [1,n], so val can represent index. Therefore, set val to its correct position +- 小心handle i: + - value是 1-based + - 每次换位, 需要`i--`, 重新省察`nums[i]` + +#### Method2: 做标记 (negative number, or super large number) +- Option1: use negative number to mark visited: + - 很巧妙地运用了标记的方法, 标记成负数,证明visit过。 + - Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! +- Option2: use large number (larger than n) + - 跟方法2类似,也是做标记,这一次是加上一个大于n的数(因为题目给了n的border),最后check一下就好。为不超Integer.MAX_VALUE, 每次加n前,取余数。 + - 做标记的方法固然快,但是相对来说比较hacky,在常规的代码中,估计不会用到. + + + + +--- + +**76. [849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array, Basic Implementation, Two Pointers] + + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, Two Pointers: 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` + + + + +--- + +**77. [408. Valid Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/408.%20Valid%20Word%20Abbreviation.java)** Level: Easy Tags: [Basic Implementation, String] + +tricky: find integer within a string +edge case: leading '0' should not be allow in such abbr. + + + +--- + +**78. [415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)** Level: Easy Tags: [Basic Implementation, Math, String] + + +#### Two Pointer +- Use i, j to process from end of 2 strings +- handle edge case for i, j + - if i < 0, its num = 0 (since we are doing sum, blindly setting 0 is okay) +- Note: `sb.insert(0, x)` is much slower than doing a final `sb.reverse()` + +#### If manually convertin to int[] +1. when converting to int[], remember to reverse string. +1. when converting to int[], remember to reserve extra space for carry + + + +--- + +**79. [83. Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/83.%20Remove%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会非常清晰 + + + +--- + +**80. [1108. Defanging an IP Address.java](https://github.com/awangdev/LintCode/blob/master/Java/1108.%20Defanging%20an%20IP%20Address.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**81. [1021. Remove Outermost Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1021.%20Remove%20Outermost%20Parentheses.java)** Level: Easy Tags: [Stack] + +#### Stack +- use stack to hold potential pair +- when stack is empty: detect outtermost element, dont add to final result +- time: O(n), space O(n) + +#### Count occurance +- solution from discussion, time O(n), space O(1) +- save space, but less scalable: think about if there are 100 different pairs, then the couting will be a bit complex to handle. + + + +--- + +**82. [766. Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/766.%20Toeplitz%20Matrix.java)** Level: Easy Tags: [Array] + + +#### Check diagonal +- 似乎没什么算法特点, 就是array基本运算, 然后分割成一个helper function去做重复计算, 剪短代码. +- 注意check MxN 的分界线. + +#### Simpler Solution +- the goal is to check [i][j] == [i+1][j+1] for every i and j. + + + +--- + +**83. [953. Verifying an Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/953.%20Verifying%20an%20Alien%20Dictionary.java)** Level: Easy Tags: [Hash Table] + + +#### Hash Table +- mark the char position +- check adjacent words +- Optimization + - a) If s1 equals s2, just return true, no need to continue. + - b) if s2 (app) is a substring of s1(apple), just return false. + + + + +--- + +**84. [1213. Intersection of Three Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/1213.%20Intersection%20of%20Three%20Sorted%20Arrays.java)** Level: Easy Tags: [Hash Table, Two Pointers] + + +Very similar to 349.Intersection of Two Arrays. + +#### Hash Table +- Use set to check +- Verify duplicates at end rst + +#### Two Pointers +- similar to Intersection of Two Sorted Arrays +- Start from front/back, process 1 item at a time + - if match, move all pointers +- Optoin1: check from back +- Optoin2: check from frotn + + + +--- + +**85. [383. Ransom Note.java](https://github.com/awangdev/LintCode/blob/master/Java/383.%20Ransom%20Note.java)** Level: Easy Tags: [Basic Implementation, String] + +count chars in int[256] + + + +--- + +**86. [252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### Method1: sort input and compare if curr.end & next.start overlaps +- sort: `Arrays.sort(intervals, Comparator.comparing(i -> i[0]))` +- time: O(nlogn), space: O(1) + +#### Method2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 +- time: O(nlogn), space O(n) +- Not necessary for this problem, since it requires extra space with pq. + + + +--- + +**87. [665. Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/665.%20Non-decreasing%20Array.java)** Level: Easy Tags: [Array] + + +- 比较升序的时候, 必须要估计到 `i-1, i, i+1` 三个数位. +- 写出来`i-1, i, i+1`之间的关系, 然后做合理的fix. + 1. reduce nums[i+1] to fix + 1. raise nums[i+1] to fix +- 需要真的fix数组, 因为loop through做比较时会用到fix后的数字. + + + + +--- + +**88. [293. Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/293.%20Flip%20Game.java)** Level: Easy Tags: [String] + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + + + +--- + +**89. [686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)** Level: Easy Tags: [Basic Implementation, Edge Case, String] + +Track: 纸上分析edge case. +Validation helps speed it up. + + + +--- + +**90. [111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%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 Conquer to find min depth. + - if one of child is null, return the other child depth + 1 + - Pick the min of the two child depth + 1 +- need to visit all nodes + + + + +--- + +**91. [7. Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/7.%20Reverse%20Integer.java)** Level: Easy Tags: [Math] + + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + + + +--- + +**92. [303. Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/303.%20Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**93. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**94. [485. Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/485.%20Max%20Consecutive%20Ones.java)** Level: Easy Tags: [Array, Basic Implementation] + + +- preserve max +- 清零count + + + +--- + +**95. [896. Monotonic Array.java](https://github.com/awangdev/LintCode/blob/master/Java/896.%20Monotonic%20Array.java)** Level: Easy Tags: [Array] + +basic implementation + + + +--- + +**96. [26.Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/26.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 + + + +--- + +**97. [204. Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/204.%20Count%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个数就好了 + + + +--- + +**98. [58. Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/58.%20Length%20of%20Last%20Word.java)** Level: Easy Tags: [String] + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + + + +--- + +**99. [496. Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/496.%20Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack +- use stack to hold all elements + - keep poping if `stack.peek() < num` + - use map to record (top, num) +- time O(n), run through base once and sub-sequence once +- space O(n), stack, map + +#### HashMap +- O(n) space, O(n^2) time worst case + + + +--- + +**100. [717. 1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/717.%201-bit%20and%202-bit%20Characters.java)** Level: Easy Tags: [Array] + +理解题目: +1. single-bit always starts with '0', two-bits always start with '1'. +1. Therefore there is ONLY 1 way to reach end. + +#### 方法1 +Greedy. +从第一个bit开始: 如果 % 2 == 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. + + + +--- + +**101. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**102. [977. Squares of a Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/977.%20Squares%20of%20a%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +#### Two Pointers +- negative index i, positive index j + + + +--- + +**103. [824. Goat Latin.java](https://github.com/awangdev/LintCode/blob/master/Java/824.%20Goat%20Latin.java)** Level: Easy Tags: [Basic Implementation, String] + + + + +--- + +**104. [405. Convert a Number to Hexadecimal.java](https://github.com/awangdev/LintCode/blob/master/Java/405.%20Convert%20a%20Number%20to%20Hexadecimal.java)** Level: Easy Tags: [Bit Manipulation] + +#### Unsigned Shift, Mask +- Move pointer: move digit after process 4 bits. + - `>>>` Unsigned right shift + - always fills 0 irrespective of the sign of the number +- Mas: `num & 0xf` = `num & 15` + + + +--- + +**105. [509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.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. + + + + +--- + +**106. [136. Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/136.%20Single%20Number.java)** Level: Easy Tags: [Bit Manipulation, Hash Table] + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + + + +--- + +**107. [257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%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 +- time: visit all n nodes +- space: to hold all paths, O(nlogn) + - O((n-1)/2) = O(n) nodes at leaf + - O(logn) depth + +#### 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 + + + + +--- + +**108. [543. Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/543.%20Diameter%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 and CombinedPath +- Option1: Use local single path max & global combined max + - Since the local combined diameter is used for comparision, but not used for specific calculation + - calculate path length (diameter), understand: + - for single path: child single path value + 1 (curr node) + - for combined path including curr node: left child single + right child path +- Option2: record local combined and single path for each iteration + - `int[]{combinedPath, singlePath}`; + - single path: pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; + - combined path `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`, find max from: + - 1) complete left child combined path + - 2) complete right child combined path + - 3) combined path with curr root + - Note: we treat a single node itself with diameter of 1, so we want to -1 in final result + - problem statement wants the path length (not # of nodes or depth) + + + +--- + +**109. [67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%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 + +#### reverse +- Reverse string -> Convert to Integer List, add up -> Convert back to string +- pointer 从前向后, 所以只需要 1个pointer. +- 操作复杂, 如上, 证明可以解决. 没必要reverse. + +#### Incorrect: convert to Integer +把binary换成数字作加法. 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**110. [557. Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/557.%20Reverse%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就好了 + + + +--- + +**111. [203. Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/203.%20Remove%20Linked%20List%20Elements.java)** Level: Easy Tags: [Linked List] + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + + + +--- + +**112. [266. Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/266.%20Palindrome%20Permutation.java)** Level: Easy Tags: [Hash Table] + + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count char occurrance + - 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assume lower case letter. 应该至少是所有ASCII code + + + +--- + +**113. [339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS, NestedInteger] + + +给一串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. + - 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) + +#### BFS +- bfs, queue, 处理queue.size() for a level +- use a level variable to track levels +- slower since it uses extra space, worst case O(n) of all items + + + +--- + +**114. [189. Rotate Array.java](https://github.com/awangdev/LintCode/blob/master/Java/189.%20Rotate%20Array.java)** Level: Easy Tags: [Array, Rotation] + +#### Rotate array in place +- rotate all +- rotate 2 sides: < k or >= + + +#### Rotate by buffer the k array + + + +--- + +**115. [119. Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/119.%20Pascal's%20Triangle%20II.java)** Level: Easy Tags: [Array, Basic Implementation] + + +简单处理 list. code is very similar to Pascal triangle I. + +- 注意 `list = Arrays.asList(x, y, z ...)` 给fixed-size list, 不能直接 list.add(). +- Use `new ArrayList<>(Arrays.asList(...))` to wrap it up. + + + + +--- + +**116. [206. Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/206.%20Reverse%20Linked%20List.java)** Level: Easy Tags: [Linked List] + +#### Iterative +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + +#### Recursive with a helper function +- source node: head +- target node: new head + + + +--- + +**117. [168. Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/168.%20Excel%20Sheet%20Column%20Title.java)** Level: Easy Tags: [Math] + + +#### 基本换算 +- 26位, 像10位一样去思考 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + + + +--- + +**118. [104. Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/104.%20Maximum%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 + + + +--- + +**119. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**120. [443. String Compression.java](https://github.com/awangdev/LintCode/blob/master/Java/443.%20String%20Compression.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**121. [844. Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/844.%20Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + +#### Method1: Two pointers to backtack from end of string +- time: O(n) +- space: O(1) + +#### Method2: Stack +- need to remove entity just added +- use stack to hold array content; pop if # is found + + + +--- + +**122. [9. Palindrome Number.java](https://github.com/awangdev/LintCode/blob/master/Java/9.%20Palindrome%20Number.java)** Level: Easy Tags: [Math] + +#### Reverse half of the number +- build reversed integer 直到midpoint, where x <= reverse +- 如果input双数: x == reverse +- 如果input单数 (而且x>reverse): x == reverse/10 + +#### Consider palindrome +- optionA: compare digit by digit +- optionB: reverse half of the string/int, and compare with other half. + + + + + + +--- + +**123. [771. Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/771.%20Jewels%20and%20Stones.java)** Level: Easy Tags: [Hash Table] + + +- 给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 +- Basic HashSet + + + +--- + +**124. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**125. [680. Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/680.%20Valid%20Palindrome%20II.java)** Level: Easy Tags: [String] + +#### Palindrome String +- delete an index: 有两种情况 +- 用一个boolean parameter来表现state. 如果有其他status, state可以变成 String/enum + + + +--- + +**126. [70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%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]往往是有特殊状态的. 这里, dp[0]: 1种方式可以原地不动 + - dp[1]=1, 1种方式到达index=1, + - 之后的`dp[2] = dp[0]+dp[1]`: 就是dp[0]的走法 or dp[1]的走法 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**127. [747. Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/747.%20Largest%20Number%20At%20Least%20Twice%20of%20Others.java)** Level: Easy Tags: [Array] + + +多种简单操作: +- O(n) solution: 找最大值, 和第二大的值, 看是否符合题意, 就行了. +- O(2n) 最简单方法: 可以loop 两遍: 找最值; 作比较. +- O(2n) 举反例: 有一个不满足, 就够反对这个'at least twice of alllll others'. + + + +--- + +**128. [561. Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/561.%20Array%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发现规律: 升序排列会让 `高位的min(pair)` 最大化, 于是`一言不合先排列` +- 找到排列取单数位的规律,再考虑负数和正数的相同规律,即可找到排列求解的方法。 +- sort, O(nlogn) + + + + +--- + +**129. [387. First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/387.%20First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +#### Count appearance with int[256] +- 按照题意, 找到第一个 first index == last index的字母. + +#### Count appearance with hashmap (more scalable) +- 用hashmap存字母的index, 有些重复字母的index就会是个list. +- 找到单一index, 结合成list, sort, return list.get(0) +- slow due + + + +--- + +**130. [345. Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/345.%20Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i [mid+1], on right slope, end = mid +- init: start == 1, end = n - 2; + + + +--- + +**135. [110. Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/110.%20Balanced%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + +给一个binary tree, 看是否是height-balanced + +#### DFS with end state +- 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 above solution, but cost more traversal efforts +- 试图计算所有情况 + + + +--- + +**136. [246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math, Two Pointers] + + +根据题意枚举, 再根据规则basic implementation + +#### HashTable + Two Pointer +- compare left/right + +#### Alter input +- flip number (6 and 9), and then reverse the string, see if the string is the same. +- takes more + + + + +--- + +**137. [100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### Method1: DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### Method2: BFS with 2 queues +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**138. [88. Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素: 从尾部,是大数字优先排末尾的. +- Deal with remaining: + - When A values are used up, put remian of B into it + - When B values are finished, there is nothing todo. The remain of A is already in place. + + + +--- + +**139. [112. Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/112.%20Path%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. + + + + +--- + +**140. [463. Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/463.%20Island%20Perimeter.java)** Level: Easy Tags: [Hash Table] + +#### Brutle, Count Blocks and Walls +- 每个格子 +4 个墙; +- 每个shared的墙要减去: 从每个island走去另外一个, 都-1 (最终没面墙, -2) + +#### Hash Table +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么就可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不大,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + + + +--- + +**141. [170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)** Level: Easy Tags: [Design, Hash Table, Memoization] + + +#### Hash Table, Memo +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +- time: O(n), loop over all elements in map +- space: O(n), store all elements in map & memoization set + + + +--- + +**142. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**143. [14. Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/14.%20Longest%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) + + + +--- + +**144. [243. Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/243.%20Shortest%20Word%20Distance.java)** Level: Easy Tags: [Array, Two Pointers] + + + +#### Two Pointers +- Use 2 pointers to record **most recent** pos of word1 and word2 + - move pointer i [0 ~ n) and keep refreshing pos1 and pos2 + - both pos1 and pos2 will be as adjacent as possible since they both moving towards same direction +- keep recalculating best distance when either word is matched +- 而同一时间,只需要计算一个最近的curr distance: greedy不断变更A/B index, 做比较 + + + + +--- + +**145. [414. Third Maximum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/414.%20Third%20Maximum%20Number.java)** Level: Easy Tags: [Array, PriorityQueue] + +#### pq +- 注意special case: `when less than 3 items, return maximum` +- PQ是natural order: remain peek() will be the 3rd maximum + + + + +--- + +**146. [20. Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/20.%20Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +#### Stack +- 剥皮过程。解铃还须系铃人 +- 左边的外皮'{['在stack底部 +- 右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**147. [893. Groups of Special-Equivalent Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/893.%20Groups%20of%20Special-Equivalent%20Strings.java)** Level: Easy Tags: [Basic Implementation, String] + +Mark # of characters can be useful to print string signature + + + +--- + +**148. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**149. [234. Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/234.%20Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单: 两边回溯相等. However: + - 1) cannot random access index on linkded list + - 2) cannot reverse iterating linked list +- solution: reverse linked list: 遍历接开头 + - 1) 用快慢指正找到mid point + - 2) reverse 2nd half + - 3) compare leftList and rightList +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + + + +--- + +**150. [202. Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/202.%20Happy%20Number.java)** Level: Easy Tags: [Hash Table, Math] + + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + + + +--- + +**151. [69. Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/69.%20Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + +#### sqrt(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 + + + +--- + +**152. [876. Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/876.%20Middle%20of%20Linked%20List.java)** Level: Easy Tags: [Linked List] + +找Linked List的中间node + +#### 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + + + +--- + +**153. [219. Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/219.%20Contains%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 +- Time O(nm), m = # of duplicates. 太慢 +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k + + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**154. [205. Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/205.%20Isomorphic%20Strings.java)** Level: Easy Tags: [Hash Table] + + +#### HashMap +- check 2 failture cases: + - same key, value not matching + - two key maps to same value + + + +--- + +**155. [346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison +- Note: if we it is calculate moving-window-product, better to use deque :) +- Sliding window? + - It has the spirit of slinding window: 1) maintain a range; 2) check range size `if (queue.size() > size)` + - Though, the solution must use a data structure to store data; it is not the traditional sliding window type of `left/right` pointer problem + + + +--- + +**156. [938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)** Level: Easy Tags: [BST, Recursion, Tree] + +#### DFS based on BST rules +- sum up the matching L & R + - Find (L,R) on left child + - Find (L,R) on right child + - Find (L,R) covering the root node +- space O(n), worst case O(logn), height of dfs. +- time O(n) to find all nodes between (L, R) + +#### Iterative +- Using stack, or queue, list: any data structure (we are not doing ordered search) +- space O(n) +- time O(n) + + + +--- + +**157. [242. Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/242.%20Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +#### int[26] + +#### HashMap + + + +--- + +**158. [217. Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/217.%20Contains%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 + + + +--- + +**159. [796. Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/796.%20Rotate%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 不变 + + + +--- + +**160. [1041. Robot Bounded In Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/1041.%20Robot%20Bounded%20In%20Circle.java)** Level: Easy Tags: [String] + +简单的character checking. 各个方向, 加加减减. + + + +--- + +**161. [157. Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/157.%20Read%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)` +- Edge Case: + - When there is not enough space to the result buffer, `i + 3 > n`, then only copy what we can: `Math.min(n - i, count)` + - `count < 4` meaning there is not enough content to read, break + + + +--- + +**162. [121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### min[n] +- 每天都就交易价格,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 dont need to buyin at 5, 3, 6, 4 later on; + - only need to sell on higher prices. + + + +--- + +**163. [605. Can Place Flowers.java](https://github.com/awangdev/LintCode/blob/master/Java/605.%20Can%20Place%20Flowers.java)** Level: Easy Tags: [Array, Greedy] + + +#### Array +- just check flowerbed[i-1] & flowerbed[i+1] and count + + + +--- + +**164. [1. Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1.%20Two%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. + + + + +--- + +**165. [118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)** Level: Easy Tags: [Array, Basic Implementation, List] + + + + +--- + +**166. [283. Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/283.%20Move%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 + + + +--- + +**167. [1033. Moving Stones Until Consecutive.java](https://github.com/awangdev/LintCode/blob/master/Java/1033.%20Moving%20Stones%20Until%20Consecutive.java)** Level: Easy Tags: [Basic Implementation, Sort] + + +#### Analyze to understand +- put 3 elements into array, sort and follow below rules: +- min: + - if 3 elements consecutive, 0 move. + - if only 1 pair of the two elemnets consecutive or if they have 1 slot in between, it needs exactly 1 move + - otherwise, at most 2 moves +- max: # of open slots between them (high - low + 1) - n, where n = 3 +- Follow up: `1040. Moving Stones Until Consecutive` is more interesting with special rulese (cannot move to `ending spot`), and it uses sliding window concept + + + +--- + +**168. [125. Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/125.%20Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Two Pointers +- Time O(n), Space O(1). +- 普通方法, 两边check, 速度相比较regular expression更快. leetcode 4ms. +- Use helper functions. + +#### 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/ + + + +--- + +**169. [160. Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/160.%20Intersection%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 + + + +--- + +**170. [724. Find Pivot Index.java](https://github.com/awangdev/LintCode/blob/master/Java/724.%20Find%20Pivot%20Index.java)** Level: Easy Tags: [Array, PreSum] + + +#### PreSum +- want to find `nums[i - 1] == nums[n - 1] - nums[i]`, given: + - preSum[i], sum from [0, i] inclusive + - preSum[j] - preSum[i] = [i+1, j] inclusive +- O(n) to build preSum +- O(n) to find pivot + + + +--- + +**171. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**172. [720. Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/720.%20Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序(lexicographically), 排序以后才能逐个看是否partial string已经存在 + - 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 + - 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: + - 长 string 排在前; + - 相等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) + +#### 从最长的word开始做 +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**173. [760. Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/760.%20Find%20Anagram%20Mappings.java)** Level: Easy Tags: [Hash Table] + + +- HashMap 存index list +- 遍历一遍数组A, 列举出所有元素 + + + +--- + +**174. [1170. Compare Strings by Frequency of the Smallest Character.java](https://github.com/awangdev/LintCode/blob/master/Java/1170.%20Compare%20Strings%20by%20Frequency%20of%20the%20Smallest%20Character.java)** Level: Easy Tags: [Array, String] + + +#### Method1: letter frequency map, kinda bucket sort +- Goal: find word count that fits into `f(queries[i]) < f(W)` +- What if: we can store the f(W) as preSum, then goal: `rst[i] = preSum[end] - preSum[queryWordCount]` + - count(W) and store in count[i] + - calc preSum + - processs queries array +- kinda bucket sort: + - 1) we know the boundary of the word length, so we can create `bucket` + - 2) `function count(w)` can produce a value that sort a word into a specific bucket slot + - extend: the bucket can store keys that links back to the word (if there are follow up questions) +- time: O(m + n) +- space: O(m + n) + +#### Method2: No brain solution, basic impl based on the desc, w/o search. +- time: + - O(nm) to count all words, O(nlogn) to sort the wordCount + - O(nm) to to count all queries + - O(n^2) to perform the match +- space: O(n) + + + +--- + +**175. [94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Method1: DFS +- option1: dfs + rst list to carry results +- option2: Divide and Conquer, 在自己的基础上recursive, 不用helper function +- O(n) time + +#### Method2: Iterative, Stack inorder traversal +- 1) Add root.leftPath all the way to leaf, 2) process curr 3) Move to right if applicable 4) add all right.leftPath +- O(n) time, O(h) space + + + + +--- + +**176. [278. First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/278.%20First%20Bad%20Version.java)** Level: Easy Tags: [Binary Search] + + +#### Method1: Check is-NOT-BadVersion +- simply binary Search: if not bad, assign `start = mid+1` + +#### Method2: Check ifBadVersion +- 根据isBadVersion的性质,判断还如何end=mid or start=mid. +- A bit more code to handle + + + +--- + +**177. [101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### Method1: DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Method2: interative with queue +- put left or right children in pair + +#### Method3: iterative with Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**178. [13. Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/13.%20Roman%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 + +#### use map to store combinations + + + + +--- + +**179. [671. Second Minimum Node In a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/671.%20Second%20Minimum%20Node%20In%20a%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +#### BFS +- min tree: parent node is the min of left/right child +- BFS to traverse the tree and find 1st non-root smallest val +- Improvement area: when `node.val >= nextMin`, no need to dive into node children since it is a min Tree. + +#### DFS +- Find left and right val: + - if left/right equals root.val, that means the left or right sub children could have larger number + - Therefore DFS into left or right +- compare and return min(left, right) + + + +--- + +**180. [235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的 path +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Beofre lasts common ancestor is found: p and q should follow same search pattern: + - compare with root, then dfs(left) or dfs(right) + - util p and q fall on two sides of root, then return root + - 非常巧妙, 但是也比较局限; 稍微变条件, 就很难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(logn) extra space: recursive stack space +- O(logn) time + + + +--- + +**181. [256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, depends on the color of dp[n-1] + - 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- Need to have status with dp array: int[index][color status] + - dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. + - dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- time: O(nm), m = # of colors +- space: O(nm) + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 +- space:O(1) + + + +--- + + + + + + + +## NA (1) + + + + + + +## Medium (310) +**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. [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 + + + +--- + +**2. [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 + + + +--- + +**3. [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. + + + +--- + +**4. [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, 而不考虑是哪个. + + + + + + +--- + +**5. [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. + + + +--- + +**6. [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. + + + +--- + +**7. [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 + + + +--- + +**8. [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) + + + +--- + +**9. [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了 + + + +--- + +**10. [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 + + + + +--- + +**11. [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的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**12. [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), 排序 + + + +--- + +**13. [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);` + + + +--- + +**14. [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 + + + +--- + +**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. [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,要乘一下。 + + + + +--- + +**17. [Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)** Level: Medium Tags: [Bit Manipulation] + + +给出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. + + + + +--- + +**18. [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 + + + +--- + +**19. [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 一样) + + + +--- + +**20. [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拿出来就好了。 + + + +--- + +**21. [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 + + + +--- + +**22. [Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)** Level: Medium Tags: [DP] + +这个DP有点诡异. 需要斟酌。 +NOT DONE YET + + +--- + +**23. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**24. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + + +给一串点, 找是否有一个所有点中间的, 跟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) + + + +--- + +**25. [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) + + + +--- + +**26. [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 + + + +--- + +**27. [Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)** Level: Medium Tags: [Union Find] + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + + + +--- + +**28. [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 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**29. [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) + + + +--- + +**30. [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开头 + + + +--- + +**31. [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()` + + + +--- + +**32. [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 + + + + +--- + +**33. [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. + + + +--- + +**34. [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 + + + +--- + +**35. [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了。 + + + +--- + +**36. [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 + + + + +--- + +**37. [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 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**38. [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 + + + +--- + +**39. [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] + + + +--- + +**40. [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许多。 + + + + +--- + +**41. [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) + + + +--- + +**42. [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 + + + +--- + +**43. [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] + + +给一串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 + + + +--- + +**44. [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的头。 + + + + + +--- + +**45. [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). + + + + +--- + +**46. [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 + + + +--- + +**47. [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) + + + + +--- + +**48. [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. + + + +--- + +**49. [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,可惜错了。 + + + + +--- + +**50. [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) + + + +--- + +**51. [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. + + + +--- + +**52. [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, 换位子 + + + +--- + +**53. [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. + + + +--- + +**54. [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开始选。 + + + +--- + +**55. [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/ + + + +--- + +**56. [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 是理所当然的 + + + +--- + +**57. [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, 碰壁的时候就回头走。 + + + +--- + +**58. [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. + + + +--- + +**59. [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的用法: |, & + + + + +--- + +**60. [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 + + + +--- + +**61. [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. + + + + +--- + +**62. [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 +- 都是基本操作, 概念实现 + + + +--- + +**63. [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 + + + +--- + +**64. [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. + + + +--- + +**65. [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 + + + +--- + +**66. [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] + + +给一串 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) + + + +--- + +**67. [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) + + + +--- + +**68. [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加上去就好. + + + +--- + +**69. [Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)** Level: Medium Tags: [String] + + + +--- + +**70. [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? + + + +--- + +**71. [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]。 然后把三段链接在一起。 + + + + +--- + +**72. [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 + + + +--- + +**73. [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' + + + + +--- + +**74. [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的益处。 + + +--- + +**75. [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来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**76. [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一下最后一个词 + + + + +--- + +**77. [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会出问题 + + + +--- + +**78. [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. + + + +--- + +**79. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**80. [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) + + + +--- + +**81. [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的边框限制好,中间就全部遍历了。 + + + +--- + +**82. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**83. [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) + + + +--- + +**84. [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了 + + + +--- + +**85. [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的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**86. [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 + + + +--- + +**87. [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 + + + +--- + +**88. [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 + + + +--- + +**89. [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[] + + + +--- + +**90. [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链接起来。 + + + +--- + +**91. [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 + + + +--- + +**92. [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--. + + + +--- + +**93. [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) + + +--- + +**94. [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] + + +#### 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. + + + +--- + +**95. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + +--- + +**96. [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? + + + +--- + +**97. [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. + + + + +--- + +**98. [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 + + + + +--- + +**99. [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 + + + +--- + +**100. [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` + + + +--- + +**101. [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维护清楚就行。 + + +--- + +**102. [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. + + + +--- + +**103. [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 + + + +--- + +**104. [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 + + + +--- + +**105. [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本身. + + + +--- + +**106. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**107. [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; + + + +--- + +**108. [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) + + + +--- + +**109. [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. + + + + +--- + +**110. [Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)** Level: Medium Tags: [Hash Table] + + +给一面墙, 每一行是一行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 + + + +--- + +**111. [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 太慢, 不可行. + + + +--- + +**112. [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. + + + + +--- + +**113. [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) + + + + +--- + +**114. [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看出有直接关系. + + + +--- + +**115. [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看起来更容易理解. + + + +--- + +**116. [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), 就不写了 + + + +--- + +**117. [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在某个点小了,加进去当下这个空隙。 + + + +--- + +**118. [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. + + + +--- + +**119. [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) + + + +--- + +**120. [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 + + + +--- + +**121. [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一下。 + + + +--- + +**122. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**123. [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的状态, 然后看最后一步. + + + +--- + +**124. [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] + + +给一个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, 永远在左边上下上下。 + + + +--- + +**125. [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[] 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. + + + +--- + +**127. [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] + + +给一个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 + + + +--- + +**128. [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] + + +给一个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) + + + + +--- + +**129. [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)))` + + + +--- + +**130. [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] + + +跟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] + + + +--- + +**131. [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 的情况. + + +--- + +**132. [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]) + + + +--- + +**133. [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 + + + +--- + +**134. [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` + + + +--- + +**135. [Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)** Level: Medium Tags: [Union Find] + +Lint还不能跑, 全部按照题意和答案document的. + + + +--- + +**136. [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. 这个用法要记住吧, 没别的捷径. + + + +--- + +**137. [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走过的地方 + + + +--- + +**138. [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 + + + +--- + +**139. [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一下。 + + + +--- + +**140. [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 + + + +--- + +**141. [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 + + + +--- + +**142. [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]` + + + +--- + +**143. [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 + + + +--- + +**144. [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. + +可能有简化的余地, 代码有点太长. +比如试一试不用额外空间? + + + +--- + +**145. [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; + + + +--- + +**146. [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了 + + + +--- + +**147. [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) + + + +--- + +**148. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + +// 如何想到从中间initialize + + + +--- + +**149. [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 原题有一点点不一样. + + + + +--- + +**150. [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 + + + +--- + +**151. [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? + + + + +--- + +**152. [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。 + + + +--- + +**153. [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] + + +给一个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才可以施行. + + + +--- + +**154. [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 + + + +--- + +**155. [[lint]. Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Merge%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 + + + +--- + +**156. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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, 自底向上建立起的。 + + + +--- + +**157. [[lint]. Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array, Lint] + + + + +--- + +**158. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**159. [[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)** Level: Medium Tags: [Array, Hash Table, Lint] + + +把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. + + + + +--- + +**160. [[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)** Level: Medium Tags: [Array, Lint, Two Pointers] + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**161. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**162. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**163. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**164. [[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)** Level: Medium Tags: [Lint, 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) + + + +--- + +**165. [[tool]. UnionFind.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20UnionFind.java)** Level: Medium Tags: [Lint, Union Find] + + +#### Method1: Union Find with Array +- union(), find() +- Path Compresion: store skip father after found, which makes find O(1) + +#### Method2: Union Find with HashMap + + + + + +--- + +**166. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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 + + + +--- + +**167. [102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### Method1: BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### Method2: DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**168. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**169. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**170. [360. Sort Transformed Array.java](https://github.com/awangdev/LintCode/blob/master/Java/360.%20Sort%20Transformed%20Array.java)** Level: Medium Tags: [Math, Two Pointers] + + +#### Two Pointers, Math +- Being able to analys the a*x^2 + b*x graph and find the `peak` or `valley` +- Math basics: x^2 dominates the overall curve so it is up to a to determine: + - `valley`: if a < 0, both sides will be small and center will be large. Prioritize larger value. + - `peak`: if a > 0, center will be small and both sides will be large. Prioritize smaller value. + - starting index being 0 or n-1, is driven by `a` + + + +--- + +**171. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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 + + + +--- + +**172. [236. Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/236.%20Lowest%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 + - Base Case: 当root == null, p or q is found (`root == p || root == q`),那么就return the root as LCA + - 三种情况: + - 1. leftLCA and rightLCA all found: `each path has found one of p and q node as LCA`. Therefore, curr root is the lowest ancestor + - 2. One of leftLCA and rightLCA is found: return whichever one found + - 3. both LCAs are null, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time O(n), stack space O(n) + + + +--- + +**173. [1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)** Level: Medium Tags: [Array, Greedy, Permutation] + + +#### Analyze Permutation behavior +- concept similar to `31. Next Permutation` +- 1) first pass: find the one that is in incorrect order +- 2) second pass: find the right spot to swap + + + +--- + +**174. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**175. [986. Interval List Intersections.java](https://github.com/awangdev/LintCode/blob/master/Java/986.%20Interval%20List%20Intersections.java)** Level: Medium Tags: [Two Pointers] + + + + +#### Method1: Merge Interval +- There can be 1 overlapping on any interval, calculate the inner intersection: lo(A[i][0], B[j][0]), hi(A[i][1], B[j][1]) + - if low <= hi, a valid intersection exist; add + - also, if A[i][1] < B[j][1]; that is A[i].end < B[j].end, then i++; otherwise j++ + - because the further-away `end` has been used, so move on. +- O(n) + +#### Method2: Sweep line +- code is much more complex (pq, Point, process code... etc) than method1 +- we can use point to track open/close, also want to specify if point belongs to A/B +- mark 2 global parameters: aOpen, bOpen. + - process when A/B close, record if (aOpen, bOpen) has overlaop + - clear up corresponding global parameter after A/B closes +- sort all pointers in priority queue by index +- Point: {boolean isOpen; int index} +- process the queue and remember to clean up all items on same index +- time: O(nlogn) +- space: O(n) + + + + +--- + +**176. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**177. [80.Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/80.Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Basic +- 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其实就足够. + +#### Follow up: k duplicates, Two Pointers +- when index i and i-1 are diff, use count=1 to start +- in while loop, keep count++ until count==k +- reset when next diff comes in + + + +--- + +**178. [5. Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/5.%20Longest%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)? + +#### Method1: DP of interval +- Very similar to `216. Longest Palindromic Subsequence`, but this problem requires solid substring(i+1, j-1) to be palindromic +- Similarly: process i = n-1, from end so [i + 1, j] is always ready to consume +- boolean dp[i][j] to mark range (i, j) as palindrome or not. +- 在计算 dp[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- time: O(n^2) dp +- space: O(n^2) + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + + + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**179. [1007. Minimum Domino Rotations For Equal Row.java](https://github.com/awangdev/LintCode/blob/master/Java/1007.%20Minimum%20Domino%20Rotations%20For%20Equal%20Row.java)** Level: Medium Tags: [Array, Greedy] + + + +#### Method1: Count all occurrance, and count on overlap indexes +- when there is a value that can cover entire row of size n + - it must be: `n = countA[i] + countB[i] - overlap[i]` +- Code easy to write and read +- time: O(n) +- space: O(1) + +#### Method2: Negative count +- Observation: if A[0] works, no need to check B[0]. +- Because if both A[0] and B[0] exist in all dominoes, + - when you swap A[0] in a whole row, + - you will swap B[0] in a whole at the same time. + - The result of trying A[0] and B[0] will be the same. +- time: O(n) +- space: O(1) + +#### Method3: positive count Match +- there should exist 1 numbers, that can appear in (A[i], B[i]). +- failure case: there exist at least 1 index, that does not have the common number +- maximum case: there can be 2 numbers, that both will make it work. +- findCommon2, and count them: + - set.add(A[0], A[B]), + - if any new one does not exist in set, remove it from set + - if set is empty() , return -1 +- use the 2 numbers from set to do a sweep and count in A, O(n), return the less appearance one. +- time: O(n) +- space: O(1) + + + +--- + +**180. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**181. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**182. [429. N-ary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/429.%20N-ary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Tree] + + +#### BFS +- use queue to hold each level. O(n) + + + +--- + +**183. [275. H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/275.%20H-Index%20II.java)** Level: Medium Tags: [Binary Search] + + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +Aim to find the lowest index mid, which maximize h = n - mid + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + + + +--- + +**184. [694. Number of Distinct Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/694.%20Number%20of%20Distinct%20Islands.java)** Level: Medium Tags: [DFS, Hash Table] + + +#### DFS + HashSet +- DFS can find # of island, just like `200.Number of Islands`, aim to count total +- We need to map same-shap land + - One approach: print the **footprint** starting from coordinate (0, 0) + - Another approach: print the actual island in its boundary, like a QR code. (too hard to code, skip) +- Footprint approach: + - 1. always assume a newly found islands starts from (0, 0) + - 2. take 4 direction from init pos and keep printing the footprint + - 3. Since we always visit nodes from top->right->nextrow, we always visit top-left cornor of a new island, and the footprint will be identical + - Otherwises, if needed, we can sort the footprint and output the hash +- time: O(n), visit all +- space: O(n), store footprint, and dfs stacks worst case visit all nodes + + + +--- + +**185. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**186. [199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%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 +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n nodes in final results + + +#### DFS +- Use Map to override the result at each level +- dfs: + - dfs(node.left) and then dfs(node.right) because we want to log right side last +- record global max depth for iteration purpose +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n stacks (and n nodes in final results) + + + + +--- + +**187. [259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +1. Similar to 15. 3Sum, but simpler. +1. 只需要count triplet, 但是不需要save triplet, 而且还不需要handle duplicated triplets +1. 发现start, end满足条件时候,(end - start)就是所有 sum target, 那么就end-- +1. 两层循环, O(n2) + + + +--- + +**188. [1008. Construct Binary Search Tree from Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/1008.%20Construct%20Binary%20Search%20Tree%20from%20Preorder%20Traversal.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top Down DFS +- This approach highly relies on the preorder rules + - we can use validation rules to navigate throug hteh preorder array + - use a global index +- time: O(n) + + + + +--- + +**189. [151. Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/151.%20Reverse%20Words%20in%20a%20String.java)** Level: Medium Tags: [String] + + +#### Method1: Split string by space, then flip +- Option1: With `s.split(" ")`: No brain, and super fast +- Option2: With `s.split("\\s+")`, it skips space, but slow. Use sb.insert(0, xxx) +- trim() output +- Time, Space: O(n) + +#### Method2: Flip entire, then individual, two pointer +- flip entire string, then flip each individual string +- Time, Space: O(n) + + + +--- + +**190. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**191. [31. Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/31.%20Next%20Permutation.java)** Level: Medium Tags: [Array, Permutation] + + +#### Permutation Behavior +- Great write up: https://leetcode.com/problems/next-permutation/solution/ +- next lexicographically permutation: `smallest` but `larger than curr` permutation: + - find first low point from right [low] + - find the slight larger [high] to swap with [low] + - make sure right side of low is eventually the smallest +- Analyze the use cases, to find next low permutation, 2 major steps: + - 1) Find `first low/drop candidate` from right + - 2) Find `first high where nums[high] > nums[low]` from right + - 3) swap(low, high). + - By now, [low, n-1] forms a greater permutation + - but it is not the smallest, because right side [low + 1, n - 1] is descending + - 4) reverse(low + 1, n-1) to create ascending slopt on right of low (smallest next lexicographically permutation) +- Corner case: if input array is decending (1st low not found), reverse it all together O(n) +- time: O(n) visit all indexes +- space: O(1) not using additional +- Similar question: `1053. Previous Permutation With One Swap` + + + + +--- + +**192. [518. Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/518.%20Coin%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 + + + +--- + +**193. [515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS +- faster than BFS, using less space if not couting final rst: stack size, O(logn) +- time: O(n), visit all + +#### Method2: BFS with queue +- loop over queue level and record max + + + + +--- + +**194. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**195. [1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph] + + + +#### BFS +- simply calc each level sum with BFS +- top-level is processed first, since we go from top level -> deeper level + - only update result if sum is truly > global MAX. + + + +--- + +**196. [221. Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/221.%20Maximal%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; +- init: 每个点都可能是边长1, 如果 matrix[i][j] == '1' +- Space, time O(mn) +- Rolling array: [i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + + +--- + +**197. [131. Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/131.%20Palindrome%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) + + + +--- + +**198. [222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, DFS, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### Method1: DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样 + - 如果一样, 直接 2 ^ h - 1就好 + - 不一样的话, 再DFS +- calculate `2^(h)`: 位运算, Math.pow(2, h) = 2 << (h - 1). 神奇! + - 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- time: O(n) visit all nodes on 1 side +- space: O(h) visit all nodes on 1 side + + +#### Method2: Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +#### Method3: Binary Search +- NOT DONE, TODO: https://leetcode.com/problems/count-complete-tree-nodes/solution/ + + + +--- + +**199. [398. Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/398.%20Random%20Pick%20Index.java)** Level: Medium Tags: [Reservior Sampling] + + +#### 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. +- probability theory: + - 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 + + + + +--- + +**200. [238. Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/238.%20Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 第一轮:PreProduct (跟preSum的感觉有点像) + - PreProduct[i] stores product from num[0] -> num[i-1] (skipping current num[i]) + - init preProduct[i] = 1, as base for product + - 错过一位操作: always `preProduct[i] *= carry;` and `carry *= nums[i]` +- 第二轮: 从右边乘起, 每次在index i, 收到的carry都是 `nums[i+1] *....* nums[end]` + - 第一轮的结果 * 第二轮的结果, 刚好在index i 缺少掉 nums[i]. 如题所愿. +- Time: O(n) + + + +--- + +**201. [1060. Missing Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1060.%20Missing%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Binary Search] + + +#### Binary Search +- total missing nums = nums[curr] - nums[0] - curr +- edge case: if k > total missing nums, then just add the diff from nums[end] +- otherwise, find this `missing count == k` in the nums using binary search +- After binary search: `start + 1 == end`: + - re-calculate `count = nums[start] - nums[0] - start;` + - output final num: `nums[start] + k - count;` +- Option1: always compare total missing nums count +- Option2: compare partial missing nums count (inspired by: https://leetcode.com/problems/missing-element-in-sorted-array/discuss/303444/Java-O(logN)-solution-Binary-Search) + + + + +--- + +**202. [1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)** Level: Medium Tags: [Bucket Sort, DP, Hash Table, Sort] + + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**203. [299. Bulls and Cows.java](https://github.com/awangdev/LintCode/blob/master/Java/299.%20Bulls%20and%20Cows.java)** Level: Medium Tags: [Hash Table] + + +#### Solution1: use int[10] to count frequency +1. check match chars +1. check unmatched chars by counting and offset their frequency + - count++ on secret chars: if secretCount is ever < 0 => `char g` has match, then cows++ + - count-- on guess chars: if guessCount is ever >0 => `char s` has match, then cows++ + +#### Solution2: Use hashmap to count +- Improvement: since all digit, use int[10] to count + + + +--- + +**204. [1219. Path with Maximum Gold.java](https://github.com/awangdev/LintCode/blob/master/Java/1219.%20Path%20with%20Maximum%20Gold.java)** Level: Medium Tags: [Backtracking, DFS] + + + +### DFS, Backtracking +- typical recursive visit all situation + + + + +--- + +**205. [62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +#### DP, 加法原理 +- 计数DP: 2 ways to reach (i,j): dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + - non-overlapping: `dp[i - 1][j]`, `dp[i][j - 1]` + - covers the only 2 possible way to reach (i,j) +- initialization: dp[i][0] = 1, dp[0][i] = 1 + - Of course, row i = 0, or col j = 0, there is only 1 way to reach +- time O(mn), space O(mn) + +##### 滚动数组 Rolling Array +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + +#### DFS + Memoization +- move from (0,0) towards (m, n) +- use Map as memoization technique + + + +--- + +**206. [1091. Shortest Path in Binary Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/1091.%20Shortest%20Path%20in%20Binary%20Matrix.java)** Level: Medium Tags: [BFS] + + + +#### BFS +- find shortest path using queue +- time/space: O(n^2), n = grid length +- why SKIP `boolean visited[i][j]`? after a position grid[i][j] is used: + - 1) the curr path will not return to (i, j) + - 2) other route that may eventually reach (i, j) need not to be recorded, + - because the other route is already longer than the curr path + - therefore, we just simply block the visited node by `grid[x][y] = 1` + - note: block it right after it is added to the queue, so other nodes at same level will not attempt this visited node. + + + +--- + +**207. [1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +- time: O(n), visit all nodes +- space: O(logn), height of the tree + +#### Method2: HashMap, DFS. +- traverse tree and create `map ` to fast O(1) removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- This approach requires a dfs build of parentMap + - it is same amount of efforts to do the regular dfs removal. + - not a good solution +- time: O(n) +- space: O(n) + + + +--- + +**208. [1249. Minimum Remove to Make Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1249.%20Minimum%20Remove%20to%20Make%20Valid%20Parentheses.java)** Level: Medium Tags: [Stack, String] + + +#### Stack +- Goal: remove extra '(' or ')' so it is valid. +- Forward thinking: use stack to track '(' and ')', then keep appending partial string to output +- Backward thinking: use stack to filter out false indexes, and remove them in the end + + + + +--- + +**209. [15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +#### sort array, for loop + two pointer +- 处理duplicate wthin triplets: + - 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. + - 如果是triplet内有重复, 用完start point, 移动到结尾. +- Note: + - 1. 找 value triplets, 多个结果。注意,并非找index。 + - 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + - 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 +- 时间 O(n^2), 两个nested loop + +#### For loop + 2Sum +- HashMap 2Sum. Remember to handle duplicates + - 1. For loop 挑个数字A + - 2. 2Sum 出一堆2个数字的结果 + - 3. Cross match 步骤1里面的A + + + +--- + +**210. [311. Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/311.%20Sparse%20Matrix%20Multiplication.java)** Level: Medium Tags: [Hash Table] + + +给两个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 + + + +--- + +**211. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + +**212. [55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%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 +- 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) +- space: O(1) + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (j + A[j] >= i), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- time: O(n^2) +- space: O(n) + + + + +--- + +**213. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**214. [875. Koko Eating Bananas.java](https://github.com/awangdev/LintCode/blob/master/Java/875.%20Koko%20Eating%20Bananas.java)** Level: Medium Tags: [Binary Search] + + + +#### Binary Search +- Bianry serach on the min/max value range +- The mid value is calcualted with helper function `calc(piples, k)` +- find celing: `count += (i - 1) / k + 1`, faster than `Math.ceil(i / k)` +- time: O(logm) to find the best velocity, assume total range is m; O(n) for each `calc` call + + + +--- + +**215. [19. Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/19.%20Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Two Pointer +- 1 end pointer to define the window based n steps +- 1 pre pointer to track the node before the targeting node +- when end reaches null, remove nth node: link pre and head.next + + + +--- + +**216. [134. Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/134.%20Gas%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 +- 而不是求: 最后点可否走完/最值/计数 + + + +--- + +**217. [1197. Minimum Knight Moves.java](https://github.com/awangdev/LintCode/blob/master/Java/1197.%20Minimum%20Knight%20Moves.java)** Level: Medium Tags: [BFS] + + +#### BFS +- `from starting point, find min steps to reach certain point`: think of BFS + - similar: shortest path, shortest distance +- bfs: minimum steps, enumerate the possible moves + - move closer to x or y (test 8 possible directions) + - add possible moves in queue +- use visited to cache visited coordinates +- time: O(8^n), # of BFS branches +- space: O(8^n), # of BFS branche nodes + + + +--- + +**218. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**219. [1306. Jump Game III.java](https://github.com/awangdev/LintCode/blob/master/Java/1306.%20Jump%20Game%20III.java)** Level: Medium Tags: [BFS, Graph] + + +### Method1: BFS +- Find possibility to reach certain point, we can BFS: faster to find shortest candidate +- use queue to hold left, right candidates +- use set to record visited + +### Method2: DFS +- attemp all nodes, use set to record visited. +- time: O(n) +- space: O(n) + + + +--- + +**220. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + +**221. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + +**222. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**223. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**224. [1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)** Level: Medium Tags: [Lock, Semaphore, Thread] + + +#### Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 + +#### Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +- semaphore: https://www.geeksforgeeks.org/semaphore-in-java/ + + + +--- + +**225. [973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)** Level: Medium Tags: [Divide and Conquer, Heap, Sort] + + +#### PriorityQueue +- Create customized Point{} class +- Sort by distance +- Maintain queue size <= K + +#### Divide and Conquer +- ?, select sort? + + + +--- + +**226. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + +**227. [567. Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/567.%20Permutation%20in%20String.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + +#### Method1: Sliding window with left/right Pointers +- Sliding window template: + - 1) Check right pointer and move right + - 2) Move left when necessary + - 3) Verify count == 0 & end state + - Note: normally 2) and 3) are in reversed order; this problem is a bit different +- This is efficient when the number of characters is not limited to 26, the runtime is still O(m + n) +- time: O(m + n), m = s1 length, n = s2 length +- space: O(k), k = # of possible chars, 26 in this case + +#### Method2: Two Pointer, but brutle verify freq count +- 如果做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) 这一步 +- time: (m + n) + - However, if # of possible chars is more than 26 + - For example, `k unique characters`, then the runtime will become: O(m + nk) + +- space: O(k), k = # of possible chars, 26 in this case + + + +--- + +**228. [369. Plus One Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/369.%20Plus%20One%20Linked%20List.java)** Level: Medium Tags: [Linked List] + + +#### Reverse to make significant digit at tail +- Need add from the back and calculate carry +- Reverse list, so insignificant digit at head; calculate carry +- Reverse back when output + + + +--- + +**229. [211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +#### Trie, prefix tree. +- Trie Structure: `boolean isEnd`, `HashMap children` + - trie.addWord: 没node就加,有node就移动 + - trie.search: 没node就return false,有node就移动 +- Alternatively, the hash can be `TrieNode[26]` a fixed size array when applicable + - I like map better for the simplicity to write (w/o converting char -> index) + + + + +--- + +**230. [43. Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/43.%20Multiply%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]. + - Loop over num1, each row num1[x] * num2, save to correct index (i + j + 1) + - Note: skip leading '0' during output, but do not delete string "0" + - time,space O(mn) +- Option1: Calculate carry on the fly + - index `curr = i + j + 1`, left index `left = curr - 1`, since we start calculation from end of the array. + - **we only touch right side of the array once**, so we can move the carry off from it, and carry to left index + - code is concise +- Option2: save product first without calculating carry + - save product in each int index + - calculate carry on rst[] and `sb.insert(0, c)` (since we start from end of rst) + - this is actaully faster than Option1, somehow. + +#### Reverse, calculate from index 0, and reverse back +- 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'; + + + +--- + +**231. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**232. [47. Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/47.%20Permutations%20II.java)** Level: Medium Tags: [Backtracking, DFS] + +space: O(n!) + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +Similar to 46. Permutations, but with dedup + +#### Backtracking +- Sort the list, and on same level, if last element is the same as curr, skip this recursive call +- time O(n!) + +#### 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]在重复时候, 不用重新记录. + + + +--- + +**233. [332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)** Level: Medium Tags: [Backtracking, DFS, Graph] + + +#### DFS with backtcking +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +- time: O(n^n). n = # of cities. worst case, each city has (n-1) edges and need to try all combinations +- space: O(n^2), there can at most be n * (n - 1) edges + + + +--- + +**234. [88. Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- Also most identical to `33. Search in Rotated Sorted Array`: + - find where nums[mid] lands by comparing to nums[start]. i.e., if nums[mid] < nums[start], on right half of the array + - when `nums[mid] == nums[start]`: duplicate. Shift by start++ +- the worst case of `nums[mid] == nums[start]` willl cause O(n), +- but if duplicate is not entire array, should be O(logn) + + + +--- + +**235. [39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + + + +--- + +**236. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**237. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**238. [1040. Moving Stones Until Consecutive II.java](https://github.com/awangdev/LintCode/blob/master/Java/1040.%20Moving%20Stones%20Until%20Consecutive%20II.java)** Level: Medium Tags: [Array, Sliding Window] + + + +#### Analyze to understand +- Make sure to sort array: we need to use the actual number range `A[j] - A[i]`, which requires the array to be sorted +- we want to form a new array where A[n-1] - A[0] + 1 == n; order does not matter but all slots need to be filled consecutivly +- max moves: https://leetcode.com/problems/moving-stones-until-consecutive-ii/discuss/289357/c%2B%2B-with-picture + - A interval will be automatically dropped between A[0] and A[1], if moving A[0] first + - Same, a interval between A[n-2] and A[n-1] will be dropped when moving A[n-1] first + - so largest possible move = firstItem + remaining range size - n items = 1 + (A[n-1] - A[1] + 1) - n = A[n-1] - A[1] -n + 2 + - or A[n-2] - A[0] - n + 2 +- min moves: `Sliding Window` + - use slinding window to assume a right pointer, to make sure A[right] - A[left] + 1 < n; otherwise, move left++ + - check # of included stones + - calculate remaining, which is remaining moves +- Handle min move edge case: + - Consecutive Array up to right = n - 1; need 2 moves to finish + + + +--- + +**239. [307. Range Sum Query - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/307.%20Range%20Sum%20Query%20-%20Mutable.java)** Level: Medium Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree, devide and conquer +- sample problem for segment tree +- build(), update(), rangeQuery() + - build and update are standard + - rangeQuery: handle the range split check +- Null leaf node handling: NO, ideally it will not encounter null leaf. + - in update/rangeQuery: when final state (`start==end`) is reached, the recursive call ends + - there is no way for any node to dive futher into null child. +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. + + + +--- + +**240. [319. Bulb Switcher.java](https://github.com/awangdev/LintCode/blob/master/Java/319.%20Bulb%20Switcher.java)** Level: Medium Tags: [Brainteaser, Math] + + +#### Brainteaser +- https://leetcode.com/problems/bulb-switcher/discuss/77104/Math-solution.. + +#### Brutle: +- if just impl, it take O(n^2): +- repating: some pos are toggled mutiple times: if we know total times, easy to determin each pos. +- loop over [2, n], count times on each index + + + +--- + +**241. [12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)** Level: Medium Tags: [Basic Implementation, Math, String] + + +#### String, Basic implementation +- Parse each digit based on rules +- 1) parse: analyze the situations + + +--- + +**242. [1267. Count Servers that Communicate.java](https://github.com/awangdev/LintCode/blob/master/Java/1267.%20Count%20Servers%20that%20Communicate.java)** Level: Medium Tags: [Array, Graph] + + +#### two axis array, cross-reference +- analyze problem, and realize we want to eliminate `isolated servers` +- count row[], count col[] +- cross-reference row[] and col[]: `row[i]==1 & col[j]==1` indicates a isolated server + +### DFS brutle +- Unfortunately, this problems checks unconnected items, so dfs needs to brutlely check entire row or column +- Only add if `vertical + horizontal count` > 1 +- time: O(mn) * O(m + n) + + + +--- + +**243. [427. Construct Quad Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/427.%20Construct%20Quad%20Tree.java)** Level: Medium Tags: [Tree] + + +#### Basic Impl +- build tree recursively by definition +- O(n^2) time and space due to single visit to all nodes + + +--- + +**244. [981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)** Level: Medium Tags: [Binary Search, Hash Table, TreeMap] + + +#### Method1: Binary Search +- use hash to store +- binary serach on list of values + +#### Method2: TreeMap +- use hash to store > +- treemap.floorKey(timestamp) finds the top item below certain timestamp + + + +--- + +**245. [1026. Maximum Difference Between Node and Ancestor.java](https://github.com/awangdev/LintCode/blob/master/Java/1026.%20Maximum%20Difference%20Between%20Node%20and%20Ancestor.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top-Down DFS +- cache parent max and min => produce current max and min +- pass the max and min to dfs +- compare and return the max of dfs(left), dfs(right) +- time: O(n) +- space: O(logn) +- easy to write, a bit hard to think of + +#### Method2: Bottom-up DFS +- pass up the local (min, max) as object `Val{max, min}` +- easy to think of, but more code to write + + + +--- + +**246. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**247. [380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + + +#### Hash Table, Swap when removing +- Map: `map, Lis: tracks `index->value` +- list maintain 用来 insert/remove/random operations. +- Remove: swap input valueIndex & tialIndex = list.size() -1. + - list.remove(object) 应该是要O(logn) 做一个search的. + - list.remove(list.size() - 1) is cheapter + + + +--- + +**248. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**249. [91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Method1: DP, Bottom-Up by calculating base case first +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- there can be 2 final states at dp[i]: single digit or double digit. + - validate if `single digit`, dp[i] += dp[i - 1]. Last 1 digit can be decoded. + - validate if `double digit`, dp[i] += dp[i - 2]. Last 2 digits can be decoded. +- note: + - get number from char: `c - '0'` + - validatae single digit != '0', 因为'0' 不在条件之中(A-Z) +- Option1: dp[i] as # ways to decode at index i, including letter i + - The validation is done on: 1) single digit i, or 2) double digit [i-1, i] +- Option2: Partition DP, dp[i] as # ways to decode for first i letters (excluding letter i) + - 定义`dp[i] = 前i个digits最多有多少种decode的方法`: new dp[n + 1]. + - dp[i] += dp[i - x], where x = 1, 2 + - The validation is done on: 1) single digit [i-1], or 2) double digit [i-2, i-1] + - Option2 is better in terms of clean coding. We assume `dp[0]=1` as 1 way to decode 0 digits. + - No need to specially handle length == 1, because it is covered later + - No need to manualy init the first 2-digit case as in Option1 + - Return of `dp[n]` is clean +- 引申: 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +#### Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 + - especially when i = n - 2, handle 2-digt edge case carefully: + - 1) check if two-digit range [i, i+1] is valid + - 2) check if single-digit [i] is valid; if so, += dfs(s, i + 1) +- memo[i]: # ways to decode from [i, n). init with `memo[i]=-1` + + + +--- + +**250. [145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Method1: DFS, Recursive +- trivial, 先加left recursively, 再加right recursively, 然后组成头部. +- Option1 w/o helper; option2 with dfs helper. + +#### Method2, Iterative, Stack +- Option1: reversely add to list +- 双stack的思想, 需要在图纸上画一画 + - 原本需要的顺序是: 先leftChild, rightChild, currNode. + - 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild + - 这样出来的结果是reverse的, 那么翻转一下就可以了. +- reverse add: `list.add(0, x)`; +- 利用stack的特点 + - 每次加element进stack的时候, 想要在 bottom/后process的, 先加 + - 想要下一轮立刻process的, 最后push进stack. +- Option2: regular sequence add to stack: add curr, right, left + - Use set to contain the processed children + - only process curr if its children is processed + + + + +--- + +**251. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + +**252. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**253. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + +**254. [103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + + +#### 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 + + + +--- + +**255. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**256. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**257. [64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%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(N) + - 1) 需要在同一个for loop里面完成initialization, 2) reuse dp[i][j] + - Reason: dp[i % 2][j] 在被计算出来的时候, 马上在下一轮会被用. 被覆盖前不用,就白算 + - Option3 below initialize dp outside of loop: 看起来固然简单, 但是不方便空间优化 + +#### DFS (top-down) Thinking process +- Enumerate the possible 2 paths: go right, go down +- sub problem: dfs(i+1,j), dfs(i,j+1); pick the min of the two +- memoization: after the path from a point (i,j) to end is computed, record memo[i][j] to avoid repatative calculation +- time: O(mn), only visite and record memo[i][j] once. +- space: O(mn) extrem case of m=100000, n = 2; the stack height is O(mn) + + +--- + +**258. [229. Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/229.%20Majority%20Element%20II.java)** Level: Medium Tags: [Array, Moore Voting] + + +#### Two counters, Moore Voting +1. Moore voting: vote可以加减, 一旦为零, 换下一个candidate, 之前抵消掉的算作清零. +1. 一个array里面, 最多也只有2个数字 出现次数大于2次, 所以用A/B表示. +1. count overall apperance at the end for the two items: countA, countB +1. save to result as valA, valB +1. 有点 moore voting的意思: + - 当count == 0的时候, reset + - 两个candidate A/B都不等, 那么countA--, countB-- +1. 最终重新计数, 然后比较出结局. +1. 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +#### Sort + count +- O(nlogN) + + + +--- + +**259. [2. Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/2.%20Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. 跟Add Binary的理解方式一模一样. + +#### Math, Linked List +- reverse order helps calculation + - add additional carry to end + - not same length: align on left +- traverse till both ends +- 遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + + + + +--- + +**260. [114. Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/114.%20Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### Method1: DFS return tail node +- DFS to return the tail of the flattened list +- Careful handling the null child. Choose one and both works: + - Option1: put non-null as right child and continue dfs + - Option2: put non-null as left child and continue dfs + +#### Method2: void DFS +- latten the tree, no extra space. + - 1. Set right node in buffer, and connect: `root.right = root.left`, DFS flatten(root.right) + - 3. 移花接木: traverse to tail of the current root.right and attach the buffer node. + - 3. flatten the remaining of buffer + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**261. [1004. Max Consecutive Ones III.java](https://github.com/awangdev/LintCode/blob/master/Java/1004.%20Max%20Consecutive%20Ones%20III.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + + +#### Sliding window + Left/Right Two Pointers +- https://leetcode.com/problems/max-consecutive-ones-iii/solution/ +- Start with DFS thought, but realize redundant calculations: + - we never need to flip 2 indexes [A], [C] from 0 -> 1, if there is a [B] in middle that is 0 too + - the flipped k zeroes must be consecutive too +- we can utilize two pointers to establish a window that captures k zeroes + - always expend right pointer; if seeing an zero, k-- + - note: `len = right - left + 1` is the ongoing max length + - when k < 0 (too many zeros), we need to slide the left side of the window to make sure: + - keep window len + - potentially do k++ when A[left]==0 +- goal: matain a max size of the window, until right == n +- return (right - left). at this moment, right == n, so no need to (right - left + 1) + + + +--- + +**262. [1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)** Level: Medium Tags: [Array, Hash Table, TreeMap] + + + +#### Hash Table, TreeMap; atomic save +- store efficiently: use List>. only preserve changed itemd +- if no match, find last modifed item based on snapId, use TreeMap.floorEntry + - map.floorEntry(id) return the item.key lower or equal to id +- Utilize a `buffer: Map` and perform atomic save + + + +--- + +**263. [304. Range Sum Query 2D - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/304.%20Range%20Sum%20Query%202D%20-%20Immutable.java)** Level: Medium Tags: [DP, PreSum] + + +#### Method1: rangeSum/caching +- build rangeSum[i][j]: square range sum from (0,0) to (i,j), O(mn) to init +- query: time O(1) + +#### Method2: presum/caching +- build rowPreSum[i][j]: row i sum from [0 ~ j], O(mn) to init +- callign takes O(m); space O(mn) + + + + +--- + +**264. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**265. [208. Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/208.%20Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- Trie Structure: + - trace the char to children node: Map + - boolean isEnd to indicate if there is string end with this node + - `TrieNode {boolean isEnd; Map children}`. +- No need to store letter c in TrieNode +- 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。 + + + + + +--- + +**266. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + + +--- + +**267. [430. Flatten a Multilevel Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/430.%20Flatten%20a%20Multilevel%20Doubly%20Linked%20List.java)** Level: Medium Tags: [DFS, Linked List] + + +#### DFS +- Depth-first: + - 1) process curr.child, return tailChild + - 2) connect tailChild.next = curr.next +- function: link(Node a, Node b); + + + +--- + +**268. [63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%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] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + + + +--- + +**269. [139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- Bottom-up, test simply case. Sequence DP. +- true/false problem, think about dp + - 子问题: 前i个字母, 是否可以有valid break + - check: 1) dp[j] && 2) `if substring(j, i) valid`, for all j = [0 ~ i] + - dp = new boolean[n + 1]; dp[0] = true; + - test: `dp[i] |= dp[j] == true && word[j, n] in dict`. + - Need iterate over i = [0 ~ n], also j = [0, i] + - When there is a way to make dp[i] == true, then break the [j ~ i] loop, move on to test dp[i++] +- Use set dict: `dict.contains()` +- Improvement: O(n) to figure out max length, so we can skip some substring[j~i] dict.contains() +- overall O(n^2) time since the double for loop + +#### DFS +- Top-Down, break into small problems: Check front subString, and put the rest substring into dfs to test +- Memoization: for tested failed substring, record and do NOT test them again. +- Same Improvement as in DP: use max/min length of dict words as boundary + + + +--- + +**270. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**271. [449. Serialize and Deserialize BST.java](https://github.com/awangdev/LintCode/blob/master/Java/449.%20Serialize%20and%20Deserialize%20BST.java)** Level: Medium Tags: [Tree] + + +#### DFS, Divide and Conquer, Preorder (utilizing BST) +- with BST, we can: + - skip adding the null nodes into the serialized string: `String NULL = "#"` + - In deserialization: use min/max boundary to check if queue.peek() can be added: + - if not meeting BST condition, skip this dfs and let other call to consume the queue +- Faster because it shortens the serialized string + + +#### DFS, Divide and Conquer, Preorder (w/o using BST) +- Take reference in Serialize and Deserialize Binary Tree +- The approach works but does not utilize Binary Search Tree properties + + + +--- + +**272. [274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.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. + +#### Couting Sort / 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 + +#### More about Counting Sort +1. Application: for number/character range +1. Steps: + - Find range, define countArray + - Count element and record in the array + - PreSum the countArray + - Start from beginning of the array, `print & decrese count` to produce the sorted elements + + + + +--- + +**273. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + +**274. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + +**275. [364. Nested List Weight Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/364.%20Nested%20List%20Weight%20Sum%20II.java)** Level: Medium Tags: [DFS, NestedInteger] + + +#### Method1: DFS +- Build a list of NestedInt +- DFS: + - sum up integers in the list are integers + - dfs on nested list + - overallSum = sum * (depth+1) + - End state: if no nested list (no more child dfs), return depth 1 +- Parent level: sum up all ints and times the (depth+1) + + +#### Method2: BFS +- Using stack to flatten all nestedList, and process in the end +- Can actually use list, does not need to be stack. +- uses more memory + + + +--- + +**276. [49. Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/49.%20Group%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 (too slow) +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**277. [438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)** Level: Medium Tags: [Hash Table, Sliding Window, Two Pointers] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### Method1: Sliding Window, Hash Table. +- A creative way of using anagram char count `hash[c] >= 0` to determine if the curr c is a target char of the deesired anagram. + - because we always reduce hash[c]-- for all characters + - so only the anagram chars would be `hash[c] >= 0` after reducing. +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +- Slinding window always has left/right pointer: + - 1) at any given time move 1 index at a time: expand right window, process rsult, shrink left window + - 2) one of the basic goal is to maintain fixed window size +- algo: + - calc char freq of the target p, and store in a hash[256]; it will be used to distinguish anagram chars: `hash[c] >= 0` indicates a anagram char + - expand right window: move right to expand the window; ONLY when meeting a anagram char, count-- + - process result: if count reduces to 0, one anagram is found + - shrink left window: if (right - left) == p.length(), drop curr left char, and move forward +- how could we rely on only just `count == 0`? + - the hidden pre-condition is `right - left must already be p.length()`, which is validaterd in prev iteration +- time: O(n) +- space: O(1) + +#### Method2: HashTable +- count character apperance -> hash table, here just a int[26] + - use a window to record count++ and count--, in order to compare with countP +- prep the countP takes O(m) time +- time: O(n) + O(m) +- space: O(n) + + + + + +--- + +**278. [138. Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/138.%20Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List, time, space: O(n) +- Basic Implementation of copy linked list: + - use a iterator node to iterate over the list: 遍历head.next .... null. + - use a dummy node to hold reference to the iterator node. +- Map: 1. avoid creating same node; 2. return the new node if existing + - 每一步都check map里面有没有head. 没有? 加上 + - 每一步都check map里面有没有head.random. 没有? 加上 +- Note, there is a way to skip the extra map O(n): https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43491/A-solution-with-constant-space-complexity-O(1)-and-linear-time-complexity-O(N) + - However, creating a deep clone of the list is already O(n) extra space, so it is NOT effectively O(1) w/o map + - It may be beneficial, if we can not hold all nodes in memory, then the approach w/o map is more applicable. + + + +--- + +**279. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + +**280. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + +**281. [33. Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/33.%20Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 + - 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` + - 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- Binary search template: + - 1) `start + 1 < end` (adjacent indexes) + - 2) start/end = mid, + - 3) compare start and end individually + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**282. [133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%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: `map` + - 1) make new curr node; + - 2) clone all neibhors and add them +- Use the map to avoid visited nodes +- time: O(n). visit all nodes +- space: O(n). Technically only travels n levels/stacks to circle all nodes (undirected & connected) + +#### 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. + + + +--- + +**283. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**284. [636. Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/636.%20Exclusive%20Time%20of%20Functions.java)** Level: Medium Tags: [Stack] + + +#### Stack +- Task time range: + - start range = next task timestamp - start.timestamp + - end range = curr task timestamp - last task timestamp + 1; because end node is inclusive. +- How to think of using stack: a task cannot finish until end is met; a early task cannot stop until a later task ends + - Alternatively, we can use a hashmap to track as well +- Keep track of the timestamp +- make sure to +1 when end node is met because end task is inclusive to this finishing task + + + + +--- + +**285. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**286. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**287. [8. String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/8.%20String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- Handling use cases +- Parse steps: + - 0. trim space + - 1 parse operator + - 2 trim leading zero + - 3. get number string +- Validation: + - 1. max length over max integer length + - 2. exceed min/max value +- Alternatively, regular expression, but not applicable in interview: if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**288. [361. Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/361.%20Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Method1: 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 的变换. + +#### Method2: Analyze, col count array: +- Inspired by: https://leetcode.com/problems/bomb-enemy/discuss/83387/Short-O(mn)-time-O(n)-space-solution +- Analyize the problem: need to add up 2 directions at any given point. + - Notice 1: if I traverse row by row, each colSum at a specific col j is likely to be the same + - Unless there is a Wall in last row, so we have to calclate the col sum starting from current row, below the Wall + - Notice 2: for row it is the same: + - If I in a new spot row[i][j], where (i-1) is Wall, i need to sum row from 0 +- we will process each point: + - process row by row and add up row sum + - buffer col[j] in an array vertically so we can resue + - make sure to recalculate row sum or col sum if last row index or last col index is Wall +- time: O(mn) traversal +- space: O(n) only use a column array + + + +--- + +**289. [402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)** Level: Medium Tags: [Greedy, Monotonous Stack, Stack] + + +#### Monotonous Stack (Increasing) +- Greedy: Remove 1) earlier digits(数位靠前权值大), 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. + + + + +--- + +**290. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**291. [1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS, top-down +- key concetpL the `common ancester of deppest leaves` must have its `two branch being same depth`. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +- time: O(n) traversal 1 pass +- space: O(n) dfs worst case depth + +#### Method2: bottom-up, find leaves first and traverse backwards +- 1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +- 2) use leaf nodes to find way backwards till common node is found; return +- time: O(n) but two passes +- space: O(n) dsf + map storage +- this approach is more brutle and uses exrtra spaces + + + +--- + +**292. [921. Minimum Add to Make Parentheses Valid.java](https://github.com/awangdev/LintCode/blob/master/Java/921.%20Minimum%20Add%20to%20Make%20Parentheses%20Valid.java)** Level: Medium Tags: [] + + + +#### Method1: Stack +- use stack to verify the input/output of '(' and ')' +- time, space: O(n) + +#### Method1: Simpilfy stack with open parentheses count +- time:(n), space: O(1) + + + +--- + +**293. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + +**294. [785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)** Level: Medium Tags: [BFS, DFS, Garph] + + +#### DFS marking each node with a state +- `bipartite` require each node to be in exact 1 party, which means it only has 1 state +- DFS to mark node with one state; and mark its edges as reversed state + - If any node state has been assigned by different from desired one, return false. + +#### BFS, Queue +- Use `Boolean states[i]` to represent visted & state +- Try all nodes with for loop, and skip visited nodes (similar validation rules as in dfs) +- In `int next : graph[curr]`, test next level first before adding. + + + +--- + +**295. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**296. [71. Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/71.%20Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path: + - 1. `.` 代表current directory, 可以忽略. + - 2. `../` 表示previous level. + - 3. double slash 可以忽略. + - 4. empty string 要output `/` +- parse by '/', and go over using stack + - put [folder] in stack + - ".." pop() 1 element of the stack, if anything + - "." stays the same +- output stack reversely: connect with '/', skip tail + + + +--- + +**297. [34. Find First and Last Position of Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/34.%20Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- need search left bound & right bound. +- use input parameter `direction` to binary search function +- Option0: simplification, inspired by `278. First Bad Version - Method1: Check is-NOT-BadVersion` + - 1) if found match, but NOT sure it is desired boundary, just leave it and keep going + - 2) check the final results after `binary search while loop` completes + - WHY? code is easier to read in this way. + + + +--- + +**298. [721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Union Find] + +- time O(mn) + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Method1: 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, 最终用来输出. + +#### Method2: 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) + +### DFS +- TODO + + + +--- + +**299. [698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)** Level: Medium Tags: [DFS, DP, Recursion] + + +#### Method1: Brutle DFS to find subsets and return results +- Target = total / k, fixed. +- DFS: Pick one num and dfs with the remaining nums for subproblem + - subproblem1: accumulate local sum to target + - EndState: accumulatedSum == target, continue with below + - subproblem2: after accumulatedSum == target, continue dfs with k-1 + - EndState: k == 0, return overall true +- Option1: DFS with nums, and boolean[] visited. Fast +- Option2: DFS with queue. + - Specially handling: dfs(size+1) to prevent `while(size-- >0)` from skipping last item at index 0 + + +#### Method2: DP +- the problem aims to find true/false capability +- bit-masking. TODO. The DP approach is kinda hard-level +- https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/335668/DP-with-Bit-Masking-Solution-%3A-Best-for-Interviews + + + +--- + +**300. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + +**301. [366. Find Leaves of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/366.%20Find%20Leaves%20of%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS: store nodes at the same depth +- the leaves are at depth 0 and the root is at highest depth +- dfs: the depth = index of the rst, start from depth = 0 at leaf +- end state: leaf node, add to rst, and return depth + + + + +--- + +**302. [156. Binary Tree Upside Down.java](https://github.com/awangdev/LintCode/blob/master/Java/156.%20Binary%20Tree%20Upside%20Down.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS +- Given that left & right nodes are always available in pair, at each level: + - perform dfs to find new root: return deepest left node as root + - pick out curr left node and fix current level: + - add right node as left node + - add root as right node + + + +--- + +**303. [416. Partition Equal Subset Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/416.%20Partition%20Equal%20Subset%20Sum.java)** Level: Medium Tags: [Backpack, DP] + +#### Backpack DP +- the problem turns into: can we find a subset of items that sum up to target sum? +- create `boolean dp[j]` to represent if we can sum up to j, where j = sum value + - want to try out all items in num, + +#### DFS +- use dfs to find a subset of items that sum up to target sum? + + + +--- + +**304. [611. Valid Triangle Number.java](https://github.com/awangdev/LintCode/blob/master/Java/611.%20Valid%20Triangle%20Number.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Method1: Fix max and backward counting +- Sort nums: O(nlogn) +- Set max value fixed on right side at k + - set 2nd value from right index j + - set last value at min index i + - if `nums[i] + nums[j] > nums[k]`: with fixed j, i can pick from [i, j-1] combinations + - then j--, to pick another j candidate + - maintain a window [i,j]; if invalid, move i++ +- time: O(n^2) +- Note: very similar to 3-sum, fixing 1 index and use 2 pointers to move window + +#### Method2: Fix min and forward counting +- Sort nums: O(nlogn) +- Set min value at i + - set 2nd value at j=i+1; and 3rd value at k=i+2 + - find max of k that fits into triangle + - count all possible k candidates from [j+1, k] + - then move j to a new candidate +- O(n^2) + + + +--- + +**305. [341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, NestedInteger, Stack] + + +#### Method1: Stack holds items from back of the list +- Option1: always set integer on top of the stack everywhere + - if not, poping stack until the top is integer + - code is easy +- Option2: in hasNext(), faltten the list in stack + +#### Method2: DFS preprocessing. +- 用queue to store all items. Kinda hack. Defeat the purpose of the problem. +- Super fast to query next(), however, needs to holds everything in memory +- O(n) + + + +--- + +**306. [254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)** Level: Medium Tags: [BFS, Backtracking, DFS] + + + +#### Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs + + +#### Method2: BFS +- Check if the number can be devided by [2, sqrt(n)], return a list of possible factors. Only check till `Math.sqrt(n)` + - build suffixes: use the factor to devide last element of list and replace last element + - build candidate: replace last element of the queue item with new list of suffixes; add to rst + - add success item back to queue: in case last element can be simplified +- **remove dupilcates**: since we start factor from [2, sqrt(n)], the final factor list should be **ascending**!! +- time: O(x), x is the # of results +- space: O(y), y is all ongoing candidates in queue + + + +--- + +**307. [739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)** Level: Medium Tags: [Hash Table, Monotonous Stack, Stack] + + +#### Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) + +#### Method2: `Map `, kinda of like bucket sort +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: + - 1) record tempIndex[currTemp] = i; + - 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result + + +--- + +**308. [373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)** Level: Medium Tags: [Heap, MaxHeap, MinHeap] + + +#### Method1: MinHeap wiht k size +- This approach follows the pattern of finding min pair: + - 1) only need to store k pairs + - 2) always start from min of A list and min of B list + - 3) pre-build k pairs honoring A list, and then pick the min pair, and start swapping with min of list B +- First attemp all first k pairs from nums1[i] against nums2[0] <=k : O(k) +- Use queue to pull min node and save results +- Use the nums1 val from the min node, pair up with nums2[j], add back to queue to sort +- overall runtime: O(klogk) +- space: O(k) + +#### Method2: MaxHeap with k size +- Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +- overall time: O(mnLogK) +- space: O(k) + + + +--- + +**309. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high= i) +- 注意使用 dp[i] = Integer.MAX_VALUE做起始值, 来找min +- time: O(n^2), slow, and timesout + + + +--- + +**1. [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, 但是数字可为多位 + + + +--- + +**2. [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 + + + +--- + +**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个数字 + + + +--- + +**4. [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 + + + +--- + +**5. [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的要求! + + + +--- + +**6. [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)}` + + + +--- + +**7. [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 + + + +--- + +**8. [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)) + + + + +--- + +**9. [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. + + + +--- + +**10. [Orderly Queue.java](https://github.com/awangdev/LintCode/blob/master/Java/Orderly%20Queue.java)** Level: Hard Tags: [Math, String] + + + + +--- + +**11. [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? + + + +--- + +**12. [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. + + + +--- + +**13. [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 + + + +--- + +**14. [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, 看解答 + + + +--- + +**15. [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), 太慢. + + + +--- + +**16. [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一下。 + + + + +--- + +**17. [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 + + + +--- + +**18. [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. + + + + +--- + +**19. [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就可以代为解决。 + + + +--- + +**20. [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) + + + +--- + +**21. [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 + + + +--- + +**22. [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 +还要做一下那. + + + +--- + +**23. [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) + + + +--- + +**24. [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。 + + + +--- + +**25. [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 + + + +--- + +**26. [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里面的站位) + + + +--- + +**27. [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 + + + +--- + +**28. [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) + + + +--- + +**29. [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] + + +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 +- + + + +--- + +**30. [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 + + + +--- + +**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};` + + + +--- + +**32. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + +--- + +**33. [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, 节约时间复杂度. + + + +--- + +**34. [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] + + + + +--- + +**35. [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 一样 + + + +--- + +**36. [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 + + + +--- + +**37. [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]。 + + + + + +--- + +**38. [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) + + + +--- + +**39. [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) + + + + +--- + +**40. [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 + + + + +--- + +**41. [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. + + + + +--- + +**42. [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. +- 分析过, 还没有写. + + + +--- + +**43. [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 +还没有做 + + + +--- + +**44. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**45. [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 + + + + +--- + +**46. [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` + + + +--- + +**47. [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里面, 这个想法非常值得思考. + + + +--- + +**48. [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) + + + +--- + +**49. [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; + + + + +--- + +**50. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**51. [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? + + + + +--- + +**52. [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 + + + + +--- + +**53. [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的理所应当。 + + + + +--- + +**54. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + +--- + +**55. [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. + + + +--- + +**56. [Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)** Level: Hard Tags: [DP] + + + +--- + +**57. [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就可以了. + + + +--- + +**58. [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 + + + +--- + +**59. [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. + + + +--- + +**60. [Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)** Level: Hard Tags: [BFS] + + + +--- + +**61. [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. + + + +--- + +**62. [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)的巧妙用法. + + + + +--- + +**63. [[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)** Level: Hard Tags: [HashHeap, Heap, Lint] + +非题.是从九章找来的HashHeap implementation. + +#### HashHeap +- An efficient implementation of a priority queue. +- The linear hash function monotonically maps keys to buckets, and each bucket is a heap +- https://xlinux.nist.gov/dads/HTML/hashheap.html + + + +--- + +**64. [42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. + +#### Method1: Max wall from both sides +- Array: Left Max Wall vs Right Max Wall. +- 对于每个index而言, vertically 能存放的最大水柱, 就是靠 左 右 最高墙决定的: + - min(leftHighestWall, rightHighestWall) - currHeight. +- time: O(n) +- space: O(n) + +#### Method2: Two Pointers +- Optimization from Method1: two pointer, 还是找左边最高和右边最高. O(1) space. +- 利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +- always limited by the shorter wall: 左边按照maxLeft来计算, 右边按照maxRight来计算. +- time: O(n) +- space: O(1) + +#### Method3: 2 Pointers, start from 2 sides +- 1. 找中间最高bar的index +- 2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条 +- 3. 每次还要减去block自身的height +- time: O(n) +- space: O(1) + +#### Method4: Stack +- 主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +- 最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +- 用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. +- time: O(n) +- space: O(n) + + + + +--- + +**65. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**66. [843. Guess the Word.java](https://github.com/awangdev/LintCode/blob/master/Java/843.%20Guess%20the%20Word.java)** Level: Hard Tags: [MiniMax] + + +TODO: revist time/space complexity + +#### Minimax, find target, and use it to eliminate +- `擒贼先擒王`: find the candidate that has largest set of correlations with the rest candidates, and eliminate based on this candidate. + - `approach A`: count the candidate that has 0 overlaps, find min of this poll + - `approach B`: count the candidate that has largest # of connections +- cross-compare, count `match==0` : find candidates that has 0 overlap with others + - pick `min-count candidate A`: it is a candidate that has overlaps with most strings (since 0-match-count is lowest) + - the above candidate will help to **eliminate** a largerset of overlapped candidates + - guess A, return matchCount. +- filter set with matchCount: eliminateCandidate + + + +--- + +**67. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**68. [301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)** Level: Hard Tags: [BFS, DFS, DP] + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- Goal: identify invalid parentheses and remove (minimum removals) +- Step: + - Detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` + - When invalid occurs: + - chance for correction. Remove the incorrect parentheses, one at a time + - dfs on the rest of the s that has not been tested yet: start index from index i + - Core edge cases: + - Do not correct twice of the same parenthesis by checking [j-1] pos + - Make sure to attempt correction of all possible parenthesis within tested range: because it outputs all results at the same level + - return/finish once correction done +- Success case: + - a string s passed test: make sure it passes REVERSED string test! + - Core Concept: `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**. +- How does it guarantee minimum removals? + - When seeing a chance to correct, it jumps into a for loop of DFS. It `return` after the for loop. This stops additional testing + - When invalid occurs, correct it right away: minimum correction +- Complexity: + - O(nk), k being the # of recursive calls. It takes n calls to finish a full string case. + +#### BFS +- Similar to DFS, we wnat to test: 1) test input s valid, 2) remove 1 invalid parenthesis at a time, 3) process substring +- instead of testing all substrings (timeout), we want to establish rules to improve reprocess: + - Test1: skip regular char. No need to test it. + - Test2: if redundant paren, do 1 is enough. skip adjacent ones. + - Test3: if last removed extra paren is '(', the next ')' must be a valid pair. LastRemoved char: pecial handling by using a struct: `class Node {String s, int index, char lastRemoved}` +- How to end tests? When there is data in rst, stop adding to queue. + + + +--- + +**69. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**70. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**71. [41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)** Level: Hard Tags: [Analysis, Array, Edge Case] + + +给一串无序数字, 有负数: 找这个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 +1. 如果nums==null, 其实missing positive integer 自然而然是 1 +1. 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) + - 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +1. 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + +**72. [308. Range Sum Query 2D - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/308.%20Range%20Sum%20Query%202D%20-%20Mutable.java)** Level: Hard Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree +- Same concept as turning an array into a binary segment tree, + - HOWEVER, this is a 4-nary segmenet tree +- Reference. 307 Range Sum Query +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. +- Handling end stage, there are two approaches: + - ApproachA: check at beginning of recursive call (i.e in `build()`, `updateNode()`, `rangeQuery()`). + - pro: calling recursive function blindly; code is easy. + - con: be really clear about termination state, and catch it. + - ApproachB: check & come up with correct query condition before recursive call + - pro: input to recursive function is assumed to be correct + - con: sometimes really hard to write the conditions before recursive call; code is hard. + + + +--- + +**73. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**74. [1153. String Transforms Into Another String.java](https://github.com/awangdev/LintCode/blob/master/Java/1153.%20String%20Transforms%20Into%20Another%20String.java)** Level: Hard Tags: [Graph] + + +#### Graph +- analysis: + - 1) should not have mult-origin cases: 1 char maps to 1 char at maximum + - 2) need a buffer char NOT exist in target to hold inter-media transformation + - check open char (out of 26 lower letter) that is NOT in target chars +- impl the validation rules +- more to read in https://leetcode.com/problems/string-transforms-into-another-string/discuss?currentPage=1&orderBy=most_votes&query= + + + +--- + +**75. [850. Rectangle Area II.java](https://github.com/awangdev/LintCode/blob/master/Java/850.%20Rectangle%20Area%20II.java)** Level: Hard Tags: [Segment Tree, Sweep Line] + + +#### Sweep Line + Merge Interval concept +- Inspired by: https://leetcode.com/problems/rectangle-area-ii/discuss/137941/Java-TreeMap-solution-inspired-by-Skyline-and-Meeting-Room +- First consider regular sweep line and realize problem: each vertical line has multiple block segments + - Easy: take a list of vertical dots, and calculate the height diff + - We can use a TreeMap with y-coordinate as key, so to `natural sort by y-coordinate` +- Trick: can NOT remove used y coordinate from map, because the rectangle may continue to expand to right side. +- apply simple equation to calc area: `(long)preY * (p.x - preX)` +- time: + - sort initial queue: O(nlogn) + - process queue: O(n) + - TreeMap insertion: O(logn) + - TreeMap traversal: O(n) + - overall, process queue can be O(n^2) +- space: O(n) + +#### Sweep Line concept, bottom->top sweep +- https://leetcode.com/problems/rectangle-area-ii/discuss/137914/C%2B%2BPython-Discretization-and-O(NlogN) + +#### Segment Tree +- TODO lol + + + +--- + +**76. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**77. [51. N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/51.%20N-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 +- Backtracking by replacement: each row has 1 queen, so just store it in int[] columns (CC book solution) + +#### 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 + + + + +--- + +**78. [305. Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/305.%20Number%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 + + + +--- + +**79. [741. Cherry Pickup.java](https://github.com/awangdev/LintCode/blob/master/Java/741.%20Cherry%20Pickup.java)** Level: Hard Tags: [DFS, DP] + + +special hint: `r1 + c1 = constant t = r2 + c2`, if the two points are moving at same time. + +#### DFS + Memo: TOP-DOWN +- Similar concept to Minimum Path Sum +- https://leetcode.com/problems/cherry-pickup/solution/ +- realize r1 + c1 = r2 + c2. Knowing 3 parameters can uniquely identify the 4th. +- assume there are 2 people starting from origin, and the 2 people can go total 4 directions + - perform DFS based on the 4 directions + - concern: do they visit the same spot? possible. when that happens, make sure we do not double count the grid[i][j] +- when is the end state? + - then anyone, for example, (r1,c1) reaches end (n-1, n-1). + - it means the other person also reaches end +- use memo: memo[r1][c1][r2], it records any given (r1, c1, r2, c2) state + + + + +--- + +**80. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**81. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**82. [158. Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/158.%20Read%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就好了. +- alaternatively: use queue to hold so we do not need to worry about size + + + +--- + +**83. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**84. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**85. [239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Method1: Deque, Monotonous queue +- 维持monotonuous queue: `front is always at max` and the `tail end is min`. Always need to return the max end of queue. +- when adding new elements x: + - 1) start from small-end of the queue + - 2) drop all smaller elements + - 3) append to the ending element that is larger than x. + - This is to maintain a front->tail decreasing queue +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`: better than using arraylist, since DeQueue(linked list) removes at O(1) cost +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +- Option1: sliding window template using right/left + while loop + - 1) tailing the new number to max queue, if applicable + - 2) process: record max + - 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] +- Option2: same concept, but use index `i` to mark right, and `i - k + 1` to mark left. +- time: O(n), one pass +- space: O(k), store the deque + + +#### Method2: Heap +- can always build a `class Node{index, val}`; and sort them with PQ of size k +- time: O(nlogK) +- space: O(k) +- this is not linear time, not as good as method1 + + + +--- + +**86. [10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + + + +--- + +**87. [1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)** Level: Hard Tags: [DFS, Stack, String] + +#### Parse exp as sub problem +- Analyze the pattern: 1) single char, 2) with !, 3) with &, | +- Identify sub problem + - Use stack to parse the data in "()", which is a sub problem to solve with recursive call + - Handle &, | case: need to parse multiple +- Be comfortable with string parsing +- Slight improve: + - If see obvious result, directly return evaluation w/o further parsing + - use memo to store evaluated exp + +#### Evaluate inner exp and save back to Stack +- Use '(' and ')' to mark inner exp +- Evaluate the inner exp and save result back to Stack: the result will be 'f' or 't' +- This is slightly slow because: + - It requires all stack items on top to be processed before reaching the operator + - There is no room to optimize even there is simplification for specific operator + + + +--- + +**88. [715. Range Module.java](https://github.com/awangdev/LintCode/blob/master/Java/715.%20Range%20Module.java)** Level: Hard Tags: [Segment Tree, TreeSet] + + +#### TreeSet +- start with considering array structure but operation are all O(n) + - what if we can easily find range, and update +- TreeSet: + - build a class `Interval {int start, end;}` + - build a customized `compareTo` that sorts the interval by start at default, but sort by end if a.start==b.start + - Query: TreeSet allow us to find element in O(logn) + - Add Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + - Remove Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + + + +--- + +**89. [432. All One Data Structure.java](https://github.com/awangdev/LintCode/blob/master/Java/432.%20All%20One%20Data%20Structure.java)** Level: Hard Tags: [Design, Doubly Linked List] + + +#### Doubly Linked List +- IMPORTANT: the problem aims to put keys of same frequency in same node! This affects the design of node +- Main a class `Node {keySet, count, last/next pointers}` +- Each operation: + - 1) finds target node and extract the key + - 2) calculate: count +/- 1 + - 3) find new spot to store the key (prior positions or later positions) +- Be careful when handling the cases in inc() and dec() + + + +--- + +**90. [639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%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]) +- Enumeration: + - 具体分析 '*' 出现的位置, 枚举出数字, 基本功. + - 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) + - 枚举好以后, 其实这个题目的写法和思考过程都不难 +- Mode: + 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. + + +#### DFS + memoization +- DFS top-down approach is used to analyze the problem. The logic flow: +- 1) consider the case of 1 letter or 2 letters. +- 2) one letter: + - [*]: + 9 * dfs(s, i + 1) + - [0~9]: + dfs(s, i + 1) +- 3) two letters: + - [_, *]: depends + - [*, _]: depends + - [*, *]: + 15 * dfs(s, i + 2) +- memo[i] records # of ways to decode from [i ~ n] +- space: O(n), Size of recursion tree can go upto n +- time: O(n), `memo array is filled exactly once`!!! + + + +--- + +**91. [68. Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/68.%20Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- greedy approach: line up as many words as possible; once exceed the MaxLength, justify the list of words +- Steps + - 1) Split & group + - 2) Juststify a row of words + - 3) clean up last row +- Calcualte bounded row length = `width + (list.size() - 1)`. `list.size()-1` = Minimum amount of slot/space used. +- Calculate max ave spaces ot insert into each slot = `totalExtraSpace/slot` +- `Juststify a row of words`: + - 1) take a list of words and assume minimum 1 space in-between words + - 2) distribute the remaining spaces evenly to each slot +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**92. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**93. [273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%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来找到结果, 每段的处理方法都是一样的 +- Note: + - StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 + - 注意加 " " 的时候, 如果多余, 要`trim()` + - 注意, `小于20的数字, 有自己的特殊写法, 需要额外handle` + - 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 +- Thinking process: + - `1 ~ 19`: [one, two ... nine, ten, eleven, ...., ninteen] + - `20 ~ x0`: [twenty, thirty, fourty, ... ninety] + - `x00`: hundred: 100 + - thousand: 10^3 + - million: 10^6 + - billion: 10^9 + - trillian: 10^12 way over 2^31, not needed +- plan: + - parse 3 digits at a time + - convert the 3 digit to [xx hundred xx-ty x] + - come up with a string[] + - insert the thousands/million/billion to the string[] + + + +--- + +**94. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**95. [52. N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/52.%20N-Queens%20II.java)** Level: Hard Tags: [Backtracking] + + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking (with replacement) +- Each row has just 1 Queen value +- As CC book suggests, use `int[] columns` of length n to store all queen col positions for n rows + - `int[] columns` is slightly easier to backtrack by updating certain index i with new col + - list will usualy has the add/remove pattern for backtracking + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + + + + +--- + +**96. [65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**97. [632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)** Level: Hard Tags: [Hash Table, Sliding Window, Two Pointers] + + +#### Method1: Sliding Window +- First sort all of the items together by actual val using `Node {int val, int row}` +- Slinding window goal: + - 1) use right to find range that touches all rows, + - 2) use left to shrink the range +- Sliding Window Template + - move right pointer + - Counts[i] = # of elements used in left/right range + - when counts[i] == 0, countUnique++; the number of row/list being included + - when count == row size: + - processing & save shorter range by using left/right Pointers + - move left pointer; when counts[i] == 0, countUnique-- +- time: O(nlogn) for initial sort and then O(n) to process +- space: O(n) +- What is hard here? To think of the idea of counting one usage of each row: + - when each all rows are used at least 1 time + - calculate the min dist + +#### Method2 PQ?, Similar to merging k array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104893/Java-Code-using-PriorityQueue.-similar-to-merge-k-array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/solution/ + + + +--- + +**98. [745. Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/745.%20Prefix%20and%20Suffix%20Search.java)** Level: Hard Tags: [Trie] + + +#### Chain `suffix # prefix` +- Build Trie for all combinations of `suffix#prefix`; all assigned with weight +- how does it make sure to return largest weight/index? + - when we build trie, always update weight for the path nodes it goes through + - yes, it overrides, but this problem does not care if some words are not found +- Time: + - build: go through all words O(n) * word length * 2 => O(n) + - query: O(1) tree height is just at most 20. +- Space: O(N) store all words + + + +--- + +**99. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + +**100. [689. Maximum Sum of 3 Non-Overlapping Subarrays.java](https://github.com/awangdev/LintCode/blob/master/Java/689.%20Maximum%20Sum%20of%203%20Non-Overlapping%20Subarrays.java)** Level: Hard Tags: [Array, DP] + + +#### DP, Divide and conquer +- split into 3 parts [0, i -1], [i, i + k -1]. [i + k, n - 1] +- NOTE: be very careful about index handling: + - `presum[i + 1] - presum[0]` gives inclusive range of `[0, i]` +- Use DP to record the starting position of max sum, +- inspired by: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C%2B%2BJava-DP-with-explanation-O(n) + - 1) calculate preSum with range [0, n] + - 2) calculate leftMaxIndex[], rightMaxIndex[] + - 3) test middle range to find max solution +- Note: the test range for 1, 2, 3 always start with assumption that k has been consumed from one side +- Note: When need to record at max/min value change, we can check/assign it manually (rather than use a object to carry & sort) + + + + +--- + +**101. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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" + + + +--- + +**102. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**103. [265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + + +一排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) + + + +--- + +**104. [272. Closest Binary Search Tree Value II.java](https://github.com/awangdev/LintCode/blob/master/Java/272.%20Closest%20Binary%20Search%20Tree%20Value%20II.java)** Level: Hard Tags: [Stack, Tree] + + +#### Method1: Stack, DFS, Inorder Traversal +- find successors and predecessors using BST (both list will be sorted); in the end, we can easily get top k from the two sorted list + - with BST: **inorder traversal gives us sorted predecessors + - with BST: **reversed-inorder traversal gives us sorted successors + - smallest on top of the stack +- time: O(n) visit all nodes, O(k) to output +- space overall: O(n) to store all nodes + +#### Method2: BFS, brutle force +- Itereate over all nodes and maintain pq (improvemenet point: how to avoid traversing entire tree?) +- prioritize nodes that are closer to target, so we may stop early when result reaches k candidates +- time: O(n*logn) +- kinds slow and not utilizing BST + + + +--- + +**105. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + + + + + + + +## Review (2) +**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 III.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20III.java)** Level: Review Tags: [] + + + +--- + + + + 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/Java/3 Sum Closest.java b/Others/old records/LintCode-Backup/3 Sum Closest.java similarity index 66% rename from Java/3 Sum Closest.java rename to Others/old records/LintCode-Backup/3 Sum Closest.java index ea4b234..da399e9 100644 --- a/Java/3 Sum Closest.java +++ b/Others/old records/LintCode-Backup/3 Sum Closest.java @@ -1,28 +1,33 @@ +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 -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. */ +/* +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 { - /** - * @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; @@ -49,3 +54,4 @@ public int threeSumClosest(int[] num, int target) { } } +``` \ 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/Java/Anagrams.java b/Others/old records/LintCode-Backup/Anagrams.java similarity index 68% rename from Java/Anagrams.java rename to Others/old records/LintCode-Backup/Anagrams.java index 2d1395c..4b47340 100644 --- a/Java/Anagrams.java +++ b/Others/old records/LintCode-Backup/Anagrams.java @@ -1,22 +1,27 @@ -hashtable 的做法。 -toCharArray -Arrays.sort -Stirng.valueOf(char[]) +M +HashMap 的做法. sort每个string, 存进HashMap, 重复的就是anagrams,最后输出。 + toCharArray + Arrays.sort + Stirng.valueOf(char[]) +时间n*L*O(logL),L是最长string的长度。 -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. -这一步把for s: strs -里面的时间复杂度降到了O(L). L = s.length() -而普通的,for 里面的时间复杂度是 Llog(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. + + ``` /* Given an array of strings, return all groups of strings that are anagrams. 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/Java/Balanced Binary Tree.java b/Others/old records/LintCode-Backup/Balanced Binary Tree.java similarity index 82% rename from Java/Balanced Binary Tree.java rename to Others/old records/LintCode-Backup/Balanced Binary Tree.java index 076d297..93b9c87 100644 --- a/Java/Balanced Binary Tree.java +++ b/Others/old records/LintCode-Backup/Balanced Binary Tree.java @@ -1,17 +1,23 @@ +M + 1. DFS using depth marker: 每个depth都存一下。然后如果有不符合条件的,存为-1. - 一旦有-1, 就全部返回。 - 最后比较返回结果是不是-1. 是-1,那就false + 一旦有-1, 就全部返回。 + 最后比较返回结果是不是-1. 是-1,那就false. + Traverse 整个tree, O(n) 2. 从基本的题目理解考虑,想到leaf node的情况。如果判断了leaf node, 那其他node应该就是可以recursive。 - 直接在isBalanced上面recursive. - 然后这个可能是个小小的优化,因为不需要计算所有的depth.一旦发现一个false,其他的就不需要计算,直接返回了。 + 直接在isBalanced上面recursive. + 关键return false的判断情况:如果有个node是null, 那么同一行相邻的那个,一旦有了children,那么就说明两个分支的depth已经是>=2了,那么就return false. + + 然后这个可能是个小小的优化,因为不需要计算所有的depth.一旦发现一个false,其他的就不需要计算,直接返回了。 + ``` /* -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,7 +30,7 @@ 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 */ 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/Java/Best Time to Buy and Sell Stock II.java b/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock II.java similarity index 59% rename from Java/Best Time to Buy and Sell Stock II.java rename to Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock II.java index 90196d9..2ca5b6c 100644 --- a/Java/Best Time to Buy and Sell Stock II.java +++ b/Others/old records/LintCode-Backup/Best Time to Buy and Sell Stock II.java @@ -1,16 +1,34 @@ -找涨幅最大的区间,买卖。 -飞似得涨,到峰顶,就卖。 +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). +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 +*/ -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,9 +37,10 @@ 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 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/Java/Binary Tree Inorder Traversal.java b/Others/old records/LintCode-Backup/Binary Tree Inorder Traversal.java similarity index 51% rename from Java/Binary Tree Inorder Traversal.java rename to Others/old records/LintCode-Backup/Binary Tree Inorder Traversal.java index c53c3e8..1b98ec6 100644 --- a/Java/Binary Tree Inorder Traversal.java +++ b/Others/old records/LintCode-Backup/Binary Tree Inorder Traversal.java @@ -1,19 +1,22 @@ -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 +法一: +Recursive: Divide and Conquer, with helper method -注意inorder traversal在check right node的事后, -不论right == null or != null, 每次都要强行move to right. +法二: +Stack: +Add left nodes all the way +Print curr +Move to right, add right if possible. + +注意stack.pop()在加完left-most child 的后,一定要curr = curr.right. -如果不node = node.right, -很可能发生窘境: -node alays = stack.top(), 然后stack.top()一直是一开始把left 全部遍历的内容。所以就会infinite loop, 永远在左边上下上下。 +若不右移,很可能发生窘境: +curr下一轮还是去找自己的left-most child,不断重复curr and curr.left, 会infinite loop, 永远在左边上下上下。 ``` /* -Binary Tree Inorder Traversal Given a binary tree, return the inorder traversal of its nodes' values. Example @@ -32,12 +35,12 @@ Can you do it without recursion? Tags Expand -Binary Tree +Recursion Binary Tree Binary Tree Traversal */ /* - 3. Use a helper method, recursively add to rst + 1. Use a helper method, recursively add to rst */ public class Solution { @@ -99,7 +102,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 +113,5 @@ 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/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/Java/Binary Tree Level Order Traversal.java b/Others/old records/LintCode-Backup/Binary Tree Level Order Traversal.java similarity index 51% rename from Java/Binary Tree Level Order Traversal.java rename to Others/old records/LintCode-Backup/Binary Tree Level Order Traversal.java index 8cdb741..aa2d884 100644 --- a/Java/Binary Tree Level Order Traversal.java +++ b/Others/old records/LintCode-Backup/Binary Tree Level Order Traversal.java @@ -1,9 +1,16 @@ -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 + +方法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上面加数字。 + + ``` /* -34% Accepted Given a binary tree, return the level order traversal of its nodes' values. (ie, from left to right, level by level). Example @@ -24,21 +31,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 */ /** @@ -53,6 +52,21 @@ * } */ +/* +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 +77,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 +97,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/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/Java/Binary Tree Maximum Path Sum.java b/Others/old records/LintCode-Backup/Binary Tree Maximum Path Sum.java similarity index 70% rename from Java/Binary Tree Maximum Path Sum.java rename to Others/old records/LintCode-Backup/Binary Tree Maximum Path Sum.java index f3ad82e..1d2ad38 100644 --- a/Java/Binary Tree Maximum Path Sum.java +++ b/Others/old records/LintCode-Backup/Binary Tree Maximum Path Sum.java @@ -1,37 +1,43 @@ -有点难理解. -复杂原因是:因为可能有负值啊。不能乱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 +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 + ``` /* -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 */ @@ -95,8 +101,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); 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/Java/Binary Tree Paths.java b/Others/old records/LintCode-Backup/Binary Tree Paths.java similarity index 95% rename from Java/Binary Tree Paths.java rename to Others/old records/LintCode-Backup/Binary Tree Paths.java index f2e8730..f15a24c 100644 --- a/Java/Binary Tree Paths.java +++ b/Others/old records/LintCode-Backup/Binary Tree Paths.java @@ -1,7 +1,13 @@ +E + +方法1: Recursive:分叉。Helper。 -非递归练习了一下 -因为要每次切短list, 所以再加了一个Stack 来存level +方法2,Iterative: + 非递归练习了一下 + 因为要每次切短list, 所以再加了一个Stack 来存level + + ``` /* Binary Tree Paths @@ -127,10 +133,6 @@ public List binaryTreePaths(TreeNode root) { return rst; } } - - - - /** * Definition of TreeNode: * public class TreeNode { diff --git a/Java/Binary Tree Postorder Traversal.java b/Others/old records/LintCode-Backup/Binary Tree Postorder Traversal.java similarity index 90% rename from Java/Binary Tree Postorder Traversal.java rename to Others/old records/LintCode-Backup/Binary Tree Postorder Traversal.java index 795367c..68ed22d 100644 --- a/Java/Binary Tree Postorder Traversal.java +++ b/Others/old records/LintCode-Backup/Binary Tree Postorder Traversal.java @@ -1,9 +1,11 @@ -最prefer 2 stack的做法。特别清楚。 -stack1和stack2合作。 -记得这个做法。。。挺神奇的。 +E -Divide and Conquer 的方法也非常明了! +最prefer 2 stack的做法: + stack1和stack2合作。倒水。记这个做法。。。挺神奇的。 +Divide and Conquer 的recursive方法也非常明了! + +注意,这些binary tree traversal的题目,常常有多个做法:recursive or iterative ``` /* @@ -63,7 +65,7 @@ public ArrayList postorderTraversal(TreeNode root) { /* - 2. Non-recursive, interative + 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. @@ -132,11 +134,4 @@ public void helper(ArrayListrst, TreeNode node) { } - - - - - - - ``` \ No newline at end of file diff --git a/Java/Binary Tree Preorder Traversal.java b/Others/old records/LintCode-Backup/Binary Tree Preorder Traversal.java similarity index 96% rename from Java/Binary Tree Preorder Traversal.java rename to Others/old records/LintCode-Backup/Binary Tree Preorder Traversal.java index f2cc535..1fb121f 100644 --- a/Java/Binary Tree Preorder Traversal.java +++ b/Others/old records/LintCode-Backup/Binary Tree Preorder Traversal.java @@ -1,7 +1,10 @@ -Preorder 写写, stack -1. Divide and conquer -2. Stack(NON-recursive) push curr, push right, push left. -3. recursive with helper method +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. diff --git a/Java/Binary Tree Zigzag Level Order Traversal.java b/Others/old records/LintCode-Backup/Binary Tree Zigzag Level Order Traversal.java similarity index 100% rename from Java/Binary Tree Zigzag Level Order Traversal.java rename to Others/old records/LintCode-Backup/Binary Tree Zigzag Level Order Traversal.java 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/Java/Climbing Stairs.java b/Others/old records/LintCode-Backup/Climbing Stairs.java similarity index 100% rename from Java/Climbing Stairs.java rename to Others/old records/LintCode-Backup/Climbing Stairs.java diff --git a/Java/Clone Graph.java b/Others/old records/LintCode-Backup/Clone Graph.java similarity index 100% rename from Java/Clone Graph.java rename to Others/old records/LintCode-Backup/Clone Graph.java 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/Java/Combination Sum II.java b/Others/old records/LintCode-Backup/Combination Sum II.java similarity index 100% rename from Java/Combination Sum II.java rename to Others/old records/LintCode-Backup/Combination Sum II.java diff --git a/Java/Combination Sum.java b/Others/old records/LintCode-Backup/Combination Sum.java similarity index 100% rename from Java/Combination Sum.java rename to Others/old records/LintCode-Backup/Combination Sum.java 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/Java/Compare Strings.java b/Others/old records/LintCode-Backup/Compare Strings.java similarity index 100% rename from Java/Compare Strings.java rename to Others/old records/LintCode-Backup/Compare Strings.java 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/Java/Copy List with Random Pointer.java b/Others/old records/LintCode-Backup/Copy List with Random Pointer.java similarity index 100% rename from Java/Copy List with Random Pointer.java rename to Others/old records/LintCode-Backup/Copy List with Random Pointer.java 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/Java/Delete Digits.java b/Others/old records/LintCode-Backup/Delete Digits.java similarity index 100% rename from Java/Delete Digits.java rename to Others/old records/LintCode-Backup/Delete Digits.java 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/Java/Edit Distance.java b/Others/old records/LintCode-Backup/Edit Distance.java similarity index 100% rename from Java/Edit Distance.java rename to Others/old records/LintCode-Backup/Edit Distance.java 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/Java/Fibonacci.java b/Others/old records/LintCode-Backup/Fibonacci.java similarity index 100% rename from Java/Fibonacci.java rename to Others/old records/LintCode-Backup/Fibonacci.java 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/Java/First Bad Version.java b/Others/old records/LintCode-Backup/First Bad Version.java similarity index 100% rename from Java/First Bad Version.java rename to Others/old records/LintCode-Backup/First Bad Version.java diff --git a/Java/First Missing Positive.java b/Others/old records/LintCode-Backup/First Missing Positive.java similarity index 100% rename from Java/First Missing Positive.java rename to Others/old records/LintCode-Backup/First Missing Positive.java diff --git a/Java/Flatten Binary Tree to Linked List.java b/Others/old records/LintCode-Backup/Flatten Binary Tree to Linked List.java similarity index 100% rename from Java/Flatten Binary Tree to Linked List.java rename to Others/old records/LintCode-Backup/Flatten Binary Tree to Linked List.java diff --git a/Java/Gas Station.java b/Others/old records/LintCode-Backup/Gas Station.java similarity index 100% rename from Java/Gas Station.java rename to Others/old records/LintCode-Backup/Gas Station.java diff --git a/Java/Generate Parentheses.java b/Others/old records/LintCode-Backup/Generate Parentheses.java similarity index 100% rename from Java/Generate Parentheses.java rename to Others/old records/LintCode-Backup/Generate Parentheses.java diff --git a/Java/Graph Valid Tree.java b/Others/old records/LintCode-Backup/Graph Valid Tree.java similarity index 100% rename from Java/Graph Valid Tree.java rename to Others/old records/LintCode-Backup/Graph Valid Tree.java 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/Java/Hash Function.java b/Others/old records/LintCode-Backup/Hash Function.java similarity index 100% rename from Java/Hash Function.java rename to Others/old records/LintCode-Backup/Hash Function.java 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/Java/Heapify.java b/Others/old records/LintCode-Backup/Heapify.java similarity index 100% rename from Java/Heapify.java rename to Others/old records/LintCode-Backup/Heapify.java diff --git a/Java/House Robber.java b/Others/old records/LintCode-Backup/House Robber.java similarity index 100% rename from Java/House Robber.java rename to Others/old records/LintCode-Backup/House Robber.java 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/Java/Insert Interval.java b/Others/old records/LintCode-Backup/Insert Interval.java similarity index 100% rename from Java/Insert Interval.java rename to Others/old records/LintCode-Backup/Insert Interval.java 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/Java/Intersection of Two Linked Lists.java b/Others/old records/LintCode-Backup/Intersection of Two Linked Lists.java similarity index 91% rename from Java/Intersection of Two Linked Lists.java rename to Others/old records/LintCode-Backup/Intersection of Two Linked Lists.java index 28078fa..7363445 100644 --- a/Java/Intersection of Two Linked Lists.java +++ b/Others/old records/LintCode-Backup/Intersection of Two Linked Lists.java @@ -1,5 +1,9 @@ +E + 长短list,找重合点。 -长度不同的话,切掉长的那个的extra length。 那么起点一样后,重合点就会同时到达。 +长度不同的话,切掉长的list那个的extra length。 那么起点一样后,重合点就会同时到达。 + + ``` /* Write a program to find the node at which the intersection of two singly linked lists begins. 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/Java/Jump Game.java b/Others/old records/LintCode-Backup/Jump Game.java similarity index 100% rename from Java/Jump Game.java rename to Others/old records/LintCode-Backup/Jump Game.java 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/Java/Length of Last Word.java b/Others/old records/LintCode-Backup/Length of Last Word.java similarity index 100% rename from Java/Length of Last Word.java rename to Others/old records/LintCode-Backup/Length of Last Word.java 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/Java/Linked List Cycle.java b/Others/old records/LintCode-Backup/Linked List Cycle.java similarity index 100% rename from Java/Linked List Cycle.java rename to Others/old records/LintCode-Backup/Linked List Cycle.java diff --git a/Java/Longest Common Prefix.java b/Others/old records/LintCode-Backup/Longest Common Prefix.java similarity index 100% rename from Java/Longest Common Prefix.java rename to Others/old records/LintCode-Backup/Longest Common Prefix.java 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/Java/Longest Palindromic Substring.java b/Others/old records/LintCode-Backup/Longest Palindromic Substring.java similarity index 100% rename from Java/Longest Palindromic Substring.java rename to Others/old records/LintCode-Backup/Longest Palindromic Substring.java 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/Java/Longest Substring with At Most K Distinct Characters.java b/Others/old records/LintCode-Backup/Longest Substring with At Most K Distinct Characters.java similarity index 100% rename from Java/Longest Substring with At Most K Distinct Characters.java rename to Others/old records/LintCode-Backup/Longest Substring with At Most K Distinct Characters.java diff --git a/Java/Longest Words.java b/Others/old records/LintCode-Backup/Longest Words.java similarity index 100% rename from Java/Longest Words.java rename to Others/old records/LintCode-Backup/Longest Words.java diff --git a/Java/Lowest Common Ancestor II.java b/Others/old records/LintCode-Backup/Lowest Common Ancestor II.java similarity index 100% rename from Java/Lowest Common Ancestor II.java rename to Others/old records/LintCode-Backup/Lowest Common Ancestor II.java 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/Java/Maximal Square.java b/Others/old records/LintCode-Backup/Maximal Square.java similarity index 70% rename from Java/Maximal Square.java rename to Others/old records/LintCode-Backup/Maximal Square.java index c3048fa..fd858f9 100644 --- a/Java/Maximal Square.java +++ b/Others/old records/LintCode-Backup/Maximal Square.java @@ -1,7 +1,16 @@ -从边长为2的正方形看起,看左上角的那个点。 -如何确定是个正方形?首先看左上点是不是1,然后看右边,右下,下面的点是不是1. -DP就是根据这个特征想出来。dp[i,j]代表从右下推上来,包括当前点的,所积累的最长边。 -注意dp[i,j]被右,右下,下三点的最短点所限制。这就是fn. +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, @@ -24,12 +33,12 @@ 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/Maximum Depth of Binary Tree.java b/Others/old records/LintCode-Backup/Maximum Depth of Binary Tree.java similarity index 100% rename from Java/Maximum Depth of Binary Tree.java rename to Others/old records/LintCode-Backup/Maximum Depth of Binary Tree.java diff --git a/Java/Maximum Product Subarray.java b/Others/old records/LintCode-Backup/Maximum Product Subarray.java similarity index 100% rename from Java/Maximum Product Subarray.java rename to Others/old records/LintCode-Backup/Maximum Product Subarray.java 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/Java/Median.java b/Others/old records/LintCode-Backup/Median.java similarity index 100% rename from Java/Median.java rename to Others/old records/LintCode-Backup/Median.java diff --git a/Java/Merge Intervals.java b/Others/old records/LintCode-Backup/Merge Intervals.java similarity index 100% rename from Java/Merge Intervals.java rename to Others/old records/LintCode-Backup/Merge Intervals.java diff --git a/Java/Merge Sorted Array II.java b/Others/old records/LintCode-Backup/Merge Sorted Array II.java similarity index 100% rename from Java/Merge Sorted Array II.java rename to Others/old records/LintCode-Backup/Merge Sorted Array II.java diff --git a/Java/Merge Sorted Array.java b/Others/old records/LintCode-Backup/Merge Sorted Array.java similarity index 100% rename from Java/Merge Sorted Array.java rename to Others/old records/LintCode-Backup/Merge Sorted Array.java 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/Java/Merge k Sorted Arrays.java b/Others/old records/LintCode-Backup/Merge k Sorted Arrays.java similarity index 100% rename from Java/Merge k Sorted Arrays.java rename to Others/old records/LintCode-Backup/Merge k Sorted Arrays.java diff --git a/Java/Merge k Sorted Lists.java b/Others/old records/LintCode-Backup/Merge k Sorted Lists.java similarity index 100% rename from Java/Merge k Sorted Lists.java rename to Others/old records/LintCode-Backup/Merge k Sorted Lists.java diff --git a/Java/Middle of Linked List.java b/Others/old records/LintCode-Backup/Middle of Linked List.java similarity index 100% rename from Java/Middle of Linked List.java rename to Others/old records/LintCode-Backup/Middle of Linked List.java 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/Java/Minimum Path Sum.java b/Others/old records/LintCode-Backup/Minimum Path Sum.java similarity index 100% rename from Java/Minimum Path Sum.java rename to Others/old records/LintCode-Backup/Minimum Path Sum.java 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/Java/Minimum Window Substring.java b/Others/old records/LintCode-Backup/Minimum Window Substring.java similarity index 100% rename from Java/Minimum Window Substring.java rename to Others/old records/LintCode-Backup/Minimum Window Substring.java 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/Java/Next Permutation.java b/Others/old records/LintCode-Backup/Next Permutation.java similarity index 100% rename from Java/Next Permutation.java rename to Others/old records/LintCode-Backup/Next Permutation.java 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/Java/Nth to Last Node in List.java b/Others/old records/LintCode-Backup/Nth to Last Node in List.java similarity index 100% rename from Java/Nth to Last Node in List.java rename to Others/old records/LintCode-Backup/Nth to Last Node in List.java 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/Java/Number of Islands II.java b/Others/old records/LintCode-Backup/Number of Islands II.java similarity index 100% rename from Java/Number of Islands II.java rename to Others/old records/LintCode-Backup/Number of Islands II.java diff --git a/Java/Number of Islands.java b/Others/old records/LintCode-Backup/Number of Islands.java similarity index 67% rename from Java/Number of Islands.java rename to Others/old records/LintCode-Backup/Number of Islands.java index 4aeb36d..dd4dcbe 100644 --- a/Java/Number of Islands.java +++ b/Others/old records/LintCode-Backup/Number of Islands.java @@ -1,7 +1,11 @@ +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. @@ -79,4 +83,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/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/Java/Palindrome Linked List.java b/Others/old records/LintCode-Backup/Palindrome Linked List.java similarity index 100% rename from Java/Palindrome Linked List.java rename to Others/old records/LintCode-Backup/Palindrome Linked List.java 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/Java/Palindrome Partitioning.java b/Others/old records/LintCode-Backup/Palindrome Partitioning.java similarity index 100% rename from Java/Palindrome Partitioning.java rename to Others/old records/LintCode-Backup/Palindrome Partitioning.java 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/Java/Permutations II.java b/Others/old records/LintCode-Backup/Permutations II.java similarity index 100% rename from Java/Permutations II.java rename to Others/old records/LintCode-Backup/Permutations II.java diff --git a/Java/Permutations.java b/Others/old records/LintCode-Backup/Permutations.java similarity index 100% rename from Java/Permutations.java rename to Others/old records/LintCode-Backup/Permutations.java 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; ipivot, 如果写成了 <= 会 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/Java/Recover Rotated Sorted Array.java b/Others/old records/LintCode-Backup/Recover Rotated Sorted Array.java similarity index 100% rename from Java/Recover Rotated Sorted Array.java rename to Others/old records/LintCode-Backup/Recover Rotated Sorted Array.java 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/Java/Remove Duplicates from Sorted Array.java b/Others/old records/LintCode-Backup/Remove Duplicates from Sorted Array.java similarity index 100% rename from Java/Remove Duplicates from Sorted Array.java rename to Others/old records/LintCode-Backup/Remove Duplicates from Sorted Array.java 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/Java/Remove Duplicates from Sorted List.java b/Others/old records/LintCode-Backup/Remove Duplicates from Sorted List.java similarity index 100% rename from Java/Remove Duplicates from Sorted List.java rename to Others/old records/LintCode-Backup/Remove Duplicates from Sorted List.java 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/Java/Remove Linked List Elements.java b/Others/old records/LintCode-Backup/Remove Linked List Elements.java similarity index 100% rename from Java/Remove Linked List Elements.java rename to Others/old records/LintCode-Backup/Remove Linked List Elements.java 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/Java/Remove Nth Node From End of List.java b/Others/old records/LintCode-Backup/Remove Nth Node From End of List.java similarity index 100% rename from Java/Remove Nth Node From End of List.java rename to Others/old records/LintCode-Backup/Remove Nth Node From End of List.java 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/Java/Reverse Integer.java b/Others/old records/LintCode-Backup/Reverse Integer.java similarity index 100% rename from Java/Reverse Integer.java rename to Others/old records/LintCode-Backup/Reverse Integer.java 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/Java/Reverse Words in a String.java b/Others/old records/LintCode-Backup/Reverse Words in a String.java similarity index 100% rename from Java/Reverse Words in a String.java rename to Others/old records/LintCode-Backup/Reverse Words in a String.java 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/Java/Rotate String.java b/Others/old records/LintCode-Backup/Rotate String.java similarity index 100% rename from Java/Rotate String.java rename to Others/old records/LintCode-Backup/Rotate String.java 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/Java/Segment Tree Build II.java b/Others/old records/LintCode-Backup/Segment Tree Build II.java similarity index 100% rename from Java/Segment Tree Build II.java rename to Others/old records/LintCode-Backup/Segment Tree Build II.java diff --git a/Java/Segment Tree Build.java b/Others/old records/LintCode-Backup/Segment Tree Build.java similarity index 100% rename from Java/Segment Tree Build.java rename to Others/old records/LintCode-Backup/Segment Tree Build.java 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/Java/Segment Tree Query.java b/Others/old records/LintCode-Backup/Segment Tree Query.java similarity index 100% rename from Java/Segment Tree Query.java rename to Others/old records/LintCode-Backup/Segment Tree Query.java 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/Java/Single Number.java b/Others/old records/LintCode-Backup/Single Number.java similarity index 100% rename from Java/Single Number.java rename to Others/old records/LintCode-Backup/Single Number.java 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/Java/Sliding Window Maximum.java b/Others/old records/LintCode-Backup/Sliding Window Maximum.java similarity index 100% rename from Java/Sliding Window Maximum.java rename to Others/old records/LintCode-Backup/Sliding Window Maximum.java 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/Java/Sqrt(x).java b/Others/old records/LintCode-Backup/Sqrt(x).java similarity index 100% rename from Java/Sqrt(x).java rename to Others/old records/LintCode-Backup/Sqrt(x).java 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/Java/Subarray Sum.java b/Others/old records/LintCode-Backup/Subarray Sum.java similarity index 100% rename from Java/Subarray Sum.java rename to Others/old records/LintCode-Backup/Subarray Sum.java 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/Java/Topological Sorting.java b/Others/old records/LintCode-Backup/Topological Sorting.java similarity index 100% rename from Java/Topological Sorting.java rename to Others/old records/LintCode-Backup/Topological Sorting.java 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/Java/Trapping Rain Water.java b/Others/old records/LintCode-Backup/Trapping Rain Water.java similarity index 100% rename from Java/Trapping Rain Water.java rename to Others/old records/LintCode-Backup/Trapping Rain Water.java 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/Java/Unique Characters.java b/Others/old records/LintCode-Backup/Unique Characters.java similarity index 100% rename from Java/Unique Characters.java rename to Others/old records/LintCode-Backup/Unique Characters.java diff --git a/Java/Unique Path.java b/Others/old records/LintCode-Backup/Unique Path.java similarity index 100% rename from Java/Unique Path.java rename to Others/old records/LintCode-Backup/Unique Path.java diff --git a/Java/Unique Paths II.java b/Others/old records/LintCode-Backup/Unique Paths II.java similarity index 100% rename from Java/Unique Paths II.java rename to Others/old records/LintCode-Backup/Unique Paths II.java 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/Java/Valid Palindrome.java b/Others/old records/LintCode-Backup/Valid Palindrome.java similarity index 100% rename from Java/Valid Palindrome.java rename to Others/old records/LintCode-Backup/Valid Palindrome.java 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/Java/Valid Sudoku.java b/Others/old records/LintCode-Backup/Valid Sudoku.java similarity index 100% rename from Java/Valid Sudoku.java rename to Others/old records/LintCode-Backup/Valid Sudoku.java diff --git a/Java/Validate Binary Search Tree.java b/Others/old records/LintCode-Backup/Validate Binary Search Tree.java similarity index 57% rename from Java/Validate Binary Search Tree.java rename to Others/old records/LintCode-Backup/Validate Binary Search Tree.java index 21cfec4..1942660 100644 --- a/Java/Validate Binary Search Tree.java +++ b/Others/old records/LintCode-Backup/Validate Binary Search Tree.java @@ -1,3 +1,8 @@ +M + +查看每个parent-child关系。同时把root level上面传下来max,min界限定住。 + +``` /* 29% Accepted Given a binary tree, determine if it is a valid binary search tree (BST). @@ -22,10 +27,6 @@ Given a binary tree, determine if it is a valid binary search tree (BST). Tags Expand Tree Binary Tree Binary Search 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. */ /** @@ -39,26 +40,35 @@ Given a binary tree, determine if it is a valid binary search tree (BST). * } * } */ + +//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 { - /** - * @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/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/Java/Word Break.java b/Others/old records/LintCode-Backup/Word Break.java similarity index 100% rename from Java/Word Break.java rename to Others/old records/LintCode-Backup/Word Break.java 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..33727f2 100644 --- a/README.md +++ b/README.md @@ -1,252 +1,638 @@ -# LintCode +![home](https://user-images.githubusercontent.com/10102793/115952522-45a10a00-a49b-11eb-825e-08f913d9bbbf.png) +# 简介 +这个Github已经开启超过五年, 很高兴它可以帮到有需要的人. 信息有价, 知识无价, 每逢闲暇, 我会来维护这个repo, 给刷题的朋友们一些我的想法和见解. 下面来简单介绍一下: -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. +- **README.md**: 所有所做过的题目 +- **ReviewPage.md**: 所有题目的总结和归纳(不断完善中) +- **KnowledgeHash2.md**: 对所做过的知识点的一些笔记 +- **SystemDesign.md**: 对系统设计的一些笔记 -| 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| + +## 1/ 程序員土汪 YouTube 学习工作视频 + +* [我拒绝了Uber $200万刀/4y的Offer | I declined the $2M offer from Uber and went to ...](https://youtu.be/RtQFzb77dGY) +* [薪资大公开 - 2021年, 北美软件工程师, 一年赚多少? (Google vs Amazon) | How much do I make as a Software Engineer (2021)](https://youtu.be/nmgyT_m1j3s) +* [为什么我选择放弃亚马逊的工作 | Why I left Amazon as a Software Engineer (2021)](https://youtu.be/BBpivK4I_8U) +* [曾经的留级生, 今天一线大厂软件工程师! | The making of a software engineer(2021)](https://youtu.be/cACqlpXKRpY) +* [看了这些, 还想去美国留学吗? 我的留学经历 (2020)](https://youtu.be/XqOA5q5Rtpw) +* [如何拿到亚马逊的工作! How succeed in Amazon Interview! (2019)](https://youtu.be/NIuOjjKaK9M) +* [十分钟学会Python? (2019)](https://youtu.be/DRQYOdO9BAU) +* [刚到美国到底怎样开口说英文? (2019)](https://youtu.be/pd3WR5K-bLs) + + +我的[Youtube Channel](https://www.youtube.com/channel/UCQNPegv0VqempHNYPWKkVNw)未来会更新这些方面的内容: +* 工作经验的分享: 当然会跟Software Engineer比较相关 +* 在美国的学生时代的经历 + +希望在这里参考刷题经验时, 可以去关注我的频道! 搜"土汪/TuwangZ"可以找到我. [Youtube: 土汪](https://www.youtube.com/channel/UCQNPegv0VqempHNYPWKkVNw), [Bilibili: 土汪](https://space.bilibili.com/249496206). + +有任何程序员工作的疑问, 都可以给我留言/私信/邮件. wechat: TuWangZ, Instagram: TuWang.Z + +希望大家学习顺利, 对未来充满希望! + + +# 2/ 目录 Java Algorithm Problems + +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)|Hard|[Array, Coordinate DP, DP, Greedy]|O(n)|O(1)|Java|0| +|N/A|[Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)|Medium|[Enumeration, Greedy]|||Java|1| +|N/A|[Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)|Medium|[Binary Search, Divide and Conquer]|||Java|2| +|N/A|[Missing Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Ranges.java)|Medium|[Array]|||Java|3| +|N/A|[Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)|Medium|[BST, Tree]|||Java|4| +|N/A|[Convert Integer A to Integer B.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Integer%20A%20to%20Integer%20B.java)|Easy|[Bit Manipulation]|||Java|5| +|N/A|[Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)|Medium|[Backpack DP, DP]|||Java|6| +|N/A|[Total Occurrence of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Occurrence%20of%20Target.java)|Medium|[]|||Java|7| +|N/A|[House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)|Medium|[DFS, DP, Status DP, Tree]|||Java|8| +|N/A|[Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)|Medium|[DFS, Tree]|||Java|9| +|N/A|[Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)|Medium|[Backpack DP, DP]|||Java|10| +|N/A|[Closest Number in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Number%20in%20Sorted%20Array.java)|Easy|[Binary Search]|||Java|11| +|N/A|[Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)|Hard|[Binary Tree, DFS, Expression Tree, Stack]|||Java|12| +|N/A|[Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)|Easy|[Array, Bit Manipulation, Math]|||Java|13| +|N/A|[Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)|Medium|[Backtracking, DFS, String]|||Java|14| +|N/A|[Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)|Medium|[Linked List, Math, Two Pointers]|||Java|15| +|N/A|[Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)|Medium|[BST, DP, Tree]|||Java|16| +|N/A|[Largest Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number.java)|Medium|[Sort]|||Java|17| +|N/A|[Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)|Easy|[String, Two Pointers]|||Java|18| +|N/A|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|[Array, Coordinate DP, DFS, DP, Memoization]|||Java|19| +|N/A|[Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)|Hard|[DP, Hash Table]|||Java|20| +|N/A|[Summary Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Summary%20Ranges.java)|Medium|[Array]|||Java|21| +|N/A|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|||Java|22| +|N/A|[Single Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20III.java)|Medium|[Bit Manipulation]|||Java|23| +|N/A|[Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)|Easy|[Math]|||Java|24| +|N/A|[Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)|Medium|[DFS, Divide and Conquer]|||Java|25| +|N/A|[Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)|Hard|[Design, Geometry, Hash Table]|||Java|26| +|N/A|[Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)|Medium|[Bit Manipulation]|O(n)|O(1), 32-bit array|Java|27| +|N/A|[Word Pattern.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Pattern.java)|Easy|[]|||Java|28| +|N/A|[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|[Tree]|||Java|29| +|N/A|[Count 1 in Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%201%20in%20Binary.java)|Easy|[Bit Manipulation]|||Java|30| +|N/A|[Two Lists Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Lists%20Sum.java)|Medium|[Linked List]|||Java|31| +|N/A|[Flatten 2D Vector.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%202D%20Vector.java)|Medium|[Design]|||Java|32| +|N/A|[Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Hamming%20Distance.java)|Easy|[]|||Java|33| +|N/A|[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|[Union Find]|||Java|34| +|N/A|[Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)|Medium|[Binary Search, Divide and Conquer, Lint, Segment Tree]|||Java|35| +|N/A|[Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)|Medium|[DP]|||Java|36| +|N/A|[Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)|Medium|[Array, Coordinate DP, DP, Memoization]|||Java|37| +|N/A|[Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)|Easy|[Array, Math]|||Java|38| +|N/A|[Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)|Easy|[DP, Sequence DP]|O(n)|O(n)|Java|39| +|N/A|[Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)|Medium|[Hash Table, Math]|O(n)|O(n)|Java|40| +|N/A|[Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)|Hard|[Bit Manipulation, String]|||Java|41| +|N/A|[Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)|Hard|[Array, Hash Table, Union Find]|||Java|42| +|N/A|[Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)|Medium|[Array, Binary Search]|||Java|43| +|N/A|[Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)|Medium|[DFS, Divide and Conquer, Double Recursive, Tree]|||Java|44| +|N/A|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|[Array, DP, Greedy, Sequence DP, Subarray]|O(m)|O(1)|Java|45| +|N/A|[Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)|Medium|[Union Find]|||Java|46| +|N/A|[Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)|Medium|[Binary Search, Lint, Segment Tree]|||Java|47| +|N/A|[Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)|Easy|[Bit Manipulation]|O(n), n = # of bits|O(1)|Java|48| +|N/A|[Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)|Medium|[Backtracking, DFS, DP]|||Java|49| +|N/A|[Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)|Easy|[DFS, Divide and Conquer, Tree]|||Java|50| +|N/A|[Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)|Medium|[BFS, Tree]|||Java|51| +|N/A|[Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)|Easy|[Array, Subarray]|O(n)|O(1)|Java|52| +|N/A|[IndexMatch.java](https://github.com/awangdev/LintCode/blob/master/Java/IndexMatch.java)|Easy|[]|||Java|53| +|N/A|[Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)|Medium|[BFS, DFS]|||Java|54| +|N/A|[Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)|Medium|[DFS, Divide and Conquer, Stack]|||Java|55| +|N/A|[The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)|Medium|[BFS, DFS]|||Java|56| +|N/A|[Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)|Medium|[DP, String]|||Java|57| +|N/A|[Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)|Hard|[Greedy, Hash Table, Heap]|||Java|58| +|N/A|[Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)|Easy|[Basic Implementation, String]|||Java|59| +|N/A|[Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)|Hard|[Array, Binary Search, DFS, Divide and Conquer]|||Java|60| +|N/A|[Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)|Medium|[BFS, DP, Math, Partition DP]|||Java|61| +|N/A|[Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)|Medium|[Array, Backtracking, DFS]|||Java|62| +|N/A|[Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)|Medium|[Backpack DP, DP]|||Java|63| +|N/A|[Reshape the Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Reshape%20the%20Matrix.java)|Easy|[]|||Java|64| +|N/A|[Update Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Update%20Bits.java)|Medium|[Bit Manipulation]|||Java|65| +|N/A|[Triangle Count.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangle%20Count.java)|Medium|[Array]|||Java|66| +|N/A|[Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)|Hard|[Greedy, Hash Table, Stack]|||Java|67| +|N/A|[Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)|Medium|[Backtracking, Math]|||Java|68| +|N/A|[House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)|Medium|[DP, Sequence DP, Status DP]|||Java|69| +|N/A|[O(1) Check Power of 2.java](https://github.com/awangdev/LintCode/blob/master/Java/O(1)%20Check%20Power%20of%202.java)|Easy|[Bit Manipulation]|||Java|70| +|N/A|[Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)|Medium|[Backtracking, String]|||Java|71| +|N/A|[Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)|Easy|[Stack, Two Pointers]|||Java|72| +|N/A|[Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)|Medium|[Array, Binary Search, Subarray, Two Pointers]|O(n)|O(1)|Java|73| +|N/A|[Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)|Easy|[Design, Stack]|||Java|74| +|N/A|[Minimum Absolute Difference in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Absolute%20Difference%20in%20BST.java)|Easy|[BST]|||Java|75| +|N/A|[Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)|Medium|[Stack, Tree]|||Java|76| +|N/A|[ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)|Medium|[Design, Hash Table]|||Java|77| +|N/A|[HashWithArray.java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithArray.java)|Easy|[]|||Java|78| +|N/A|[Flood Fill.java](https://github.com/awangdev/LintCode/blob/master/Java/Flood%20Fill.java)|Easy|[DFS]|||Java|79| +|N/A|[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|[Array, DFS, Divide and Conquer, Tree]|||Java|80| +|N/A|[Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)|Medium|[Backpack DP, DP]|||Java|81| +|N/A|[Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)|Medium|[DP, Double Sequence DP, Sequence DP]|||Java|82| +|N/A|[Peeking Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Peeking%20Iterator.java)|Medium|[Design]|||Java|83| +|N/A|[Orderly Queue.java](https://github.com/awangdev/LintCode/blob/master/Java/Orderly%20Queue.java)|Hard|[Math, String]|||Java|84| +|N/A|[QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)|Medium|[Quick Sort, Sort]|||Java|85| +|N/A|[Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)|Hard|[Array, DP, Hash Table, Stack]|||Java|86| +|N/A|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|||Java|87| +|N/A|[Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)|Easy|[DFS, Tree]|||Java|88| +|N/A|[LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)|Hard|[Design, Hash Table]|||Java|89| +|N/A|[Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)|Easy|[Basic Implementation]|||Java|90| +|N/A|[Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)|Hard|[DP, Interval DP, String]|||Java|91| +|N/A|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|[BFS, DFS, Graph, Tree, Union Find]|||Java|92| +|N/A|[Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)|Medium|[Linked List, Two Pointers]|||Java|93| +|N/A|[Swap Nodes in Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Nodes%20in%20Pairs.java)|Medium|[Linked List]|||Java|94| +|N/A|[Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)|Easy|[Array, Coordinate DP, DP]|||Java|95| +|N/A|[K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)|Hard|[DP, Double Sequence DP, Sequence DP, Trie]|||Java|96| +|N/A|[Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)|Medium|[Backtracking, Combination, DFS]|||Java|97| +|N/A|[Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)|Easy|[Array, DFS]|||Java|98| +|N/A|[Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)|Medium|[Divide and Conquer, Linked List, Merge Sort, Sort]|||Java|99| +|N/A|[Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)|Medium|[Array, Binary Search]|||Java|100| +|N/A|[Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)|Hard|[Backtracking, DFS, Trie]|||Java|101| +|N/A|[K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)|Hard|[Array, BST, TreeSet]|||Java|102| +|N/A|[Gray Code.java](https://github.com/awangdev/LintCode/blob/master/Java/Gray%20Code.java)|Medium|[Backtracking]|||Java|103| +|N/A|[Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)|Medium|[Hash Table, Math]|||Java|104| +|N/A|[Game of Life.java](https://github.com/awangdev/LintCode/blob/master/Java/Game%20of%20Life.java)|Medium|[Array]|||Java|105| +|N/A|[Compare Version Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Version%20Numbers.java)|Medium|[String]|||Java|106| +|N/A|[Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)|Easy|[Design]|||Java|107| +|N/A|[Ugly Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number.java)|Medium|[Math]|||Java|108| +|N/A|[Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)|Hard|[Binary Search, Coordinate DP, DP]|||Java|109| +|N/A|[Rehashing.java](https://github.com/awangdev/LintCode/blob/master/Java/Rehashing.java)|Medium|[Hash Table]|||Java|110| +|N/A|[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|111| +|N/A|[Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)|Medium|[DP, Double Sequence DP, Sequence DP, String]|||Java|112| +|N/A|[Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)|Medium|[Array, Enumeration]|||Java|113| +|N/A|[Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)|Hard|[Backpack DP, DP]|||Java|114| +|N/A|[Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)|Medium|[Array, Backpack DP, DP]|||Java|115| +|N/A|[Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)|Medium|[Coordinate DP, DP]|O(n^2)||Java|116| +|N/A|[Permutation Index.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Index.java)|Easy|[]|||Java|117| +|N/A|[4Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/4Sum.java)|Medium|[Hash Table]|||Java|118| +|N/A|[Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)|Hard|[KMP, String]|||Java|119| +|N/A|[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|[DFS, Divide and Conquer, Tree]|||Java|120| +|N/A|[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|[DFS, Divide and Conquer, Tree]|||Java|121| +|N/A|[Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)|Medium|[String]|||Java|122| +|N/A|[Contiguous Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Contiguous%20Array.java)|Medium|[Hash Table]|||Java|123| +|N/A|[Reverse Linked List II .java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List%20II%20.java)|Medium|[Linked List]|||Java|124| +|N/A|[Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)|Hard|[Hash Table, String, Trie]|||Java|125| +|N/A|[Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)|Hard|[Binary Search, DFS, Divide and Conquer]|||Java|126| +|N/A|[Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)|Medium|[BFS, Graph]|||Java|127| +|N/A|[Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)|Medium|[Hash Table, String, Two Pointers]|||Java|128| +|N/A|[Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)|Medium|[Hash Table, Math]|||Java|129| +|N/A|[Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)|Medium|[Array, Sort]|||Java|130| +|N/A|[Reverse Words in a String II.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20II.java)|Medium|[String]|||Java|131| +|N/A|[Remove Node in Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Node%20in%20Binary%20Search%20Tree.java)|Hard|[BST]|||Java|132| +|N/A|[Reorder List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reorder%20List.java)|Medium|[Linked List]|||Java|133| +|N/A|[Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)|Hard|[DFS, Graph, Tree, Union Find]|||Java|134| +|N/A|[[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)|Easy|[Array, Lint, Quick Select, Quick Sort, Two Pointers]|O(n)|O(logN)|Java|135| +|N/A|[Swap Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Bits.java)|Easy|[Bit Manipulation]|||Java|136| +|N/A|[Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)|Medium|[Array, Math]|||Java|137| +|N/A|[Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)|Medium|[Binary Search, Coordinate DP, DP, Memoization]|O(n^2) dp, O(nLogN) binary search|O(n)|Java|138| +|N/A|[Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)|Easy|[Bit Manipulation, Math]|||Java|139| +|N/A|[Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)|Easy|[Design, Stack]|||Java|140| +|N/A|[Count of Smaller Number before itself.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number%20before%20itself.java)|Hard|[]|||Java|141| +|N/A|[Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)|Medium|[Hash Table, Linked List]|||Java|142| +|N/A|[Number of Digit One.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Digit%20One.java)|Hard|[Math]|||Java|143| +|N/A|[Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)|Easy|[DFS, Tree]|||Java|144| +|N/A|[Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)|Medium|[BST, Binary Tree]|||Java|145| +|N/A|[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|[Array, DP, Sequence DP]|||Java|146| +|N/A|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words|O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map`|Java|147| +|N/A|[Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)|Medium|[Array, BFS, Backtracking, DFS]|O(2^n)||Java|148| +|N/A|[One Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/One%20Edit%20Distance.java)|Medium|[String]|||Java|149| +|N/A|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|150| +|N/A|[Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)|Hard|[DP, String]|||Java|151| +|N/A|[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|[BST]|||Java|152| +|N/A|[Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)|Medium|[Array, Two Pointers]|||Java|153| +|N/A|[Word Ladder.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder.java)|Medium|[BFS]|||Java|154| +|N/A|[Single Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20II.java)|Medium|[Bit Manipulation]|||Java|155| +|N/A|[Heaters.java](https://github.com/awangdev/LintCode/blob/master/Java/Heaters.java)|Easy|[]|||Java|156| +|N/A|[Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)|Medium|[BST, DFS, Stack, Tree]|||Java|157| +|N/A|[Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)|Hard|[Backtracking, DFS]|||Java|158| +|N/A|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|[Array, DP, Game Theory, Memoization, MiniMax]|||Java|159| +|N/A|[Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)|Medium|[Linked List, Two Pointers]|||Java|160| +|N/A|[Classical Binary Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Classical%20Binary%20Search.java)|Easy|[Binary Search]|||Java|161| +|N/A|[Wood Cut.java](https://github.com/awangdev/LintCode/blob/master/Java/Wood%20Cut.java)|Medium|[Binary Search]|||Java|162| +|N/A|[Connecting Graph III.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20III.java)|Medium|[Union Find]|||Java|163| +|N/A|[Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)|Easy|[BFS, DFS, Tree]|||Java|164| +|N/A|[Remove Duplicates from Unsorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Unsorted%20List.java)|Medium|[Linked List]|||Java|165| +|N/A|[Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)|Medium|[Hash Table, PreSum, Subarray]|O(n)|O(n)|Java|166| +|N/A|[The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)|Medium|[Array, Sort, Two Pointers]|||Java|167| +|N/A|[Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)|Medium|[BST, DP, Divide and Conquer, Tree]|||Java|168| +|N/A|[Encode and Decode Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20Strings.java)|Medium|[String]|||Java|169| +|N/A|[Remove Duplicates from Sorted List II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List%20II.java)|Medium|[Linked List]|||Java|170| +|N/A|[Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)|Hard|[Array, Binary Search, Two Pointers]|||Java|171| +|N/A|[Matrix Zigzag Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/Matrix%20Zigzag%20Traversal.java)|Easy|[]|||Java|172| +|N/A|[Ones and Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Ones%20and%20Zeroes.java)|Hard|[DP]|||Java|173| +|N/A|[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|[BFS, DFS, Graph, Union Find]|||Java|174| +|N/A|[Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)|Medium|[Array, Hash Table, PreSum]|||Java|175| +|N/A|[Zigzag Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Zigzag%20Iterator.java)|Medium|[BST]|||Java|176| +|N/A|[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|[BFS, DFS]|||Java|177| +|N/A|[Implement Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack.java)|Easy|[Stack]|||Java|178| +|N/A|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|[Array, Interval, PriorityQueue, Sort, Sweep Line]|||Java|179| +|N/A|[Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)|Medium|[BFS, DFS, Matrix DFS, Union Find]|||Java|180| +|N/A|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|||Java|181| +|N/A|[Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)|Hard|[Backtracking, DFS, Divide and Conquer, String]|O(4^n)|O(4^n)|Java|182| +|N/A|[Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)|Hard|[DFS, Greedy, Math]|||Java|183| +|N/A|[Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)|Medium|[Design, Hash Table]|||Java|184| +|N/A|[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|[DP, Sequence DP]|||Java|185| +|N/A|[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|[Array, Binary Search]|||Java|186| +|N/A|[Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)|Hard|[Coordinate DP, Stack, String]|||Java|187| +|N/A|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|[DP, Enumeration, Heap, Math, PriorityQueue]|O(n)|O(n)|Java|188| +|N/A|[Add Two Numbers II.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers%20II.java)|Medium|[Linked List]|||Java|189| +|N/A|[Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)|Review|[Array, Binary Search, PreSum]|||Java|190| +|N/A|[Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)|Hard|[Binary Tree, Expression Tree, Minimum Binary Tree, Stack]|||Java|191| +|N/A|[Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)|Easy|[DFS, Tree]|||Java|192| +|N/A|[Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)|Hard|[Binary Search, DP, Partition DP]|||Java|193| +|N/A|[Power of Three.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Three.java)|Easy|[Math]|||Java|194| +|N/A|[Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)|Medium|[Partition, Quick Sort, Sort, Two Pointers]|||Java|195| +|N/A|[Maximum Subarray III.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20III.java)|Review|[]|||Java|196| +|N/A|[Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)|Easy|[Backtracking, DFS, Tree]|||Java|197| +|N/A|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|198| +|N/A|[Shortest Distance from All Buildings.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Distance%20from%20All%20Buildings.java)|Hard|[BFS]|||Java|199| +|N/A|[Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)|Medium|[Hash Table]|O(mn)|O(X), X = max wall width|Java|200| +|N/A|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|||Java|201| +|N/A|[Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)|Hard|[DP, String]|||Java|202| +|N/A|[Shuffle an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Shuffle%20an%20Array.java)|Medium|[Permutation]|||Java|203| +|N/A|[Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)|Hard|[BST, DFS, Tree]|||Java|204| +|N/A|[My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)|Medium|[Array, TreeMap]|||Java|205| +|N/A|[Evaluate Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Reverse%20Polish%20Notation.java)|Medium|[Stack]|O(n)|O(n)|Java|206| +|N/A|[Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)|Medium|[Bit Manipulation, Bitwise DP, DP]|||Java|207| +|N/A|[Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)|Medium|[Partition, Sort, String, Two Pointers]|||Java|208| +|N/A|[Two Strings Are Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Strings%20Are%20Anagrams.java)|Easy|[]|||Java|209| +|N/A|[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|[Array, Binary Search, Two Pointers]|||Java|210| +|N/A|[[HackerRank]. Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/[HackerRank].%20Change%20to%20Anagram.java)|Easy|[String]|||Java|211| +|N/A|[Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)|Easy|[Design, Stack]|||Java|212| +|N/A|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|||Java|213| +|N/A|[Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)|Hard|[Backtracking, Trie]|||Java|214| +|N/A|[Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)|Medium|[Linked List, Sort]|||Java|215| +|N/A|[Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)|Medium|[Binary Search, Lint, Segment Tree]|||Java|216| +|N/A|[Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)|Medium|[DFS, Enumeration, Math, Sequence DFS]|||Java|217| +|N/A|[The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)|Medium|[BFS, DFS, PriorityQueue]|||Java|218| +|N/A|[k Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/k%20Sum.java)|Hard|[DP]|||Java|219| +|N/A|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|[Array, DP, Game Theory, Interval DP, Memoization]|||Java|220| +|N/A|[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|[BST, DFS, Divide and Conquer, Linked List]|||Java|221| +|N/A|[Guess Number Higher or Lower.java](https://github.com/awangdev/LintCode/blob/master/Java/Guess%20Number%20Higher%20or%20Lower.java)|Easy|[Binary Search]|||Java|222| +|N/A|[Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)|Hard|[BFS, Heap, MinHeap, PriorityQueue]|||Java|223| +|N/A|[Bricks Falling When Hit.java](https://github.com/awangdev/LintCode/blob/master/Java/Bricks%20Falling%20When%20Hit.java)|Hard|[Union Find]|||Java|224| +|N/A|[Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)|Medium|[PreSum, PriorityQueue, Sort, Subarray]|O(nlogn)|O(n)|Java|225| +|N/A|[Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)|Hard|[DP, Divide and Conquer, Interval DP, Memoization]|||Java|226| +|N/A|[Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)|Easy|[Array, Two Pointers]|||Java|227| +|N/A|[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|[DP]|||Java|228| +|N/A|[Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)|Hard|[DP, Partition DP]|||Java|229| +|N/A|[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|[Linked List, Stack, Tree]|O(n)|O(n)|Java|230| +|N/A|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|||Java|231| +|N/A|[Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)|Hard|[BFS, Graph]|||Java|232| +|N/A|[Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)|Hard|[Binary Search, Lint, Segment Tree]|||Java|233| +|N/A|[Add Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Digits.java)|Easy|[Math]|||Java|234| +|N/A|[HashWithCustomizedClass(LinkedList).java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithCustomizedClass(LinkedList).java)|Medium|[Hash Table]|||Java|235| +|N/A|[Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)|Hard|[DP]|||Java|236| +|N/A|[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|[DFS, Divide and Conquer, Tree]|O(n)|O(n)|Java|237| +|N/A|[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|[Binary Search, Heap]|O(n + klogn)|O(n)|Java|238| +|N/A|[Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)|Medium|[Array, Backtracking, Combination, DFS]|||Java|239| +|N/A|[Last Position of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Last%20Position%20of%20Target.java)|Easy|[Binary Search]|||Java|240| +|N/A|[Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)|Easy|[DFS, Double Recursive, Tree]|||Java|241| +|N/A|[Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)|Hard|[Binary Tree, DFS, Expression Tree, Stack]|||Java|242| +|N/A|[Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)|Easy|[BFS, Tree]|||Java|243| +|N/A|[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|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(n), O(1) rolling array|Java|244| +|N/A|[Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)|Medium|[Binary Search, Math]|||Java|245| +|N/A|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|||Java|246| +|N/A|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|[Array, Partition, Quick Sort, Sort, Two Pointers]|||Java|247| +|N/A|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|[Array, BFS, Backtracking, DFS, Hash Table, String]|||Java|248| +|N/A|[Sum of Two Integers.java](https://github.com/awangdev/LintCode/blob/master/Java/Sum%20of%20Two%20Integers.java)|Easy|[Bit Manipulation]|||Java|249| +|N/A|[Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)|Medium|[DP, MiniMax]|||Java|250| +|N/A|[Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)|Medium|[Union Find]|||Java|251| +|N/A|[Search Insert Position.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Insert%20Position.java)|Easy|[]|||Java|252| +|N/A|[Longest Univalue Path.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Univalue%20Path.java)|Easy|[]|||Java|253| +|N/A|[Contains Duplicate III.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20III.java)|Medium|[BST]|||Java|254| +|N/A|[Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)|Medium|[Array, Enumeration]|||Java|255| +|N/A|[Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)|Medium|[Basic Implementation, Enumeration, String]|||Java|256| +|N/A|[Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)|Medium|[Hash Table, String]|||Java|257| +|N/A|[The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)|Hard|[BFS, DFS, PriorityQueue]|||Java|258| +|N/A|[Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)|Medium|[DP, Game Theory, Greedy]|||Java|259| +|N/A|[Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)|Medium|[DFS, Divide and Conquer, Tree]|||Java|260| +|N/A|[The Spiral Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Spiral%20Matrix%20II.java)|Medium|[Array]|||Java|261| +|N/A|[Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)|Easy|[BST, Tree]|||Java|262| +|N/A|[Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)|Medium|[DP, Math]|||Java|263| +|N/A|[Queue Reconstruction by Height.java](https://github.com/awangdev/LintCode/blob/master/Java/Queue%20Reconstruction%20by%20Height.java)|Medium|[Greedy]|||Java|264| +|N/A|[Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)|Medium|[Coordinate DP, DP, Status DP]|||Java|265| +|N/A|[Interleaving Positive and Negative Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20Positive%20and%20Negative%20Numbers.java)|Medium|[Two Pointers]|||Java|266| +|N/A|[Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)|Medium|[DFS, Hash Table, Tree]|||Java|267| +|N/A|[Excel Sheet Column Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Number.java)|Easy|[Math]|||Java|268| +|N/A|[Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)|Medium|[DFS, DP]|||Java|269| +|N/A|[Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)|Medium|[Array, Quick Sort, Sort, Two Pointers]|||Java|270| +|N/A|[Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)|Hard|[BFS]|||Java|271| +|N/A|[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|[Array, BST, Binary Search, DP, Queue, TreeSet]|||Java|272| +|N/A|[String Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/String%20Permutation.java)|Easy|[]|||Java|273| +|N/A|[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|[Bit Manipulation, Trie]|||Java|274| +|N/A|[Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)|Medium|[Array, Binary Search]|||Java|275| +|N/A|[Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)|Medium|[Backtracking, Permutation]|||Java|276| +|N/A|[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|[DFS, Tree]|O(n)|O(1)|Java|277| +|N/A|[Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)|Easy|[Brainteaser, DP, Game Theory]|||Java|278| +|N/A|[Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)|Medium|[Array, Binary Search]|||Java|279| +|N/A|[Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)|Hard|[Array, Monotonous Stack, Stack]|||Java|280| +|[lint]|[[lint]. Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Merge%20k%20Sorted%20Arrays.java)|Medium|[Heap, MinHeap, PriorityQueue]|O(nlogk)|O(k)|Java|281| +|[lint]|[[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build%20II.java)|Medium|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|||Java|282| +|[lint]|[[lint]. Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Nth%20to%20Last%20Node%20in%20List.java)|Easy|[Linked List, Lint]|||Java|283| +|[lint]|[[lint]. Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Product%20of%20Array%20Exclude%20Itself.java)|Medium|[Array, Lint]|||Java|284| +|[lint]|[[lint]. Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Compare%20Strings.java)|Easy|[Lint, String]|||Java|285| +|[lint]|[[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Query.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|286| +|[lint]|[[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)|Hard|[HashHeap, Heap, Lint]|||Java|287| +|[lint]|[[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)|Easy|[Hash Table, Lint, String]|||Java|288| +|[lint]|[[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)|Medium|[Array, Hash Table, Lint]|O(n)|O(n)|Java|289| +|[lint]|[[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)|Medium|[Array, Lint, Two Pointers]|||Java|290| +|[lint]|[[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)|Easy|[Array, Lint, String]|||Java|291| +|[lint]|[[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)|Easy|[Hash Table, Lint, Tree]|||Java|292| +|[lint]|[[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)|Medium|[HashHeap, Heap, Lint, MinHeap]|||Java|293| +|[lint]|[[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)|Easy|[Array, Hash Table, Lint, PreSum, Subarray]|O(n)|O(n)|Java|294| +|[lint]|[[lint]. Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Recover%20Rotated%20Sorted%20Array.java)|Easy|[Array, Lint]|||Java|295| +|[lint]|[[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)|Medium|[Array, Binary Search, Lint, Two Pointers]|||Java|296| +|[lint]|[[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)|Medium|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|||Java|297| +|[tool]|[[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)|Medium|[Lint, Merge Sort, Sort]|O(mlogn)|O(n)|Java|298| +|[tool]|[[tool]. Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Hash%20Function.java)|Easy|[Hash Table, Lint]|O(1) get|O(n) store map|Java|299| +|[tool]|[[tool]. UnionFind.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20UnionFind.java)|Medium|[Lint, Union Find]|O(n), with Path Compression O(mN), with Union by Rank O(logN)|O(n)|Java|300| +|[tool]|[[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)|Medium|[BFS, DFS, Lint, Topological Sort]|O(V + E)|O(V + E)|Java|301| +|36|[36. Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/36.%20Valid%20Sudoku.java)|Easy|[Enumeration, Hash Table]|(mn)|(mn)|Java|302| +|359|[359. Logger Rate Limiter.java](https://github.com/awangdev/LintCode/blob/master/Java/359.%20Logger%20Rate%20Limiter.java)|Easy|[Design, Hash Table]|O(1)|O(n)|Java|303| +|198|[198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)|Easy|[DP, Sequence DP, Status DP]|O(n)|O(n) or rolling array O(1)|Java|304| +|21|[21. Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/21.%20Merge%20Two%20Sorted%20Lists.java)|Easy|[Linked List]|O(n)|O(1)|Java|305| +|102|[102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|306| +|788|[788. Rotated Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/788.%20Rotated%20Digits.java)|Easy|[Basic Implementation, String]|O(n)|O(n)|Java|307| +|42|[42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)|Hard|[Array, Stack, Two Pointers]|O(n)|O(1)|Java|308| +|347|[347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|O(n)|O(n)|Java|309| +|269|[269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)|Hard|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n), n = # of graph edges|O(n)|Java|310| +|237|[237. Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/237.%20Delete%20Node%20in%20a%20Linked%20List.java)|Easy|[Linked List]|||Java|311| +|142|[142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)|Medium|[Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|312| +|448|[448. Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/448.%20Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)|Easy|[Array, Bucket Sort]|O(n)|O(1)|Java|313| +|360|[360. Sort Transformed Array.java](https://github.com/awangdev/LintCode/blob/master/Java/360.%20Sort%20Transformed%20Array.java)|Medium|[Math, Two Pointers]|O(n)|O(n) store result|Java|314| +|22|[22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%20Parentheses.java)|Medium|[Backtracking, DFS, Sequence DFS, String]|O(2^n)|O(2^n)|Java|315| +|849|[849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)|Easy|[Array, Basic Implementation, Two Pointers]|O(n)|O(1)|Java|316| +|408|[408. Valid Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/408.%20Valid%20Word%20Abbreviation.java)|Easy|[Basic Implementation, String]|||Java|317| +|415|[415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)|Easy|[Basic Implementation, Math, String]|O(n)|O(n)|Java|318| +|83|[83. Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/83.%20Remove%20Duplicates%20from%20Sorted%20List.java)|Easy|[Linked List]|||Java|319| +|1108|[1108. Defanging an IP Address.java](https://github.com/awangdev/LintCode/blob/master/Java/1108.%20Defanging%20an%20IP%20Address.java)|Easy|[Basic Implementation, String]|||Java|320| +|1021|[1021. Remove Outermost Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1021.%20Remove%20Outermost%20Parentheses.java)|Easy|[Stack]|||Java|321| +|236|[236. Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/236.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)|Medium|[DFS, Tree]|O(n)|O(n)|Java|322| +|766|[766. Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/766.%20Toeplitz%20Matrix.java)|Easy|[Array]|O(mn)|O(1)|Java|323| +|953|[953. Verifying an Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/953.%20Verifying%20an%20Alien%20Dictionary.java)|Easy|[Hash Table]|O(nm)|O(1)|Java|324| +|1053|[1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)|Medium|[Array, Greedy, Permutation]|O(n)|O(1)|Java|325| +|1213|[1213. Intersection of Three Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/1213.%20Intersection%20of%20Three%20Sorted%20Arrays.java)|Easy|[Hash Table, Two Pointers]|O(m + n + h) two pointers approach|O(1)|Java|326| +|383|[383. Ransom Note.java](https://github.com/awangdev/LintCode/blob/master/Java/383.%20Ransom%20Note.java)|Easy|[Basic Implementation, String]|||Java|327| +|56|[56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)|Medium|[Array, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|328| +|252|[252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)|Easy|[PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(1)|Java|329| +|665|[665. Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/665.%20Non-decreasing%20Array.java)|Easy|[Array]|O(n)|O(1)|Java|330| +|843|[843. Guess the Word.java](https://github.com/awangdev/LintCode/blob/master/Java/843.%20Guess%20the%20Word.java)|Hard|[MiniMax]|TODO|TODO|Java|331| +|986|[986. Interval List Intersections.java](https://github.com/awangdev/LintCode/blob/master/Java/986.%20Interval%20List%20Intersections.java)|Medium|[Two Pointers]|O(n)|O(1)|Java|332| +|76|[76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)|Hard|[Hash Table, Sliding Window, String, Two Pointers]|O(n)|O(1)|Java|333| +|293|[293. Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/293.%20Flip%20Game.java)|Easy|[String]|||Java|334| +|244|[244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)|Medium|[Array, Design, Hash Table, Two Pointers]|O(n) to build map, O(a + b) to query|O(n)|Java|335| +|686|[686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)|Easy|[Basic Implementation, Edge Case, String]|||Java|336| +|80|[80.Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/80.Remove%20Duplicates%20from%20Sorted%20Array%20II.java)|Medium|[Array, Two Pointers]|||Java|337| +|301|[301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)|Hard|[BFS, DFS, DP]|||Java|338| +|111|[111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%20Depth%20of%20Binary%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(n)|Java|339| +|1216|[1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)|Hard|[DFS, DP, Memoization, String]|O(n^2)|O(n^2)|Java|340| +|7|[7. Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/7.%20Reverse%20Integer.java)|Easy|[Math]|O(n)|O(1)|Java|341| +|5|[5. Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/5.%20Longest%20Palindromic%20Substring.java)|Medium|[DP, String]|O(n^2)|O(n^2)|Java|342| +|303|[303. Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/303.%20Range%20Sum%20Query%20-%20Immutable.java)|Easy|[DP, PreSum]|O(1) query, O(n) setup|O(n)|Java|343| +|674|[674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)|Easy|[Array, Coordinate DP, DP, Sliding Window]|O(n)|O(1)|Java|344| +|1007|[1007. Minimum Domino Rotations For Equal Row.java](https://github.com/awangdev/LintCode/blob/master/Java/1007.%20Minimum%20Domino%20Rotations%20For%20Equal%20Row.java)|Medium|[Array, Greedy]|O(n)|O(1)|Java|345| +|485|[485. Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/485.%20Max%20Consecutive%20Ones.java)|Easy|[Array, Basic Implementation]|O(n)|O(1)|Java|346| +|896|[896. Monotonic Array.java](https://github.com/awangdev/LintCode/blob/master/Java/896.%20Monotonic%20Array.java)|Easy|[Array]|||Java|347| +|207|[207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)|Medium|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|348| +|327|[327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)|Hard|[BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree]|O(nlogn)|O(n)|Java|349| +|987|[987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)|Medium|[BFS, Binary Tree, DFS, Hash Table, Tree]|||Java|350| +|26|[26.Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/26.Remove%20Duplicates%20from%20Sorted%20Array.java)|Easy|[Array, Two Pointers]|||Java|351| +|429|[429. N-ary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/429.%20N-ary%20Tree%20Level%20Order%20Traversal.java)|Medium|[BFS, Tree]|O(n)|O(n)|Java|352| +|275|[275. H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/275.%20H-Index%20II.java)|Medium|[Binary Search]|O(logN)|O(1) extra|Java|353| +|204|[204. Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/204.%20Count%20Primes.java)|Easy|[Hash Table, Math]|||Java|354| +|58|[58. Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/58.%20Length%20of%20Last%20Word.java)|Easy|[String]|||Java|355| +|496|[496. Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/496.%20Next%20Greater%20Element%20I.java)|Easy|[Hash Table, Stack]|O(n)|O(n)|Java|356| +|41|[41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)|Hard|[Analysis, Array, Edge Case]|O(n)|O(1)|Java|357| +|694|[694. Number of Distinct Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/694.%20Number%20of%20Distinct%20Islands.java)|Medium|[DFS, Hash Table]|O(n)|O(n)|Java|358| +|717|[717. 1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/717.%201-bit%20and%202-bit%20Characters.java)|Easy|[Array]|||Java|359| +|53|[53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)|Easy|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|O(n)|O(n), O(1) rolling array|Java|360| +|152|[152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)|Medium|[Array, DP, PreProduct, Subarray]|O(n)|O(1)|Java|361| +|199|[199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%20Tree%20Right%20Side%20View.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|362| +|259|[259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)|Medium|[Array, Sort, Two Pointers]|||Java|363| +|977|[977. Squares of a Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/977.%20Squares%20of%20a%20Sorted%20Array.java)|Easy|[Array, Two Pointers]|O(n)|O(n)|Java|364| +|824|[824. Goat Latin.java](https://github.com/awangdev/LintCode/blob/master/Java/824.%20Goat%20Latin.java)|Easy|[Basic Implementation, String]|O(n)|O(1)|Java|365| +|308|[308. Range Sum Query 2D - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/308.%20Range%20Sum%20Query%202D%20-%20Mutable.java)|Hard|[Binary Indexed Tree, Segment Tree]|build(n), update(logn), rangeRuery(logn + k)|O(n)|Java|366| +|1203|[1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)|Hard|[BFS, DFS, Graph, Topological Sort]|O(V + E) to traverse the graph, #nodes + #edges|O(V + E)|Java|367| +|1153|[1153. String Transforms Into Another String.java](https://github.com/awangdev/LintCode/blob/master/Java/1153.%20String%20Transforms%20Into%20Another%20String.java)|Hard|[Graph]|O(n)|O(n)|Java|368| +|1008|[1008. Construct Binary Search Tree from Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/1008.%20Construct%20Binary%20Search%20Tree%20from%20Preorder%20Traversal.java)|Medium|[DFS, Tree]|O(n)|O(n)|Java|369| +|151|[151. Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/151.%20Reverse%20Words%20in%20a%20String.java)|Medium|[String]|O(n)||Java|370| +|855|[855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)|Medium|[PriorityQueue, Sort, TreeMap, TreeSet]|O(logn)|O(n)|Java|371| +|31|[31. Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/31.%20Next%20Permutation.java)|Medium|[Array, Permutation]|O(n)|O(1)|Java|372| +|518|[518. Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/518.%20Coin%20Change%202.java)|Medium|[Backpack DP, DP]|O(n)|O(n)|Java|373| +|405|[405. Convert a Number to Hexadecimal.java](https://github.com/awangdev/LintCode/blob/master/Java/405.%20Convert%20a%20Number%20to%20Hexadecimal.java)|Easy|[Bit Manipulation]|||Java|374| +|850|[850. Rectangle Area II.java](https://github.com/awangdev/LintCode/blob/master/Java/850.%20Rectangle%20Area%20II.java)|Hard|[Segment Tree, Sweep Line]|O(n^2)|O(n)|Java|375| +|515|[515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|376| +|253|[253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)|Medium|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|377| +|1161|[1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)|Medium|[BFS, DFS, Graph]|O(n) visit all nodes|O(n)|Java|378| +|509|[509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.java)|Easy|[DP, Math, Memoization]|||Java|379| +|221|[221. Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/221.%20Maximal%20Square.java)|Medium|[Coordinate DP, DP]|O(mn)|O(mn)|Java|380| +|131|[131. Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/131.%20Palindrome%20Partitioning.java)|Medium|[Backtracking, DFS]|O(2^n)|O(n^2)|Java|381| +|136|[136. Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/136.%20Single%20Number.java)|Easy|[Bit Manipulation, Hash Table]|||Java|382| +|222|[222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)|Medium|[Binary Search, DFS, Tree]|O(n)|O(h)|Java|383| +|257|[257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%20Tree%20Paths.java)|Easy|[Backtracking, Binary Tree, DFS]|O(n)|O(nlogn)|Java|384| +|543|[543. Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/543.%20Diameter%20of%20Binary%20Tree.java)|Easy|[Tree]|O(n) when non-balanced|O(n) when non-balanced|Java|385| +|398|[398. Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/398.%20Random%20Pick%20Index.java)|Medium|[Reservior Sampling]|O(n)|O(n) for input int[], O(1) extra space used|Java|386| +|238|[238. Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/238.%20Product%20of%20Array%20Except%20Self.java)|Medium|[Array, PreProduct]|O(n)|O(1)|Java|387| +|1060|[1060. Missing Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1060.%20Missing%20Element%20in%20Sorted%20Array.java)|Medium|[Binary Search]|O(logn)|O(1)|Java|388| +|1048|[1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)|Medium|[Bucket Sort, DP, Hash Table, Sort]|O(n)|O(n)|Java|389| +|67|[67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%20Binary.java)|Easy|[Math, String, Two Pointers]|||Java|390| +|299|[299. Bulls and Cows.java](https://github.com/awangdev/LintCode/blob/master/Java/299.%20Bulls%20and%20Cows.java)|Medium|[Hash Table]|O(n)|O(n)|Java|391| +|557|[557. Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/557.%20Reverse%20Words%20in%20a%20String%20III.java)|Easy|[String]|||Java|392| +|203|[203. Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/203.%20Remove%20Linked%20List%20Elements.java)|Easy|[Linked List]|||Java|393| +|1219|[1219. Path with Maximum Gold.java](https://github.com/awangdev/LintCode/blob/master/Java/1219.%20Path%20with%20Maximum%20Gold.java)|Medium|[Backtracking, DFS]|O(n^2)|O(n) recursive depth|Java|394| +|266|[266. Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/266.%20Palindrome%20Permutation.java)|Easy|[Hash Table]|O(n)|O(n)|Java|395| +|62|[62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(mn), rolling array O(n)|Java|396| +|1091|[1091. Shortest Path in Binary Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/1091.%20Shortest%20Path%20in%20Binary%20Matrix.java)|Medium|[BFS]|O(n^2)||Java|397| +|1110|[1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)|Medium|[DFS, Divide and Conquer, Tree]|O(n)|O(logn)|Java|398| +|1249|[1249. Minimum Remove to Make Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1249.%20Minimum%20Remove%20to%20Make%20Valid%20Parentheses.java)|Medium|[Stack, String]|O(n)|O(n)|Java|399| +|15|[15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)|Medium|[Array, Sort, Two Pointers]|O(n^2)||Java|400| +|311|[311. Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/311.%20Sparse%20Matrix%20Multiplication.java)|Medium|[Hash Table]|O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row`|O(1) extra|Java|401| +|339|[339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)|Easy|[BFS, DFS, NestedInteger]|O(n)|O(h), h = levels|Java|402| +|322|[322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)|Medium|[Backpack DP, DFS, DP, Memoization]|O(n * S)|O(S)|Java|403| +|55|[55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%20Game.java)|Medium|[Array, DP, Greedy]|O(n)|O(1)|Java|404| +|173|[173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%20Search%20Tree%20Iterator.java)|Medium|[BST, Design, Stack, Tree]|O(1) average|O(h)|Java|405| +|140|[140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)|Hard|[Backtracking, DFS, DP, Hash Table, Memoization]|O(n!)|O(n!)|Java|406| +|51|[51. N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/51.%20N-Queens.java)|Hard|[Backtracking]|O(n!)|O(n^2)|Java|407| +|875|[875. Koko Eating Bananas.java](https://github.com/awangdev/LintCode/blob/master/Java/875.%20Koko%20Eating%20Bananas.java)|Medium|[Binary Search]|O(n*logM)|O(1)|Java|408| +|189|[189. Rotate Array.java](https://github.com/awangdev/LintCode/blob/master/Java/189.%20Rotate%20Array.java)|Easy|[Array, Rotation]|||Java|409| +|19|[19. Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/19.%20Remove%20Nth%20Node%20From%20End%20of%20List.java)|Medium|[Linked List, Two Pointers]|O(n)|O(1)|Java|410| +|134|[134. Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/134.%20Gas%20Station.java)|Medium|[Greedy]|O(n)|O(1)|Java|411| +|119|[119. Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/119.%20Pascal's%20Triangle%20II.java)|Easy|[Array, Basic Implementation]|O(k^2), pascal triangle size|O(k^2)|Java|412| +|1197|[1197. Minimum Knight Moves.java](https://github.com/awangdev/LintCode/blob/master/Java/1197.%20Minimum%20Knight%20Moves.java)|Medium|[BFS]|O(8^n)|O(8^n)|Java|413| +|493|[493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)|Medium|[BST, Binary Indexed Tree, Divide and Conquer, Merge Sort, Segment Tree]|||Java|414| +|1306|[1306. Jump Game III.java](https://github.com/awangdev/LintCode/blob/master/Java/1306.%20Jump%20Game%20III.java)|Medium|[BFS, Graph]|O(n)|O(n)|Java|415| +|305|[305. Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/305.%20Number%20of%20Islands%20II.java)|Hard|[Union Find]|O(k * log(mn))|O(mn)|Java|416| +|206|[206. Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/206.%20Reverse%20Linked%20List.java)|Easy|[Linked List]|||Java|417| +|277|[277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)|Medium|[Adjacency Matrix, Array, Graph, Greedy, Pruning]|O(n)|O(1)|Java|418| +|741|[741. Cherry Pickup.java](https://github.com/awangdev/LintCode/blob/master/Java/741.%20Cherry%20Pickup.java)|Hard|[DFS, DP]|O(n^3)|O(n^3), memo size|Java|419| +|168|[168. Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/168.%20Excel%20Sheet%20Column%20Title.java)|Easy|[Math]|O(n)|O(1)|Java|420| +|104|[104. Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/104.%20Maximum%20Depth%20of%20Binary%20Tree.java)|Easy|[DFS, Tree]|||Java|421| +|349|[349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)|Easy|[Binary Search, Hash Table, Sort, Two Pointers]|O(m + n)|O(m + n)|Java|422| +|443|[443. String Compression.java](https://github.com/awangdev/LintCode/blob/master/Java/443.%20String%20Compression.java)|Easy|[Basic Implementation, String]|||Java|423| +|297|[297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|O(n)|O(n)|Java|424| +|46|[46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)|Medium|[BFS, Backtracking, DFS, Permutation]|O(n!)|O(n!)|Java|425| +|844|[844. Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/844.%20Backspace%20String%20Compare.java)|Easy|[Stack, Two Pointers]|O(n)|O(1)|Java|426| +|9|[9. Palindrome Number.java](https://github.com/awangdev/LintCode/blob/master/Java/9.%20Palindrome%20Number.java)|Easy|[Math]|||Java|427| +|1094|[1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)|Medium|[Greedy, Heap, PriorityQueue, Sort]|O(n)|O(1) only use bucket size 1000|Java|428| +|245|[245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)|Medium|[Array, Design, Hash Table, Two Pointers]|O(n)|O(1)|Java|429| +|1117|[1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)|Medium|[Lock, Semaphore, Thread]|||Java|430| +|973|[973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)|Medium|[Divide and Conquer, Heap, Sort]|O(klogk)|O(k)|Java|431| +|771|[771. Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/771.%20Jewels%20and%20Stones.java)|Easy|[Hash Table]|O(n)|O(n)|Java|432| +|200|[200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)|Medium|[BFS, DFS, Matrix DFS, Union Find]|O(n)|O(n)|Java|433| +|141|[141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)|Easy|[Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|434| +|567|[567. Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/567.%20Permutation%20in%20String.java)|Medium|[Sliding Window, Two Pointers]|O(m + n)|O(1)|Java|435| +|727|[727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)|Hard|[DP, Hash Table, Sliding Window, String, Two Pointers]|O(n^2)|O(1)|Java|436| +|158|[158. Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/158.%20Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)|Hard|[Enumeration, String]|O(n)|O(n)|Java|437| +|369|[369. Plus One Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/369.%20Plus%20One%20Linked%20List.java)|Medium|[Linked List]|O(n)|O(1)|Java|438| +|211|[211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)|Medium|[Backtracking, Design, Trie]|O(n) to search and to add word|< O(mn), depends on the input. m = # of words|Java|439| +|43|[43. Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/43.%20Multiply%20Strings.java)|Medium|[Math, String]|O(mn)|O(mn)|Java|440| +|621|[621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)|Medium|[Array, Enumeration, Greedy, PriorityQueue, Queue]|O(n)|O(1)|Java|441| +|680|[680. Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/680.%20Valid%20Palindrome%20II.java)|Easy|[String]|||Java|442| +|295|[295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)|Hard|[Design, Heap, MaxHeap, MinHeap]|O(1) get, O(logn) addNum|O(n)|Java|443| +|70|[70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%20Stairs.java)|Easy|[DP, Memoization, Sequence DP]|||Java|444| +|747|[747. Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/747.%20Largest%20Number%20At%20Least%20Twice%20of%20Others.java)|Easy|[Array]|||Java|445| +|315|[315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|O(nlogn)|O(n)|Java|446| +|239|[239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)|Hard|[Deque, Heap, Sliding Window]|O(n)|O(n)|Java|447| +|47|[47. Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/47.%20Permutations%20II.java)|Medium|[Backtracking, DFS]|||Java|448| +|332|[332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)|Medium|[Backtracking, DFS, Graph]|O(n^n)|O(m)|Java|449| +|88|[88. Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Search%20in%20Rotated%20Sorted%20Array%20II.java)|Medium|[Array, Binary Search]|O(logn), worst O(n)|O(1)|Java|450| +|561|[561. Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/561.%20Array%20Partition%20I.java)|Easy|[Array]|O(nlogn)|O(1)|Java|451| +|387|[387. First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/387.%20First%20Unique%20Character%20in%20a%20String.java)|Easy|[Hash Table, String]|O(n)|O(256) = O(1)|Java|452| +|345|[345. Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/345.%20Reverse%20Vowels%20of%20a%20String.java)|Easy|[String, Two Pointers]|||Java|453| +|39|[39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)|Medium|[Array, Backtracking, Combination, DFS]|O(k * 2^n), k = avg rst length|O(k) stack depth, if not counting result size|Java|454| +|10|[10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|||Java|455| +|367|[367. Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/367.%20Valid%20Perfect%20Square.java)|Easy|[Binary Search, Math]|O(logN)|O(1)|Java|456| +|270|[270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)|Easy|[BST, Binary Search, Tree]|O(logn)|O(1)|Java|457| +|28|[28. Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/28.%20Implement%20strStr().java)|Easy|[String, Two Pointers]|||Java|458| +|1106|[1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)|Hard|[DFS, Stack, String]|||Java|459| +|144|[144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)|Medium|[BFS, DFS, Stack, Tree]|O(n)|O(n)|Java|460| +|852|[852. Peak Index in a Mountain Array.java](https://github.com/awangdev/LintCode/blob/master/Java/852.%20Peak%20Index%20in%20a%20Mountain%20Array.java)|Easy|[Binary Search]|O(logn)|O(1)|Java|461| +|146|[146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)|Medium|[Design, Doubly Linked List, Hash Table, Linked List]|O(1)|O(1)|Java|462| +|110|[110. Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/110.%20Balanced%20Binary%20Tree.java)|Easy|[DFS, Tree]|||Java|463| +|1040|[1040. Moving Stones Until Consecutive II.java](https://github.com/awangdev/LintCode/blob/master/Java/1040.%20Moving%20Stones%20Until%20Consecutive%20II.java)|Medium|[Array, Sliding Window]|O(nlogn)|O(n)|Java|464| +|246|[246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)|Easy|[Enumeration, Hash Table, Math, Two Pointers]|O(n)|O(1)|Java|465| +|100|[100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(logn)|Java|466| +|307|[307. Range Sum Query - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/307.%20Range%20Sum%20Query%20-%20Mutable.java)|Medium|[Binary Indexed Tree, Segment Tree]|build O(n), query (logn +k), update O(logn)|O(n)|Java|467| +|88|[88. Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Merge%20Sorted%20Array.java)|Easy|[Array, Two Pointers]|O(n)|O(1)|Java|468| +|319|[319. Bulb Switcher.java](https://github.com/awangdev/LintCode/blob/master/Java/319.%20Bulb%20Switcher.java)|Medium|[Brainteaser, Math]|O(1)|O(1)|Java|469| +|112|[112. Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/112.%20Path%20Sum.java)|Easy|[DFS, Tree]|||Java|470| +|463|[463. Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/463.%20Island%20Perimeter.java)|Easy|[Hash Table]|O(n)||Java|471| +|170|[170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)|Easy|[Design, Hash Table, Memoization]|O(n)|O(n)|Java|472| +|122|[122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(1) greedy, O(n) dp|Java|473| +|715|[715. Range Module.java](https://github.com/awangdev/LintCode/blob/master/Java/715.%20Range%20Module.java)|Hard|[Segment Tree, TreeSet]|query O(logn), update O(n)|O(n)|Java|474| +|12|[12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)|Medium|[Basic Implementation, Math, String]|O(n)|O(n)|Java|475| +|14|[14. Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/14.%20Longest%20Common%20Prefix.java)|Easy|[String]|||Java|476| +|243|[243. Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/243.%20Shortest%20Word%20Distance.java)|Easy|[Array, Two Pointers]|O(n)|O(1)|Java|477| +|414|[414. Third Maximum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/414.%20Third%20Maximum%20Number.java)|Easy|[Array, PriorityQueue]|||Java|478| +|1267|[1267. Count Servers that Communicate.java](https://github.com/awangdev/LintCode/blob/master/Java/1267.%20Count%20Servers%20that%20Communicate.java)|Medium|[Array, Graph]|O(mn)|O(m + n)|Java|479| +|20|[20. Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/20.%20Valid%20Parentheses.java)|Easy|[Stack, String]|O(n)|O(n)|Java|480| +|893|[893. Groups of Special-Equivalent Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/893.%20Groups%20of%20Special-Equivalent%20Strings.java)|Easy|[Basic Implementation, String]|||Java|481| +|427|[427. Construct Quad Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/427.%20Construct%20Quad%20Tree.java)|Medium|[Tree]|O(n^2)|O(n^2)|Java|482| +|981|[981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)|Medium|[Binary Search, Hash Table, TreeMap]|set O(1), get(logn)|O(n)|Java|483| +|169|[169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)|Easy|[Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort]|O(n)|O(1)|Java|484| +|234|[234. Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/234.%20Palindrome%20Linked%20List.java)|Easy|[Linked List, Two Pointers]|O(n)|O(1)|Java|485| +|202|[202. Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/202.%20Happy%20Number.java)|Easy|[Hash Table, Math]|O(m), m iterations|O(m), m number in set|Java|486| +|69|[69. Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/69.%20Sqrt(x).java)|Easy|[Binary Search, Math]|||Java|487| +|876|[876. Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/876.%20Middle%20of%20Linked%20List.java)|Easy|[Linked List]|||Java|488| +|1026|[1026. Maximum Difference Between Node and Ancestor.java](https://github.com/awangdev/LintCode/blob/master/Java/1026.%20Maximum%20Difference%20Between%20Node%20and%20Ancestor.java)|Medium|[DFS, Tree]|O(n)|O(logn)|Java|489| +|78|[78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)|Medium|[Array, BFS, Backtracking, Bit Manipulation, DFS]|O(2^n)|O(2^n)|Java|490| +|432|[432. All One Data Structure.java](https://github.com/awangdev/LintCode/blob/master/Java/432.%20All%20One%20Data%20Structure.java)|Hard|[Design, Doubly Linked List]|O(1)|O(n)|Java|491| +|380|[380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)|Medium|[Array, Design, Hash Table]|O(1) avg|O(n)|Java|492| +|560|[560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)|Medium|[Array, Hash Table, PreSum, Subarray]|O(n)|O(n)|Java|493| +|219|[219. Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/219.%20Contains%20Duplicate%20II.java)|Easy|[Array, Hash Table]|O(n)|O(n)|Java|494| +|91|[91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)|Medium|[DP, Partition DP, String]|O(n)|O(n)|Java|495| +|205|[205. Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/205.%20Isomorphic%20Strings.java)|Easy|[Hash Table]|O(n)|O(n)|Java|496| +|639|[639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%20Ways%20II.java)|Hard|[DP, Enumeration, Partition DP]|O(n)|O(n)|Java|497| +|346|[346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)|Easy|[Design, Queue, Sliding Window]|O(1) for `next()`|O(size) for fixed storage|Java|498| +|145|[145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)|Medium|[Stack, Tree, Two Stacks]|O(n)|O(n)|Java|499| +|938|[938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)|Easy|[BST, Recursion, Tree]|||Java|500| +|210|[210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)|Medium|[BFS, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|501| +|68|[68. Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/68.%20Text%20Justification.java)|Hard|[Enumeration, String]|O(n) go over words|O(maxLength) buffer list|Java|502| +|314|[314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)|Medium|[BFS, DFS, Hash Table, Tree]|O(n)|O(n)|Java|503| +|287|[287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)|Medium|[Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|504| +|242|[242. Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/242.%20Valid%20Anagram.java)|Easy|[Hash Table, Sort]|O(n)|O(1), unique chars|Java|505| +|340|[340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)|Hard|[Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers]|O(n)|O(k)|Java|506| +|217|[217. Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/217.%20Contains%20Duplicate.java)|Easy|[Array, Hash Table]|O(n)|O(1)|Java|507| +|103|[103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)|Medium|[BFS, Stack, Tree]|O(n)|O(n)|Java|508| +|1057|[1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)|Medium|[Bucket Sort, Greedy, PriorityQueue, Sort]|O(mn)|O(mn)|Java|509| +|261|[261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)|Medium|[BFS, DFS, Graph, Union Find]|||Java|510| +|64|[64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%20Path%20Sum.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(n) rolling array|Java|511| +|796|[796. Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/796.%20Rotate%20String.java)|Easy|[String]|||Java|512| +|229|[229. Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/229.%20Majority%20Element%20II.java)|Medium|[Array, Moore Voting]|O(n)|(1)|Java|513| +|1041|[1041. Robot Bounded In Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/1041.%20Robot%20Bounded%20In%20Circle.java)|Easy|[String]|||Java|514| +|2|[2. Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/2.%20Add%20Two%20Numbers.java)|Medium|[Linked List, Math]|O(max(m,n))|O(max(m,n))|Java|515| +|157|[157. Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/157.%20Read%20N%20Characters%20Given%20Read4.java)|Easy|[Enumeration, String]|||Java|516| +|114|[114. Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/114.%20Flatten%20Binary%20Tree%20to%20Linked%20List.java)|Medium|[Binary Tree, DFS]|O(n)|O(n), stacks|Java|517| +|121|[121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)|Easy|[Array, DP, Sequence DP]|||Java|518| +|1004|[1004. Max Consecutive Ones III.java](https://github.com/awangdev/LintCode/blob/master/Java/1004.%20Max%20Consecutive%20Ones%20III.java)|Medium|[Sliding Window, Two Pointers]|O(n)|O(1)|Java|519| +|1146|[1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)|Medium|[Array, Hash Table, TreeMap]|O(1) set, O(logn) get, O(x) snap, x = # of changes|O(n * m), n = array size, m = # of snaps|Java|520| +|273|[273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%20to%20English%20Words.java)|Hard|[Enumeration, Math, String]|O(n)|O(1)|Java|521| +|304|[304. Range Sum Query 2D - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/304.%20Range%20Sum%20Query%202D%20-%20Immutable.java)|Medium|[DP, PreSum]|O(mn) build, O(1) query|O(mn)|Java|522| +|605|[605. Can Place Flowers.java](https://github.com/awangdev/LintCode/blob/master/Java/605.%20Can%20Place%20Flowers.java)|Easy|[Array, Greedy]|O(n)|O(1)|Java|523| +|1|[1. Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1.%20Two%20Sum.java)|Easy|[Array, Hash Table]|O(n)|O(n)|Java|524| +|118|[118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)|Easy|[Array, Basic Implementation, List]|O(n^2) based on pascal triangle size|O(n^2)|Java|525| +|23|[23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)|Medium|[Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue]|O(nlogk)|O(logk)|Java|526| +|283|[283. Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/283.%20Move%20Zeroes.java)|Easy|[Array, Two Pointers]|O(n)|O(1)|Java|527| +|208|[208. Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/208.%20Implement%20Trie%20(Prefix%20Tree).java)|Medium|[Design, Trie]|||Java|528| +|516|[516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%20Palindromic%20Subsequence.java)|Medium|[DFS, DP, Interval DP, Memoization]|O(n^2)|O(n^2)|Java|529| +|218|[218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)|Hard|[BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line]|O(n^2logn)|O(n)|Java|530| +|430|[430. Flatten a Multilevel Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/430.%20Flatten%20a%20Multilevel%20Doubly%20Linked%20List.java)|Medium|[DFS, Linked List]|O(n)|O(1)|Java|531| +|63|[63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%20Paths%20II.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(mn)|Java|532| +|52|[52. N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/52.%20N-Queens%20II.java)|Hard|[Backtracking]|O(n!)|O(n)|Java|533| +|1033|[1033. Moving Stones Until Consecutive.java](https://github.com/awangdev/LintCode/blob/master/Java/1033.%20Moving%20Stones%20Until%20Consecutive.java)|Easy|[Basic Implementation, Sort]|O(1), only 3 elements|O(1)|Java|534| +|139|[139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)|Medium|[DP, Hash Table, Sequence DP]|O(n^2)|O(n)|Java|535| +|105|[105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|[Array, DFS, Divide and Conquer, Hash Table, Tree]|O(n)|O(n)|Java|536| +|125|[125. Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/125.%20Valid%20Palindrome.java)|Easy|[String, Two Pointers]|||Java|537| +|449|[449. Serialize and Deserialize BST.java](https://github.com/awangdev/LintCode/blob/master/Java/449.%20Serialize%20and%20Deserialize%20BST.java)|Medium|[Tree]|O(n)|O(n)|Java|538| +|274|[274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.H-Index.java)|Medium|[Bucket Sort, Hash Table, Sort]|O(n)|O(n)|Java|539| +|160|[160. Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/160.%20Intersection%20of%20Two%20Linked%20Lists.java)|Easy|[Linked List]|||Java|540| +|40|[40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%20Sum%20II.java)|Medium|[Array, Backtracking, Combination, DFS]|O(k * 2^n), k = avg rst length|O(n) stack depth, if not counting result size|Java|541| +|410|[410. Split Array Largest Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/410.%20Split%20Array%20Largest%20Sum.java)|N/A|[]|||Java|542| +|724|[724. Find Pivot Index.java](https://github.com/awangdev/LintCode/blob/master/Java/724.%20Find%20Pivot%20Index.java)|Easy|[Array, PreSum]|O(n)|O(1)|Java|543| +|523|[523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)|Medium|[Coordinate DP, DP, Math, PreSum, Subarray]|O(n)|O(k)|Java|544| +|65|[65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)|Hard|[Enumeration, Math, String]|O(n)|O(1)|Java|545| +|350|[350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)|Easy|[Binary Search, Hash Table, Sort, Two Pointers]|(n)|(n)|Java|546| +|364|[364. Nested List Weight Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/364.%20Nested%20List%20Weight%20Sum%20II.java)|Medium|[DFS, NestedInteger]|O(n), visit all nodes|O(h), depth|Java|547| +|49|[49. Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/49.%20Group%20Anagrams.java)|Medium|[Hash Table, String]|O(nk)|O(nk)|Java|548| +|720|[720. Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/720.%20Longest%20Word%20in%20Dictionary.java)|Easy|[Hash Table, Trie]|O(nlogn)|O(n)|Java|549| +|438|[438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)|Medium|[Hash Table, Sliding Window, Two Pointers]|O(n)|O(1)|Java|550| +|632|[632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)|Hard|[Hash Table, Sliding Window, Two Pointers]|O(nlogn), n = total elements|O(n) to store sorted list|Java|551| +|138|[138. Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/138.%20Copy%20List%20with%20Random%20Pointer.java)|Medium|[Hash Table, Linked List]|O(n)|O(n)|Java|552| +|159|[159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)|Medium|[Hash Table, Sliding Window, String, Two Pointers]|O(n)|O(1)|Java|553| +|1043|[1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)|Medium|[DFS, DP, Graph, Memoization]|O(n), calc memo[n]|O(n)|Java|554| +|33|[33. Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/33.%20Search%20in%20Rotated%20Sorted%20Array.java)|Medium|[Array, Binary Search]|O(logn)|O(1)|Java|555| +|760|[760. Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/760.%20Find%20Anagram%20Mappings.java)|Easy|[Hash Table]|O(n)|O(n)|Java|556| +|133|[133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%20Graph.java)|Medium|[BFS, DFS, Graph]|O(n)|O(n)|Java|557| +|743|[743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)|Medium|[BFS, DFS, Graph, Heap, PQ]|O(nlogn)|O(n)|Java|558| +|636|[636. Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/636.%20Exclusive%20Time%20of%20Functions.java)|Medium|[Stack]|O(n)|O(n)|Java|559| +|692|[692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|O(n)|O(n)|Java|560| +|1170|[1170. Compare Strings by Frequency of the Smallest Character.java](https://github.com/awangdev/LintCode/blob/master/Java/1170.%20Compare%20Strings%20by%20Frequency%20of%20the%20Smallest%20Character.java)|Easy|[Array, String]|O(m + n)|O(m + n)|Java|561| +|426|[426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|[BST, DFS, Divide and Conquer, Linked List, Tree]|O(n)|O(1)|Java|562| +|745|[745. Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/745.%20Prefix%20and%20Suffix%20Search.java)|Hard|[Trie]|O(N + Q)|O(N)|Java|563| +|8|[8. String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/8.%20String%20to%20Integer%20(atoi).java)|Medium|[Math, String]|O(n)|O(n)|Java|564| +|361|[361. Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/361.%20Bomb%20Enemy.java)|Medium|[Coordinate DP, DP]|O(mn)|O(n) by calculating column sum|Java|565| +|94|[94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)|Easy|[Hash Table, Stack, Tree]|O(n)|O(logn)|Java|566| +|402|[402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)|Medium|[Greedy, Monotonous Stack, Stack]|O(n)|O(n)|Java|567| +|98|[98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)|Medium|[BST, DFS, Divide and Conquer, Tree]|O(n)|O(logn)|Java|568| +|1123|[1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|569| +|921|[921. Minimum Add to Make Parentheses Valid.java](https://github.com/awangdev/LintCode/blob/master/Java/921.%20Minimum%20Add%20to%20Make%20Parentheses%20Valid.java)|Medium|[]|O(n)|O(1)|Java|570| +|399|[399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%20Division.java)|Medium|[BFS, DFS, Graph, Union Find]|||Java|571| +|785|[785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)|Medium|[BFS, DFS, Garph]|O(n)|O(n)|Java|572| +|767|[767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)|Medium|[Greedy, Hash Table, Heap, Sort, String]|O(m), m = # of unique letters|O(nLogm), n = length|Java|573| +|71|[71. Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/71.%20Simplify%20Path.java)|Medium|[Stack, String]|O(n)|O(n)|Java|574| +|34|[34. Find First and Last Position of Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/34.%20Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array.java)|Medium|[Array, Binary Search]|O(logn)|O(1)|Java|575| +|278|[278. First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/278.%20First%20Bad%20Version.java)|Easy|[Binary Search]|O(logN)|O(1)|Java|576| +|124|[124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)|Hard|[DFS, DP, Tree, Tree DP]|O(n)|O(logn)|Java|577| +|721|[721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)|Medium|[DFS, Hash Table, Union Find]|||Java|578| +|689|[689. Maximum Sum of 3 Non-Overlapping Subarrays.java](https://github.com/awangdev/LintCode/blob/master/Java/689.%20Maximum%20Sum%20of%203%20Non-Overlapping%20Subarrays.java)|Hard|[Array, DP]|O(n)|O(n)|Java|579| +|101|[101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(n)|Java|580| +|149|[149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%20Points%20on%20a%20Line.java)|Hard|[Array, Geometry, Hash Table, Math]|O(n^2)|O()|Java|581| +|698|[698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)|Medium|[DFS, DP, Recursion]|O(k^(n-k) * k!)|O(n)|Java|582| +|57|[57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)|Hard|[Array, PriorityQueue, Sort, Sweep Line]|O(n)|O(n)|Java|583| +|13|[13. Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/13.%20Roman%20to%20Integer.java)|Easy|[Math, String]|O(n)|O(1)|Java|584| +|716|[716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)|Medium|[Design, Doubly Linked List, Stack, TreeMap]|avg O(1), [O(logN) peekMax(), TreeMap]; [O(n) popMax(), TwoStack]|O(n)|Java|585| +|671|[671. Second Minimum Node In a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/671.%20Second%20Minimum%20Node%20In%20a%20Binary%20Tree.java)|Easy|[BFS, Tree]|O(n)|O(n) leaf nodes|Java|586| +|366|[366. Find Leaves of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/366.%20Find%20Leaves%20of%20Binary%20Tree.java)|Medium|[DFS, Tree]|O(n)|O(h)|Java|587| +|235|[235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)|Easy|[BST, DFS, Tree]|O(logn)|O(logn)|Java|588| +|156|[156. Binary Tree Upside Down.java](https://github.com/awangdev/LintCode/blob/master/Java/156.%20Binary%20Tree%20Upside%20Down.java)|Medium|[DFS, Tree]|O(n)|O(h)|Java|589| +|416|[416. Partition Equal Subset Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/416.%20Partition%20Equal%20Subset%20Sum.java)|Medium|[Backpack, DP]|||Java|590| +|611|[611. Valid Triangle Number.java](https://github.com/awangdev/LintCode/blob/master/Java/611.%20Valid%20Triangle%20Number.java)|Medium|[Array, Two Pointers]|O(n^2)|O(logn), sorting space|Java|591| +|341|[341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)|Medium|[Design, NestedInteger, Stack]|O(n)|O(n)|Java|592| +|254|[254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)|Medium|[BFS, Backtracking, DFS]|O(x), x is the # of results|O(y), y is all ongoing candidates in queue|Java|593| +|739|[739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)|Medium|[Hash Table, Monotonous Stack, Stack]|O(n)|O(n)|Java|594| +|373|[373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)|Medium|[Heap, MaxHeap, MinHeap]|O(klogk)|O(k)|Java|595| +|256|[256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)|Easy|[DP, Sequence DP, Status DP]|O(nm), m = # of colors|O(nm), or O(1) with rolling array|Java|596| +|265|[265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)|Hard|[DP, Sequence DP, Status DP]|O(NK^2):|O(K) with rolling array|Java|597| +|272|[272. Closest Binary Search Tree Value II.java](https://github.com/awangdev/LintCode/blob/master/Java/272.%20Closest%20Binary%20Search%20Tree%20Value%20II.java)|Hard|[Stack, Tree]|O(n)|O(n)|Java|598| +|72|[72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)|Hard|[DP, Double Sequence DP, Sequence DP, String]|O(MN)||Java|599| +|215|[215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort]|O(nlogk)|O(k)|Java|600| diff --git a/ReviewPage.md b/ReviewPage.md new file mode 100644 index 0000000..535e77d --- /dev/null +++ b/ReviewPage.md @@ -0,0 +1,11806 @@ +# 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. [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 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 + + + +--- + +**1. [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哪一个。 + + + +--- + +**2. [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 + + + +--- + +**3. [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 + + + +--- + +**4. [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. + + + +--- + +**5. [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 + + + +--- + +**6. [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, 而不考虑是哪个. + + + + + + +--- + +**7. [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. + + + +--- + +**8. [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. + + + +--- + +**9. [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 + + + +--- + +**10. [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) + + + +--- + +**11. [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) + + + +--- + +**12. [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, 但是数字可为多位 + + + +--- + +**13. [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) 太慢, 不合题意 + + + +--- + +**14. [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了 + + + +--- + +**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. [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的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**17. [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), 排序 + + + +--- + +**18. [Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)** Level: Easy Tags: [String, Two Pointers] + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + + + +--- + +**19. [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);` + + + +--- + +**20. [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 + + + +--- + +**21. [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 + + + +--- + +**22. [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个数字 + + + +--- + +**23. [Single Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20III.java)** Level: Medium Tags: [Bit Manipulation] + +TODO: wut? + + +--- + +**24. [Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)** Level: Easy Tags: [Math] + + + +--- + +**25. [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,要乘一下。 + + + + +--- + +**26. [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 + + + +--- + +**27. [Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)** Level: Medium Tags: [Bit Manipulation] + + +给出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. + + + + +--- + +**28. [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一一对应。 + + + +--- + +**29. [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. + + + +--- + +**30. [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. + + + +--- + +**31. [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 + + + +--- + +**32. [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 一样) + + + +--- + +**33. [Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Hamming%20Distance.java)** Level: Easy Tags: [] + +bit: XOR, &, shift>> + + + +--- + +**34. [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拿出来就好了。 + + + +--- + +**35. [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 + + + +--- + +**36. [Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)** Level: Medium Tags: [DP] + +这个DP有点诡异. 需要斟酌。 +NOT DONE YET + + +--- + +**37. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**38. [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. + + +--- + +**39. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + + +#### 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题,想不到,就是搞不出。 + + + + +--- + +**40. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + + +给一串点, 找是否有一个所有点中间的, 跟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) + + + +--- + +**41. [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的要求! + + + +--- + +**42. [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)}` + + + +--- + +**43. [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) + + + +--- + +**44. [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 + + + +--- + +**45. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**46. [Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)** Level: Medium Tags: [Union Find] + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + + + +--- + +**47. [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 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**48. [Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)** Level: Easy Tags: [Bit Manipulation] + + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + + + +--- + +**49. [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) + + + +--- + +**50. [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 + + + +--- + +**51. [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开头 + + + +--- + +**52. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + + + +--- + +**53. [IndexMatch.java](https://github.com/awangdev/LintCode/blob/master/Java/IndexMatch.java)** Level: Easy Tags: [] + +有序, 假设有这样的数字:target. +target 左边的数字,一定不比index大,target右边的数字,一定比index大。 +这样可以binary search.O(logn) + + + +--- + +**54. [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()` + + + +--- + +**55. [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 + + + + +--- + +**56. [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. + + + +--- + +**57. [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 + + + +--- + +**58. [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 + + + +--- + +**59. [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 + + + +--- + +**60. [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)) + + + + +--- + +**61. [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了。 + + + +--- + +**62. [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 + + + + +--- + +**63. [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 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**64. [Reshape the Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Reshape%20the%20Matrix.java)** Level: Easy Tags: [] + +读例子理解题意. +理清counter case. Basic implementation + + + +--- + +**65. [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 + + + +--- + +**66. [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] + + + +--- + +**67. [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. + + + +--- + +**68. [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许多。 + + + + +--- + +**69. [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) + + + +--- + +**70. [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] + + + +--- + +**71. [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 + + + +--- + +**72. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + +--- + +**73. [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] + + +给一串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 + + + +--- + +**74. [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()里, 每次换水,查看末尾项. + + + + +--- + +**75. [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 + + + +--- + +**76. [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的头。 + + + + + +--- + +**77. [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). + + + + +--- + +**78. [HashWithArray.java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithArray.java)** Level: Easy Tags: [] + + + + +--- + +**79. [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 + + + +--- + +**80. [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 + + + +--- + +**81. [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) + + + + +--- + +**82. [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. + + + +--- + +**83. [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,可惜错了。 + + + + +--- + +**84. [Orderly Queue.java](https://github.com/awangdev/LintCode/blob/master/Java/Orderly%20Queue.java)** Level: Hard Tags: [Math, String] + + + + +--- + +**85. [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) + + + +--- + +**86. [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? + + + +--- + +**87. [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. + + + +--- + +**88. [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. + + + +--- + +**89. [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 + + + +--- + +**90. [Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)** Level: Easy Tags: [Basic Implementation] + +根据 Cosine Similarity 的公式, basic implementation + + + +--- + +**91. [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, 看解答 + + + +--- + +**92. [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. + + + +--- + +**93. [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, 换位子 + + + +--- + +**94. [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. + + + +--- + +**95. [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作比较;求的所有情况的最大值嘛。 + + + +--- + +**96. [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), 太慢. + + + +--- + +**97. [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开始选。 + + + +--- + +**98. [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, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**99. [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/ + + + +--- + +**100. [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 是理所当然的 + + + +--- + +**101. [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一下。 + + + + +--- + +**102. [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 + + + +--- + +**103. [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, 碰壁的时候就回头走。 + + + +--- + +**104. [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. + + + +--- + +**105. [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的用法: |, & + + + + +--- + +**106. [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 + + + +--- + +**107. [Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)** Level: Easy Tags: [Design] + +让一个class 是 singleton + + + +--- + +**108. [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. + + + + +--- + +**109. [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. + + + + +--- + +**110. [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 +- 都是基本操作, 概念实现 + + + +--- + +**111. [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就可以代为解决。 + + + +--- + +**112. [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 + + + +--- + +**113. [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. + + + +--- + +**114. [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) + + + +--- + +**115. [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 + + + +--- + +**116. [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] + + +给一串 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) + + + +--- + +**117. [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】上位,所踏过的所有小命啊! + +我这解释太生动了。因为耗费了好长时间思考... + + + +--- + +**118. [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) + + + +--- + +**119. [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 + + + +--- + +**120. [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. + + + +--- + +**121. [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加上去就好. + + + +--- + +**122. [Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)** Level: Medium Tags: [String] + + + +--- + +**123. [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? + + + +--- + +**124. [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]。 然后把三段链接在一起。 + + + + +--- + +**125. [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 +还要做一下那. + + + +--- + +**126. [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) + + + +--- + +**127. [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 + + + +--- + +**128. [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' + + + + +--- + +**129. [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的益处。 + + +--- + +**130. [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来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**131. [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一下最后一个词 + + + + +--- + +**132. [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。 + + + +--- + +**133. [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会出问题 + + + +--- + +**134. [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 + + + +--- + +**135. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**136. [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负数补位. + + + +--- + +**137. [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. + + + +--- + +**138. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**139. [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找合适的数字. + + + +--- + +**140. [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,也是类似做法 + + + +--- + +**141. [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里面的站位) + + + +--- + +**142. [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) + + + +--- + +**143. [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 + + + +--- + +**144. [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的基础上, 比对左左,左右,右左,右右 + + + +--- + +**145. [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的边框限制好,中间就全部遍历了。 + + + +--- + +**146. [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) + + + +--- + +**147. [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] + + +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 +- + + + +--- + +**148. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**149. [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) + + + +--- + +**150. [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了 + + + +--- + +**151. [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 + + + +--- + +**152. [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规律。 + + + +--- + +**153. [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的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**154. [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 + + + +--- + +**155. [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 + + + +--- + +**156. [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 + + + +--- + +**157. [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 + + + +--- + +**158. [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};` + + + +--- + +**159. [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[] + + + +--- + +**160. [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链接起来。 + + + +--- + +**161. [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. + + + + +--- + +**162. [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 + + + +--- + +**163. [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--. + + + +--- + +**164. [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完 + + + +--- + +**165. [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) + + +--- + +**166. [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] + + +#### 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. + + + +--- + +**167. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + +--- + +**168. [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? + + + +--- + +**169. [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. + + + + +--- + +**170. [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 + + + + +--- + +**171. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + +--- + +**172. [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.有点耐心 + + + +--- + +**173. [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, 节约时间复杂度. + + + +--- + +**174. [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 + + + +--- + +**175. [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` + + + +--- + +**176. [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维护清楚就行。 + + +--- + +**177. [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. + + + +--- + +**178. [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 + + + +--- + +**179. [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 + + + +--- + +**180. [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 + + + +--- + +**181. [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] + + + + +--- + +**182. [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 一样 + + + +--- + +**183. [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 + + + +--- + +**184. [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本身. + + + +--- + +**185. [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]。 + + + + + +--- + +**186. [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) + + + +--- + +**187. [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) + + + + +--- + +**188. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**189. [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; + + + +--- + +**190. [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. + + + +--- + +**191. [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 + + + + +--- + +**192. [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的判断 + + + +--- + +**193. [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. + + + + +--- + +**194. [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整除. 一步到位. + + + +--- + +**195. [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) + + + +--- + +**196. [Maximum Subarray III.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20III.java)** Level: Review Tags: [] + + + +--- + +**197. [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的情况。要把遍历的例子写写 + + + +--- + +**198. [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. + + + + +--- + +**199. [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. +- 分析过, 还没有写. + + + +--- + +**200. [Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)** Level: Medium Tags: [Hash Table] + + +给一面墙, 每一行是一行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 + + + +--- + +**201. [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 +还没有做 + + + +--- + +**202. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**203. [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 太慢, 不可行. + + + +--- + +**204. [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 + + + + +--- + +**205. [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. + + + + +--- + +**206. [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) + + + + +--- + +**207. [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看出有直接关系. + + + +--- + +**208. [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看起来更容易理解. + + + +--- + +**209. [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去做 + + + +--- + +**210. [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), 就不写了 + + + +--- + +**211. [[HackerRank]. Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/[HackerRank].%20Change%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. + + + +--- + +**212. [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了. + + + + +--- + +**213. [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` + + + +--- + +**214. [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里面, 这个想法非常值得思考. + + + +--- + +**215. [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在某个点小了,加进去当下这个空隙。 + + + +--- + +**216. [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. + + + +--- + +**217. [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) + + + +--- + +**218. [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 + + + +--- + +**219. [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) + + + +--- + +**220. [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; + + + + +--- + +**221. [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一下。 + + + +--- + +**222. [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 公式 + + + +--- + +**223. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**224. [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? + + + + +--- + +**225. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**226. [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 + + + + +--- + +**227. [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 + + + +--- + +**228. [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的状态, 然后看最后一步. + + + +--- + +**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. [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] + + +给一个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, 永远在左边上下上下。 + + + +--- + +**231. [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[] 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. + + + +--- + +**236. [Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)** Level: Hard Tags: [DP] + + + +--- + +**237. [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] + + +给一个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 + + + +--- + +**238. [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] + + +给一个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) + + + + +--- + +**239. [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)))` + + + +--- + +**240. [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 + + + +--- + +**241. [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` + + + +--- + +**242. [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就可以了. + + + +--- + +**243. [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; + + + + +--- + +**244. [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] + + +跟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] + + + +--- + +**245. [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 的情况. + + +--- + +**246. [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]) + + + +--- + +**247. [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 + + + +--- + +**248. [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 + + + +--- + +**249. [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) + + + +--- + +**250. [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` + + + +--- + +**251. [Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)** Level: Medium Tags: [Union Find] + +Lint还不能跑, 全部按照题意和答案document的. + + + +--- + +**252. [Search Insert Position.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Insert%20Position.java)** Level: Easy Tags: [] + +一般的binary search. +在结尾判断该return 哪个position。 + + +--- + +**253. [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. + + + +--- + +**254. [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. 这个用法要记住吧, 没别的捷径. + + + +--- + +**255. [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走过的地方 + + + +--- + +**256. [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 + + + +--- + +**257. [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一下。 + + + +--- + +**258. [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. + + + +--- + +**259. [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 + + + +--- + +**260. [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 + + + +--- + +**261. [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]` + + + +--- + +**262. [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 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; + + + +--- + +**266. [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了 + + + +--- + +**267. [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) + + + +--- + +**268. [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, 数位号) + + + + +--- + +**269. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + +// 如何想到从中间initialize + + + +--- + +**270. [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 原题有一点点不一样. + + + + +--- + +**271. [Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)** Level: Hard Tags: [BFS] + + + +--- + +**272. [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. + + + +--- + +**273. [String Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/String%20Permutation.java)** Level: Easy Tags: [] + +把#of occurrences 存进HashMap, 第一个string 做加法,第二个string做减法。最后看是否有不等于0的作判断。 + + + +--- + +**274. [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 + + + +--- + +**275. [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? + + + + +--- + +**276. [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。 + + + +--- + +**277. [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] + + +给一个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才可以施行. + + + +--- + +**278. [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的做法写出结果. + + + +--- + +**279. [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 + + + +--- + +**280. [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)的巧妙用法. + + + + +--- + +**281. [[lint]. Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Merge%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 + + + +--- + +**282. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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, 自底向上建立起的。 + + + +--- + +**283. [[lint]. Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Nth%20to%20Last%20Node%20in%20List.java)** Level: Easy Tags: [Linked List, Lint] + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth + + + +--- + +**284. [[lint]. Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array, Lint] + + + + +--- + +**285. [[lint]. Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Compare%20Strings.java)** Level: Easy Tags: [Lint, String] + +看StringA是不是包括所有 StringB的字符. Anagram + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + + + +--- + +**286. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**287. [[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)** Level: Hard Tags: [HashHeap, Heap, Lint] + +非题.是从九章找来的HashHeap implementation. + +#### HashHeap +- An efficient implementation of a priority queue. +- The linear hash function monotonically maps keys to buckets, and each bucket is a heap +- https://xlinux.nist.gov/dads/HTML/hashheap.html + + + +--- + +**288. [[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)** Level: Easy Tags: [Hash Table, Lint, String] + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**289. [[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)** Level: Medium Tags: [Array, Hash Table, Lint] + + +把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. + + + + +--- + +**290. [[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)** Level: Medium Tags: [Array, Lint, Two Pointers] + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**291. [[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)** Level: Easy Tags: [Array, Lint, 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) + + + + +--- + +**292. [[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Lint, 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! + + + + +--- + +**293. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**294. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**295. [[lint]. Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array, Lint] + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**296. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**297. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**298. [[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)** Level: Medium Tags: [Lint, 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) + + + +--- + +**299. [[tool]. Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Hash%20Function.java)** Level: Easy Tags: [Hash Table, Lint] + + +In general, there is no universal recipe to stick to when it comes to implementing hashCode(). +https://www.baeldung.com/java-hashcode + +#### 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会变得太大,所以不能算完和 再 %... + + + +--- + +**300. [[tool]. UnionFind.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20UnionFind.java)** Level: Medium Tags: [Lint, Union Find] + + +#### Method1: Union Find with Array +- union(), find() +- Path Compresion: store skip father after found, which makes find O(1) + +#### Method2: Union Find with HashMap + + + + + +--- + +**301. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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 + + + +--- + +**302. [36. Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/36.%20Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited row/col/block. + - 在nest for loop里面validate row,col,and block. + - Special: validate block要利用i 和 j 增长的规律 +- i, j are [0~n) can build block boundary in a 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 +- 可能代码稍微复杂一点 + + + +--- + +**303. [359. Logger Rate Limiter.java](https://github.com/awangdev/LintCode/blob/master/Java/359.%20Logger%20Rate%20Limiter.java)** Level: Easy Tags: [Design, Hash Table] + + +#### HashMap +- map: avoid duplicate message, records timestamp for validation +- time: O(1) +- space: O(n) + +#### Queue + Set +- 1) keep a trimmed queue and set (all tasks to be within 10 secs); +- 2) use set to O(1) check if incoming message exists. +- time: O(x), trimQueue() +- space: O(n) + + + +--- + +**304. [198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +搜刮房子, 相邻的不能碰. 每个房子里有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虽然抽象, 但是更加实用. + +#### Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- calculation + - dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]). The prior house can be either state. + - dp[i][1] = dp[i - 1][0] + nums[i]. The prior house must be `NOT ROBBED`. +- return `Math.max(dp[n - 1][0], dp[n - 1][1])` + + + +--- + +**305. [21. Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/21.%20Merge%20Two%20Sorted%20Lists.java)** Level: Easy Tags: [Linked List] + + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + + + +--- + +**306. [102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### Method1: BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### Method2: DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**307. [788. Rotated Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/788.%20Rotated%20Digits.java)** Level: Easy Tags: [Basic Implementation, String] + + +#### Basic Implementation of the rules +- [3,4,7] -> cannot rotate, failures. Must NOT have. set1 +- 2,5,6,9 -> good candidates. Must have 1. set2 +- [0,1,8] -> goes back to itself. can have +- loop over [1, N], count=int[10] appearance. + - set1 meet 0 + - set2 meet at least 1 + + + +--- + +**308. [42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. + +#### Method1: Max wall from both sides +- Array: Left Max Wall vs Right Max Wall. +- 对于每个index而言, vertically 能存放的最大水柱, 就是靠 左 右 最高墙决定的: + - min(leftHighestWall, rightHighestWall) - currHeight. +- time: O(n) +- space: O(n) + +#### Method2: Two Pointers +- Optimization from Method1: two pointer, 还是找左边最高和右边最高. O(1) space. +- 利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +- always limited by the shorter wall: 左边按照maxLeft来计算, 右边按照maxRight来计算. +- time: O(n) +- space: O(1) + +#### Method3: 2 Pointers, start from 2 sides +- 1. 找中间最高bar的index +- 2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条 +- 3. 每次还要减去block自身的height +- time: O(n) +- space: O(1) + +#### Method4: Stack +- 主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +- 最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +- 用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. +- time: O(n) +- space: O(n) + + + + +--- + +**309. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**310. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**311. [237. Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/237.%20Delete%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 + + + +--- + +**312. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**313. [448. Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/448.%20Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)** Level: Easy Tags: [Array, Bucket Sort] + + +#### Method1: Bucket Sort concept, set val to its correct position +- Given: values are [1,n], so val can represent index. Therefore, set val to its correct position +- 小心handle i: + - value是 1-based + - 每次换位, 需要`i--`, 重新省察`nums[i]` + +#### Method2: 做标记 (negative number, or super large number) +- Option1: use negative number to mark visited: + - 很巧妙地运用了标记的方法, 标记成负数,证明visit过。 + - Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! +- Option2: use large number (larger than n) + - 跟方法2类似,也是做标记,这一次是加上一个大于n的数(因为题目给了n的border),最后check一下就好。为不超Integer.MAX_VALUE, 每次加n前,取余数。 + - 做标记的方法固然快,但是相对来说比较hacky,在常规的代码中,估计不会用到. + + + + +--- + +**314. [360. Sort Transformed Array.java](https://github.com/awangdev/LintCode/blob/master/Java/360.%20Sort%20Transformed%20Array.java)** Level: Medium Tags: [Math, Two Pointers] + + +#### Two Pointers, Math +- Being able to analys the a*x^2 + b*x graph and find the `peak` or `valley` +- Math basics: x^2 dominates the overall curve so it is up to a to determine: + - `valley`: if a < 0, both sides will be small and center will be large. Prioritize larger value. + - `peak`: if a > 0, center will be small and both sides will be large. Prioritize smaller value. + - starting index being 0 or n-1, is driven by `a` + + + +--- + +**315. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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 + + + +--- + +**316. [849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array, Basic Implementation, Two Pointers] + + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, Two Pointers: 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` + + + + +--- + +**317. [408. Valid Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/408.%20Valid%20Word%20Abbreviation.java)** Level: Easy Tags: [Basic Implementation, String] + +tricky: find integer within a string +edge case: leading '0' should not be allow in such abbr. + + + +--- + +**318. [415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)** Level: Easy Tags: [Basic Implementation, Math, String] + + +#### Two Pointer +- Use i, j to process from end of 2 strings +- handle edge case for i, j + - if i < 0, its num = 0 (since we are doing sum, blindly setting 0 is okay) +- Note: `sb.insert(0, x)` is much slower than doing a final `sb.reverse()` + +#### If manually convertin to int[] +1. when converting to int[], remember to reverse string. +1. when converting to int[], remember to reserve extra space for carry + + + +--- + +**319. [83. Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/83.%20Remove%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会非常清晰 + + + +--- + +**320. [1108. Defanging an IP Address.java](https://github.com/awangdev/LintCode/blob/master/Java/1108.%20Defanging%20an%20IP%20Address.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**321. [1021. Remove Outermost Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1021.%20Remove%20Outermost%20Parentheses.java)** Level: Easy Tags: [Stack] + +#### Stack +- use stack to hold potential pair +- when stack is empty: detect outtermost element, dont add to final result +- time: O(n), space O(n) + +#### Count occurance +- solution from discussion, time O(n), space O(1) +- save space, but less scalable: think about if there are 100 different pairs, then the couting will be a bit complex to handle. + + + +--- + +**322. [236. Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/236.%20Lowest%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 + - Base Case: 当root == null, p or q is found (`root == p || root == q`),那么就return the root as LCA + - 三种情况: + - 1. leftLCA and rightLCA all found: `each path has found one of p and q node as LCA`. Therefore, curr root is the lowest ancestor + - 2. One of leftLCA and rightLCA is found: return whichever one found + - 3. both LCAs are null, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time O(n), stack space O(n) + + + +--- + +**323. [766. Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/766.%20Toeplitz%20Matrix.java)** Level: Easy Tags: [Array] + + +#### Check diagonal +- 似乎没什么算法特点, 就是array基本运算, 然后分割成一个helper function去做重复计算, 剪短代码. +- 注意check MxN 的分界线. + +#### Simpler Solution +- the goal is to check [i][j] == [i+1][j+1] for every i and j. + + + +--- + +**324. [953. Verifying an Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/953.%20Verifying%20an%20Alien%20Dictionary.java)** Level: Easy Tags: [Hash Table] + + +#### Hash Table +- mark the char position +- check adjacent words +- Optimization + - a) If s1 equals s2, just return true, no need to continue. + - b) if s2 (app) is a substring of s1(apple), just return false. + + + + +--- + +**325. [1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)** Level: Medium Tags: [Array, Greedy, Permutation] + + +#### Analyze Permutation behavior +- concept similar to `31. Next Permutation` +- 1) first pass: find the one that is in incorrect order +- 2) second pass: find the right spot to swap + + + +--- + +**326. [1213. Intersection of Three Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/1213.%20Intersection%20of%20Three%20Sorted%20Arrays.java)** Level: Easy Tags: [Hash Table, Two Pointers] + + +Very similar to 349.Intersection of Two Arrays. + +#### Hash Table +- Use set to check +- Verify duplicates at end rst + +#### Two Pointers +- similar to Intersection of Two Sorted Arrays +- Start from front/back, process 1 item at a time + - if match, move all pointers +- Optoin1: check from back +- Optoin2: check from frotn + + + +--- + +**327. [383. Ransom Note.java](https://github.com/awangdev/LintCode/blob/master/Java/383.%20Ransom%20Note.java)** Level: Easy Tags: [Basic Implementation, String] + +count chars in int[256] + + + +--- + +**328. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**329. [252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### Method1: sort input and compare if curr.end & next.start overlaps +- sort: `Arrays.sort(intervals, Comparator.comparing(i -> i[0]))` +- time: O(nlogn), space: O(1) + +#### Method2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 +- time: O(nlogn), space O(n) +- Not necessary for this problem, since it requires extra space with pq. + + + +--- + +**330. [665. Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/665.%20Non-decreasing%20Array.java)** Level: Easy Tags: [Array] + + +- 比较升序的时候, 必须要估计到 `i-1, i, i+1` 三个数位. +- 写出来`i-1, i, i+1`之间的关系, 然后做合理的fix. + 1. reduce nums[i+1] to fix + 1. raise nums[i+1] to fix +- 需要真的fix数组, 因为loop through做比较时会用到fix后的数字. + + + + +--- + +**331. [843. Guess the Word.java](https://github.com/awangdev/LintCode/blob/master/Java/843.%20Guess%20the%20Word.java)** Level: Hard Tags: [MiniMax] + + +TODO: revist time/space complexity + +#### Minimax, find target, and use it to eliminate +- `擒贼先擒王`: find the candidate that has largest set of correlations with the rest candidates, and eliminate based on this candidate. + - `approach A`: count the candidate that has 0 overlaps, find min of this poll + - `approach B`: count the candidate that has largest # of connections +- cross-compare, count `match==0` : find candidates that has 0 overlap with others + - pick `min-count candidate A`: it is a candidate that has overlaps with most strings (since 0-match-count is lowest) + - the above candidate will help to **eliminate** a largerset of overlapped candidates + - guess A, return matchCount. +- filter set with matchCount: eliminateCandidate + + + +--- + +**332. [986. Interval List Intersections.java](https://github.com/awangdev/LintCode/blob/master/Java/986.%20Interval%20List%20Intersections.java)** Level: Medium Tags: [Two Pointers] + + + + +#### Method1: Merge Interval +- There can be 1 overlapping on any interval, calculate the inner intersection: lo(A[i][0], B[j][0]), hi(A[i][1], B[j][1]) + - if low <= hi, a valid intersection exist; add + - also, if A[i][1] < B[j][1]; that is A[i].end < B[j].end, then i++; otherwise j++ + - because the further-away `end` has been used, so move on. +- O(n) + +#### Method2: Sweep line +- code is much more complex (pq, Point, process code... etc) than method1 +- we can use point to track open/close, also want to specify if point belongs to A/B +- mark 2 global parameters: aOpen, bOpen. + - process when A/B close, record if (aOpen, bOpen) has overlaop + - clear up corresponding global parameter after A/B closes +- sort all pointers in priority queue by index +- Point: {boolean isOpen; int index} +- process the queue and remember to clean up all items on same index +- time: O(nlogn) +- space: O(n) + + + + +--- + +**333. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**334. [293. Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/293.%20Flip%20Game.java)** Level: Easy Tags: [String] + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + + + +--- + +**335. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**336. [686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)** Level: Easy Tags: [Basic Implementation, Edge Case, String] + +Track: 纸上分析edge case. +Validation helps speed it up. + + + +--- + +**337. [80.Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/80.Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Basic +- 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其实就足够. + +#### Follow up: k duplicates, Two Pointers +- when index i and i-1 are diff, use count=1 to start +- in while loop, keep count++ until count==k +- reset when next diff comes in + + + +--- + +**338. [301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)** Level: Hard Tags: [BFS, DFS, DP] + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- Goal: identify invalid parentheses and remove (minimum removals) +- Step: + - Detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` + - When invalid occurs: + - chance for correction. Remove the incorrect parentheses, one at a time + - dfs on the rest of the s that has not been tested yet: start index from index i + - Core edge cases: + - Do not correct twice of the same parenthesis by checking [j-1] pos + - Make sure to attempt correction of all possible parenthesis within tested range: because it outputs all results at the same level + - return/finish once correction done +- Success case: + - a string s passed test: make sure it passes REVERSED string test! + - Core Concept: `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**. +- How does it guarantee minimum removals? + - When seeing a chance to correct, it jumps into a for loop of DFS. It `return` after the for loop. This stops additional testing + - When invalid occurs, correct it right away: minimum correction +- Complexity: + - O(nk), k being the # of recursive calls. It takes n calls to finish a full string case. + +#### BFS +- Similar to DFS, we wnat to test: 1) test input s valid, 2) remove 1 invalid parenthesis at a time, 3) process substring +- instead of testing all substrings (timeout), we want to establish rules to improve reprocess: + - Test1: skip regular char. No need to test it. + - Test2: if redundant paren, do 1 is enough. skip adjacent ones. + - Test3: if last removed extra paren is '(', the next ')' must be a valid pair. LastRemoved char: pecial handling by using a struct: `class Node {String s, int index, char lastRemoved}` +- How to end tests? When there is data in rst, stop adding to queue. + + + +--- + +**339. [111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%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 Conquer to find min depth. + - if one of child is null, return the other child depth + 1 + - Pick the min of the two child depth + 1 +- need to visit all nodes + + + + +--- + +**340. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**341. [7. Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/7.%20Reverse%20Integer.java)** Level: Easy Tags: [Math] + + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + + + +--- + +**342. [5. Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/5.%20Longest%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)? + +#### Method1: DP of interval +- Very similar to `216. Longest Palindromic Subsequence`, but this problem requires solid substring(i+1, j-1) to be palindromic +- Similarly: process i = n-1, from end so [i + 1, j] is always ready to consume +- boolean dp[i][j] to mark range (i, j) as palindrome or not. +- 在计算 dp[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- time: O(n^2) dp +- space: O(n^2) + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + + + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**343. [303. Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/303.%20Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**344. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**345. [1007. Minimum Domino Rotations For Equal Row.java](https://github.com/awangdev/LintCode/blob/master/Java/1007.%20Minimum%20Domino%20Rotations%20For%20Equal%20Row.java)** Level: Medium Tags: [Array, Greedy] + + + +#### Method1: Count all occurrance, and count on overlap indexes +- when there is a value that can cover entire row of size n + - it must be: `n = countA[i] + countB[i] - overlap[i]` +- Code easy to write and read +- time: O(n) +- space: O(1) + +#### Method2: Negative count +- Observation: if A[0] works, no need to check B[0]. +- Because if both A[0] and B[0] exist in all dominoes, + - when you swap A[0] in a whole row, + - you will swap B[0] in a whole at the same time. + - The result of trying A[0] and B[0] will be the same. +- time: O(n) +- space: O(1) + +#### Method3: positive count Match +- there should exist 1 numbers, that can appear in (A[i], B[i]). +- failure case: there exist at least 1 index, that does not have the common number +- maximum case: there can be 2 numbers, that both will make it work. +- findCommon2, and count them: + - set.add(A[0], A[B]), + - if any new one does not exist in set, remove it from set + - if set is empty() , return -1 +- use the 2 numbers from set to do a sweep and count in A, O(n), return the less appearance one. +- time: O(n) +- space: O(1) + + + +--- + +**346. [485. Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/485.%20Max%20Consecutive%20Ones.java)** Level: Easy Tags: [Array, Basic Implementation] + + +- preserve max +- 清零count + + + +--- + +**347. [896. Monotonic Array.java](https://github.com/awangdev/LintCode/blob/master/Java/896.%20Monotonic%20Array.java)** Level: Easy Tags: [Array] + +basic implementation + + + +--- + +**348. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**349. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**350. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**351. [26.Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/26.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 + + + +--- + +**352. [429. N-ary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/429.%20N-ary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Tree] + + +#### BFS +- use queue to hold each level. O(n) + + + +--- + +**353. [275. H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/275.%20H-Index%20II.java)** Level: Medium Tags: [Binary Search] + + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +Aim to find the lowest index mid, which maximize h = n - mid + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + + + +--- + +**354. [204. Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/204.%20Count%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个数就好了 + + + +--- + +**355. [58. Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/58.%20Length%20of%20Last%20Word.java)** Level: Easy Tags: [String] + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + + + +--- + +**356. [496. Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/496.%20Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack +- use stack to hold all elements + - keep poping if `stack.peek() < num` + - use map to record (top, num) +- time O(n), run through base once and sub-sequence once +- space O(n), stack, map + +#### HashMap +- O(n) space, O(n^2) time worst case + + + +--- + +**357. [41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)** Level: Hard Tags: [Analysis, Array, Edge Case] + + +给一串无序数字, 有负数: 找这个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 +1. 如果nums==null, 其实missing positive integer 自然而然是 1 +1. 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) + - 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +1. 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + +**358. [694. Number of Distinct Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/694.%20Number%20of%20Distinct%20Islands.java)** Level: Medium Tags: [DFS, Hash Table] + + +#### DFS + HashSet +- DFS can find # of island, just like `200.Number of Islands`, aim to count total +- We need to map same-shap land + - One approach: print the **footprint** starting from coordinate (0, 0) + - Another approach: print the actual island in its boundary, like a QR code. (too hard to code, skip) +- Footprint approach: + - 1. always assume a newly found islands starts from (0, 0) + - 2. take 4 direction from init pos and keep printing the footprint + - 3. Since we always visit nodes from top->right->nextrow, we always visit top-left cornor of a new island, and the footprint will be identical + - Otherwises, if needed, we can sort the footprint and output the hash +- time: O(n), visit all +- space: O(n), store footprint, and dfs stacks worst case visit all nodes + + + +--- + +**359. [717. 1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/717.%201-bit%20and%202-bit%20Characters.java)** Level: Easy Tags: [Array] + +理解题目: +1. single-bit always starts with '0', two-bits always start with '1'. +1. Therefore there is ONLY 1 way to reach end. + +#### 方法1 +Greedy. +从第一个bit开始: 如果 % 2 == 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. + + + +--- + +**360. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**361. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**362. [199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%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 +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n nodes in final results + + +#### DFS +- Use Map to override the result at each level +- dfs: + - dfs(node.left) and then dfs(node.right) because we want to log right side last +- record global max depth for iteration purpose +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n stacks (and n nodes in final results) + + + + +--- + +**363. [259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +1. Similar to 15. 3Sum, but simpler. +1. 只需要count triplet, 但是不需要save triplet, 而且还不需要handle duplicated triplets +1. 发现start, end满足条件时候,(end - start)就是所有 sum target, 那么就end-- +1. 两层循环, O(n2) + + + +--- + +**364. [977. Squares of a Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/977.%20Squares%20of%20a%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +#### Two Pointers +- negative index i, positive index j + + + +--- + +**365. [824. Goat Latin.java](https://github.com/awangdev/LintCode/blob/master/Java/824.%20Goat%20Latin.java)** Level: Easy Tags: [Basic Implementation, String] + + + + +--- + +**366. [308. Range Sum Query 2D - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/308.%20Range%20Sum%20Query%202D%20-%20Mutable.java)** Level: Hard Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree +- Same concept as turning an array into a binary segment tree, + - HOWEVER, this is a 4-nary segmenet tree +- Reference. 307 Range Sum Query +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. +- Handling end stage, there are two approaches: + - ApproachA: check at beginning of recursive call (i.e in `build()`, `updateNode()`, `rangeQuery()`). + - pro: calling recursive function blindly; code is easy. + - con: be really clear about termination state, and catch it. + - ApproachB: check & come up with correct query condition before recursive call + - pro: input to recursive function is assumed to be correct + - con: sometimes really hard to write the conditions before recursive call; code is hard. + + + +--- + +**367. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**368. [1153. String Transforms Into Another String.java](https://github.com/awangdev/LintCode/blob/master/Java/1153.%20String%20Transforms%20Into%20Another%20String.java)** Level: Hard Tags: [Graph] + + +#### Graph +- analysis: + - 1) should not have mult-origin cases: 1 char maps to 1 char at maximum + - 2) need a buffer char NOT exist in target to hold inter-media transformation + - check open char (out of 26 lower letter) that is NOT in target chars +- impl the validation rules +- more to read in https://leetcode.com/problems/string-transforms-into-another-string/discuss?currentPage=1&orderBy=most_votes&query= + + + +--- + +**369. [1008. Construct Binary Search Tree from Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/1008.%20Construct%20Binary%20Search%20Tree%20from%20Preorder%20Traversal.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top Down DFS +- This approach highly relies on the preorder rules + - we can use validation rules to navigate throug hteh preorder array + - use a global index +- time: O(n) + + + + +--- + +**370. [151. Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/151.%20Reverse%20Words%20in%20a%20String.java)** Level: Medium Tags: [String] + + +#### Method1: Split string by space, then flip +- Option1: With `s.split(" ")`: No brain, and super fast +- Option2: With `s.split("\\s+")`, it skips space, but slow. Use sb.insert(0, xxx) +- trim() output +- Time, Space: O(n) + +#### Method2: Flip entire, then individual, two pointer +- flip entire string, then flip each individual string +- Time, Space: O(n) + + + +--- + +**371. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**372. [31. Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/31.%20Next%20Permutation.java)** Level: Medium Tags: [Array, Permutation] + + +#### Permutation Behavior +- Great write up: https://leetcode.com/problems/next-permutation/solution/ +- next lexicographically permutation: `smallest` but `larger than curr` permutation: + - find first low point from right [low] + - find the slight larger [high] to swap with [low] + - make sure right side of low is eventually the smallest +- Analyze the use cases, to find next low permutation, 2 major steps: + - 1) Find `first low/drop candidate` from right + - 2) Find `first high where nums[high] > nums[low]` from right + - 3) swap(low, high). + - By now, [low, n-1] forms a greater permutation + - but it is not the smallest, because right side [low + 1, n - 1] is descending + - 4) reverse(low + 1, n-1) to create ascending slopt on right of low (smallest next lexicographically permutation) +- Corner case: if input array is decending (1st low not found), reverse it all together O(n) +- time: O(n) visit all indexes +- space: O(1) not using additional +- Similar question: `1053. Previous Permutation With One Swap` + + + + +--- + +**373. [518. Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/518.%20Coin%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 + + + +--- + +**374. [405. Convert a Number to Hexadecimal.java](https://github.com/awangdev/LintCode/blob/master/Java/405.%20Convert%20a%20Number%20to%20Hexadecimal.java)** Level: Easy Tags: [Bit Manipulation] + +#### Unsigned Shift, Mask +- Move pointer: move digit after process 4 bits. + - `>>>` Unsigned right shift + - always fills 0 irrespective of the sign of the number +- Mas: `num & 0xf` = `num & 15` + + + +--- + +**375. [850. Rectangle Area II.java](https://github.com/awangdev/LintCode/blob/master/Java/850.%20Rectangle%20Area%20II.java)** Level: Hard Tags: [Segment Tree, Sweep Line] + + +#### Sweep Line + Merge Interval concept +- Inspired by: https://leetcode.com/problems/rectangle-area-ii/discuss/137941/Java-TreeMap-solution-inspired-by-Skyline-and-Meeting-Room +- First consider regular sweep line and realize problem: each vertical line has multiple block segments + - Easy: take a list of vertical dots, and calculate the height diff + - We can use a TreeMap with y-coordinate as key, so to `natural sort by y-coordinate` +- Trick: can NOT remove used y coordinate from map, because the rectangle may continue to expand to right side. +- apply simple equation to calc area: `(long)preY * (p.x - preX)` +- time: + - sort initial queue: O(nlogn) + - process queue: O(n) + - TreeMap insertion: O(logn) + - TreeMap traversal: O(n) + - overall, process queue can be O(n^2) +- space: O(n) + +#### Sweep Line concept, bottom->top sweep +- https://leetcode.com/problems/rectangle-area-ii/discuss/137914/C%2B%2BPython-Discretization-and-O(NlogN) + +#### Segment Tree +- TODO lol + + + +--- + +**376. [515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS +- faster than BFS, using less space if not couting final rst: stack size, O(logn) +- time: O(n), visit all + +#### Method2: BFS with queue +- loop over queue level and record max + + + + +--- + +**377. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**378. [1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph] + + + +#### BFS +- simply calc each level sum with BFS +- top-level is processed first, since we go from top level -> deeper level + - only update result if sum is truly > global MAX. + + + +--- + +**379. [509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.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. + + + + +--- + +**380. [221. Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/221.%20Maximal%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; +- init: 每个点都可能是边长1, 如果 matrix[i][j] == '1' +- Space, time O(mn) +- Rolling array: [i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + + +--- + +**381. [131. Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/131.%20Palindrome%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) + + + +--- + +**382. [136. Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/136.%20Single%20Number.java)** Level: Easy Tags: [Bit Manipulation, Hash Table] + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + + + +--- + +**383. [222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, DFS, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### Method1: DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样 + - 如果一样, 直接 2 ^ h - 1就好 + - 不一样的话, 再DFS +- calculate `2^(h)`: 位运算, Math.pow(2, h) = 2 << (h - 1). 神奇! + - 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- time: O(n) visit all nodes on 1 side +- space: O(h) visit all nodes on 1 side + + +#### Method2: Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +#### Method3: Binary Search +- NOT DONE, TODO: https://leetcode.com/problems/count-complete-tree-nodes/solution/ + + + +--- + +**384. [257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%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 +- time: visit all n nodes +- space: to hold all paths, O(nlogn) + - O((n-1)/2) = O(n) nodes at leaf + - O(logn) depth + +#### 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 + + + + +--- + +**385. [543. Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/543.%20Diameter%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 and CombinedPath +- Option1: Use local single path max & global combined max + - Since the local combined diameter is used for comparision, but not used for specific calculation + - calculate path length (diameter), understand: + - for single path: child single path value + 1 (curr node) + - for combined path including curr node: left child single + right child path +- Option2: record local combined and single path for each iteration + - `int[]{combinedPath, singlePath}`; + - single path: pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; + - combined path `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`, find max from: + - 1) complete left child combined path + - 2) complete right child combined path + - 3) combined path with curr root + - Note: we treat a single node itself with diameter of 1, so we want to -1 in final result + - problem statement wants the path length (not # of nodes or depth) + + + +--- + +**386. [398. Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/398.%20Random%20Pick%20Index.java)** Level: Medium Tags: [Reservior Sampling] + + +#### 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. +- probability theory: + - 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 + + + + +--- + +**387. [238. Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/238.%20Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 第一轮:PreProduct (跟preSum的感觉有点像) + - PreProduct[i] stores product from num[0] -> num[i-1] (skipping current num[i]) + - init preProduct[i] = 1, as base for product + - 错过一位操作: always `preProduct[i] *= carry;` and `carry *= nums[i]` +- 第二轮: 从右边乘起, 每次在index i, 收到的carry都是 `nums[i+1] *....* nums[end]` + - 第一轮的结果 * 第二轮的结果, 刚好在index i 缺少掉 nums[i]. 如题所愿. +- Time: O(n) + + + +--- + +**388. [1060. Missing Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1060.%20Missing%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Binary Search] + + +#### Binary Search +- total missing nums = nums[curr] - nums[0] - curr +- edge case: if k > total missing nums, then just add the diff from nums[end] +- otherwise, find this `missing count == k` in the nums using binary search +- After binary search: `start + 1 == end`: + - re-calculate `count = nums[start] - nums[0] - start;` + - output final num: `nums[start] + k - count;` +- Option1: always compare total missing nums count +- Option2: compare partial missing nums count (inspired by: https://leetcode.com/problems/missing-element-in-sorted-array/discuss/303444/Java-O(logN)-solution-Binary-Search) + + + + +--- + +**389. [1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)** Level: Medium Tags: [Bucket Sort, DP, Hash Table, Sort] + + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**390. [67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%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 + +#### reverse +- Reverse string -> Convert to Integer List, add up -> Convert back to string +- pointer 从前向后, 所以只需要 1个pointer. +- 操作复杂, 如上, 证明可以解决. 没必要reverse. + +#### Incorrect: convert to Integer +把binary换成数字作加法. 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**391. [299. Bulls and Cows.java](https://github.com/awangdev/LintCode/blob/master/Java/299.%20Bulls%20and%20Cows.java)** Level: Medium Tags: [Hash Table] + + +#### Solution1: use int[10] to count frequency +1. check match chars +1. check unmatched chars by counting and offset their frequency + - count++ on secret chars: if secretCount is ever < 0 => `char g` has match, then cows++ + - count-- on guess chars: if guessCount is ever >0 => `char s` has match, then cows++ + +#### Solution2: Use hashmap to count +- Improvement: since all digit, use int[10] to count + + + +--- + +**392. [557. Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/557.%20Reverse%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就好了 + + + +--- + +**393. [203. Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/203.%20Remove%20Linked%20List%20Elements.java)** Level: Easy Tags: [Linked List] + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + + + +--- + +**394. [1219. Path with Maximum Gold.java](https://github.com/awangdev/LintCode/blob/master/Java/1219.%20Path%20with%20Maximum%20Gold.java)** Level: Medium Tags: [Backtracking, DFS] + + + +### DFS, Backtracking +- typical recursive visit all situation + + + + +--- + +**395. [266. Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/266.%20Palindrome%20Permutation.java)** Level: Easy Tags: [Hash Table] + + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count char occurrance + - 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assume lower case letter. 应该至少是所有ASCII code + + + +--- + +**396. [62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +#### DP, 加法原理 +- 计数DP: 2 ways to reach (i,j): dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + - non-overlapping: `dp[i - 1][j]`, `dp[i][j - 1]` + - covers the only 2 possible way to reach (i,j) +- initialization: dp[i][0] = 1, dp[0][i] = 1 + - Of course, row i = 0, or col j = 0, there is only 1 way to reach +- time O(mn), space O(mn) + +##### 滚动数组 Rolling Array +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + +#### DFS + Memoization +- move from (0,0) towards (m, n) +- use Map as memoization technique + + + +--- + +**397. [1091. Shortest Path in Binary Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/1091.%20Shortest%20Path%20in%20Binary%20Matrix.java)** Level: Medium Tags: [BFS] + + + +#### BFS +- find shortest path using queue +- time/space: O(n^2), n = grid length +- why SKIP `boolean visited[i][j]`? after a position grid[i][j] is used: + - 1) the curr path will not return to (i, j) + - 2) other route that may eventually reach (i, j) need not to be recorded, + - because the other route is already longer than the curr path + - therefore, we just simply block the visited node by `grid[x][y] = 1` + - note: block it right after it is added to the queue, so other nodes at same level will not attempt this visited node. + + + +--- + +**398. [1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +- time: O(n), visit all nodes +- space: O(logn), height of the tree + +#### Method2: HashMap, DFS. +- traverse tree and create `map ` to fast O(1) removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- This approach requires a dfs build of parentMap + - it is same amount of efforts to do the regular dfs removal. + - not a good solution +- time: O(n) +- space: O(n) + + + +--- + +**399. [1249. Minimum Remove to Make Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1249.%20Minimum%20Remove%20to%20Make%20Valid%20Parentheses.java)** Level: Medium Tags: [Stack, String] + + +#### Stack +- Goal: remove extra '(' or ')' so it is valid. +- Forward thinking: use stack to track '(' and ')', then keep appending partial string to output +- Backward thinking: use stack to filter out false indexes, and remove them in the end + + + + +--- + +**400. [15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +#### sort array, for loop + two pointer +- 处理duplicate wthin triplets: + - 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. + - 如果是triplet内有重复, 用完start point, 移动到结尾. +- Note: + - 1. 找 value triplets, 多个结果。注意,并非找index。 + - 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + - 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 +- 时间 O(n^2), 两个nested loop + +#### For loop + 2Sum +- HashMap 2Sum. Remember to handle duplicates + - 1. For loop 挑个数字A + - 2. 2Sum 出一堆2个数字的结果 + - 3. Cross match 步骤1里面的A + + + +--- + +**401. [311. Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/311.%20Sparse%20Matrix%20Multiplication.java)** Level: Medium Tags: [Hash Table] + + +给两个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 + + + +--- + +**402. [339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS, NestedInteger] + + +给一串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. + - 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) + +#### BFS +- bfs, queue, 处理queue.size() for a level +- use a level variable to track levels +- slower since it uses extra space, worst case O(n) of all items + + + +--- + +**403. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + +**404. [55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%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 +- 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) +- space: O(1) + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (j + A[j] >= i), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- time: O(n^2) +- space: O(n) + + + + +--- + +**405. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**406. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**407. [51. N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/51.%20N-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 +- Backtracking by replacement: each row has 1 queen, so just store it in int[] columns (CC book solution) + +#### 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 + + + + +--- + +**408. [875. Koko Eating Bananas.java](https://github.com/awangdev/LintCode/blob/master/Java/875.%20Koko%20Eating%20Bananas.java)** Level: Medium Tags: [Binary Search] + + + +#### Binary Search +- Bianry serach on the min/max value range +- The mid value is calcualted with helper function `calc(piples, k)` +- find celing: `count += (i - 1) / k + 1`, faster than `Math.ceil(i / k)` +- time: O(logm) to find the best velocity, assume total range is m; O(n) for each `calc` call + + + +--- + +**409. [189. Rotate Array.java](https://github.com/awangdev/LintCode/blob/master/Java/189.%20Rotate%20Array.java)** Level: Easy Tags: [Array, Rotation] + +#### Rotate array in place +- rotate all +- rotate 2 sides: < k or >= + + +#### Rotate by buffer the k array + + + +--- + +**410. [19. Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/19.%20Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Two Pointer +- 1 end pointer to define the window based n steps +- 1 pre pointer to track the node before the targeting node +- when end reaches null, remove nth node: link pre and head.next + + + +--- + +**411. [134. Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/134.%20Gas%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 +- 而不是求: 最后点可否走完/最值/计数 + + + +--- + +**412. [119. Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/119.%20Pascal's%20Triangle%20II.java)** Level: Easy Tags: [Array, Basic Implementation] + + +简单处理 list. code is very similar to Pascal triangle I. + +- 注意 `list = Arrays.asList(x, y, z ...)` 给fixed-size list, 不能直接 list.add(). +- Use `new ArrayList<>(Arrays.asList(...))` to wrap it up. + + + + +--- + +**413. [1197. Minimum Knight Moves.java](https://github.com/awangdev/LintCode/blob/master/Java/1197.%20Minimum%20Knight%20Moves.java)** Level: Medium Tags: [BFS] + + +#### BFS +- `from starting point, find min steps to reach certain point`: think of BFS + - similar: shortest path, shortest distance +- bfs: minimum steps, enumerate the possible moves + - move closer to x or y (test 8 possible directions) + - add possible moves in queue +- use visited to cache visited coordinates +- time: O(8^n), # of BFS branches +- space: O(8^n), # of BFS branche nodes + + + +--- + +**414. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**415. [1306. Jump Game III.java](https://github.com/awangdev/LintCode/blob/master/Java/1306.%20Jump%20Game%20III.java)** Level: Medium Tags: [BFS, Graph] + + +### Method1: BFS +- Find possibility to reach certain point, we can BFS: faster to find shortest candidate +- use queue to hold left, right candidates +- use set to record visited + +### Method2: DFS +- attemp all nodes, use set to record visited. +- time: O(n) +- space: O(n) + + + +--- + +**416. [305. Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/305.%20Number%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 + + + +--- + +**417. [206. Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/206.%20Reverse%20Linked%20List.java)** Level: Easy Tags: [Linked List] + +#### Iterative +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + +#### Recursive with a helper function +- source node: head +- target node: new head + + + +--- + +**418. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + +**419. [741. Cherry Pickup.java](https://github.com/awangdev/LintCode/blob/master/Java/741.%20Cherry%20Pickup.java)** Level: Hard Tags: [DFS, DP] + + +special hint: `r1 + c1 = constant t = r2 + c2`, if the two points are moving at same time. + +#### DFS + Memo: TOP-DOWN +- Similar concept to Minimum Path Sum +- https://leetcode.com/problems/cherry-pickup/solution/ +- realize r1 + c1 = r2 + c2. Knowing 3 parameters can uniquely identify the 4th. +- assume there are 2 people starting from origin, and the 2 people can go total 4 directions + - perform DFS based on the 4 directions + - concern: do they visit the same spot? possible. when that happens, make sure we do not double count the grid[i][j] +- when is the end state? + - then anyone, for example, (r1,c1) reaches end (n-1, n-1). + - it means the other person also reaches end +- use memo: memo[r1][c1][r2], it records any given (r1, c1, r2, c2) state + + + + +--- + +**420. [168. Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/168.%20Excel%20Sheet%20Column%20Title.java)** Level: Easy Tags: [Math] + + +#### 基本换算 +- 26位, 像10位一样去思考 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + + + +--- + +**421. [104. Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/104.%20Maximum%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 + + + +--- + +**422. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**423. [443. String Compression.java](https://github.com/awangdev/LintCode/blob/master/Java/443.%20String%20Compression.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**424. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**425. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + +**426. [844. Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/844.%20Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + +#### Method1: Two pointers to backtack from end of string +- time: O(n) +- space: O(1) + +#### Method2: Stack +- need to remove entity just added +- use stack to hold array content; pop if # is found + + + +--- + +**427. [9. Palindrome Number.java](https://github.com/awangdev/LintCode/blob/master/Java/9.%20Palindrome%20Number.java)** Level: Easy Tags: [Math] + +#### Reverse half of the number +- build reversed integer 直到midpoint, where x <= reverse +- 如果input双数: x == reverse +- 如果input单数 (而且x>reverse): x == reverse/10 + +#### Consider palindrome +- optionA: compare digit by digit +- optionB: reverse half of the string/int, and compare with other half. + + + + + + +--- + +**428. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**429. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**430. [1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)** Level: Medium Tags: [Lock, Semaphore, Thread] + + +#### Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 + +#### Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +- semaphore: https://www.geeksforgeeks.org/semaphore-in-java/ + + + +--- + +**431. [973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)** Level: Medium Tags: [Divide and Conquer, Heap, Sort] + + +#### PriorityQueue +- Create customized Point{} class +- Sort by distance +- Maintain queue size <= K + +#### Divide and Conquer +- ?, select sort? + + + +--- + +**432. [771. Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/771.%20Jewels%20and%20Stones.java)** Level: Easy Tags: [Hash Table] + + +- 给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 +- Basic HashSet + + + +--- + +**433. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + +**434. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**435. [567. Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/567.%20Permutation%20in%20String.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + +#### Method1: Sliding window with left/right Pointers +- Sliding window template: + - 1) Check right pointer and move right + - 2) Move left when necessary + - 3) Verify count == 0 & end state + - Note: normally 2) and 3) are in reversed order; this problem is a bit different +- This is efficient when the number of characters is not limited to 26, the runtime is still O(m + n) +- time: O(m + n), m = s1 length, n = s2 length +- space: O(k), k = # of possible chars, 26 in this case + +#### Method2: Two Pointer, but brutle verify freq count +- 如果做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) 这一步 +- time: (m + n) + - However, if # of possible chars is more than 26 + - For example, `k unique characters`, then the runtime will become: O(m + nk) + +- space: O(k), k = # of possible chars, 26 in this case + + + +--- + +**436. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**437. [158. Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/158.%20Read%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就好了. +- alaternatively: use queue to hold so we do not need to worry about size + + + +--- + +**438. [369. Plus One Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/369.%20Plus%20One%20Linked%20List.java)** Level: Medium Tags: [Linked List] + + +#### Reverse to make significant digit at tail +- Need add from the back and calculate carry +- Reverse list, so insignificant digit at head; calculate carry +- Reverse back when output + + + +--- + +**439. [211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +#### Trie, prefix tree. +- Trie Structure: `boolean isEnd`, `HashMap children` + - trie.addWord: 没node就加,有node就移动 + - trie.search: 没node就return false,有node就移动 +- Alternatively, the hash can be `TrieNode[26]` a fixed size array when applicable + - I like map better for the simplicity to write (w/o converting char -> index) + + + + +--- + +**440. [43. Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/43.%20Multiply%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]. + - Loop over num1, each row num1[x] * num2, save to correct index (i + j + 1) + - Note: skip leading '0' during output, but do not delete string "0" + - time,space O(mn) +- Option1: Calculate carry on the fly + - index `curr = i + j + 1`, left index `left = curr - 1`, since we start calculation from end of the array. + - **we only touch right side of the array once**, so we can move the carry off from it, and carry to left index + - code is concise +- Option2: save product first without calculating carry + - save product in each int index + - calculate carry on rst[] and `sb.insert(0, c)` (since we start from end of rst) + - this is actaully faster than Option1, somehow. + +#### Reverse, calculate from index 0, and reverse back +- 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'; + + + +--- + +**441. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**442. [680. Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/680.%20Valid%20Palindrome%20II.java)** Level: Easy Tags: [String] + +#### Palindrome String +- delete an index: 有两种情况 +- 用一个boolean parameter来表现state. 如果有其他status, state可以变成 String/enum + + + +--- + +**443. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**444. [70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%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]往往是有特殊状态的. 这里, dp[0]: 1种方式可以原地不动 + - dp[1]=1, 1种方式到达index=1, + - 之后的`dp[2] = dp[0]+dp[1]`: 就是dp[0]的走法 or dp[1]的走法 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**445. [747. Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/747.%20Largest%20Number%20At%20Least%20Twice%20of%20Others.java)** Level: Easy Tags: [Array] + + +多种简单操作: +- O(n) solution: 找最大值, 和第二大的值, 看是否符合题意, 就行了. +- O(2n) 最简单方法: 可以loop 两遍: 找最值; 作比较. +- O(2n) 举反例: 有一个不满足, 就够反对这个'at least twice of alllll others'. + + + +--- + +**446. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**447. [239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Method1: Deque, Monotonous queue +- 维持monotonuous queue: `front is always at max` and the `tail end is min`. Always need to return the max end of queue. +- when adding new elements x: + - 1) start from small-end of the queue + - 2) drop all smaller elements + - 3) append to the ending element that is larger than x. + - This is to maintain a front->tail decreasing queue +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`: better than using arraylist, since DeQueue(linked list) removes at O(1) cost +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +- Option1: sliding window template using right/left + while loop + - 1) tailing the new number to max queue, if applicable + - 2) process: record max + - 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] +- Option2: same concept, but use index `i` to mark right, and `i - k + 1` to mark left. +- time: O(n), one pass +- space: O(k), store the deque + + +#### Method2: Heap +- can always build a `class Node{index, val}`; and sort them with PQ of size k +- time: O(nlogK) +- space: O(k) +- this is not linear time, not as good as method1 + + + +--- + +**448. [47. Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/47.%20Permutations%20II.java)** Level: Medium Tags: [Backtracking, DFS] + +space: O(n!) + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +Similar to 46. Permutations, but with dedup + +#### Backtracking +- Sort the list, and on same level, if last element is the same as curr, skip this recursive call +- time O(n!) + +#### 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]在重复时候, 不用重新记录. + + + +--- + +**449. [332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)** Level: Medium Tags: [Backtracking, DFS, Graph] + + +#### DFS with backtcking +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +- time: O(n^n). n = # of cities. worst case, each city has (n-1) edges and need to try all combinations +- space: O(n^2), there can at most be n * (n - 1) edges + + + +--- + +**450. [88. Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- Also most identical to `33. Search in Rotated Sorted Array`: + - find where nums[mid] lands by comparing to nums[start]. i.e., if nums[mid] < nums[start], on right half of the array + - when `nums[mid] == nums[start]`: duplicate. Shift by start++ +- the worst case of `nums[mid] == nums[start]` willl cause O(n), +- but if duplicate is not entire array, should be O(logn) + + + +--- + +**451. [561. Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/561.%20Array%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发现规律: 升序排列会让 `高位的min(pair)` 最大化, 于是`一言不合先排列` +- 找到排列取单数位的规律,再考虑负数和正数的相同规律,即可找到排列求解的方法。 +- sort, O(nlogn) + + + + +--- + +**452. [387. First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/387.%20First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +#### Count appearance with int[256] +- 按照题意, 找到第一个 first index == last index的字母. + +#### Count appearance with hashmap (more scalable) +- 用hashmap存字母的index, 有些重复字母的index就会是个list. +- 找到单一index, 结合成list, sort, return list.get(0) +- slow due + + + +--- + +**453. [345. Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/345.%20Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i && 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 cannot be any match. + + + + +--- + +**456. [367. Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/367.%20Valid%20Perfect%20Square.java)** Level: Easy Tags: [Binary Search, Math] + + +#### Binary找sqrt +- binary search template: mid+1, mid-1, `start <= end` +- define index as long. + + + +--- + +**457. [270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +Concept: Iterate over all logN nodes in the BST and record the closest. Rather than finding the value at +/- 0.5 precision + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, until null leaf +- time: O(logn), space O(1) since no extra space used + +#### DFS, Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return +- time: O(logn), space: O(logn) + + + + +--- + +**458. [28. Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/28.%20Implement%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 + + + +--- + +**459. [1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)** Level: Hard Tags: [DFS, Stack, String] + +#### Parse exp as sub problem +- Analyze the pattern: 1) single char, 2) with !, 3) with &, | +- Identify sub problem + - Use stack to parse the data in "()", which is a sub problem to solve with recursive call + - Handle &, | case: need to parse multiple +- Be comfortable with string parsing +- Slight improve: + - If see obvious result, directly return evaluation w/o further parsing + - use memo to store evaluated exp + +#### Evaluate inner exp and save back to Stack +- Use '(' and ')' to mark inner exp +- Evaluate the inner exp and save result back to Stack: the result will be 'f' or 't' +- This is slightly slow because: + - It requires all stack items on top to be processed before reaching the operator + - There is no room to optimize even there is simplification for specific operator + + + +--- + +**460. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**461. [852. Peak Index in a Mountain Array.java](https://github.com/awangdev/LintCode/blob/master/Java/852.%20Peak%20Index%20in%20a%20Mountain%20Array.java)** Level: Easy Tags: [Binary Search] + + +#### Binary Search +- binary search to find A[i-1] < A[i] < A[i+1] + - if [mid-1] < [mid+1], on left slope, start = mid + - if [mid-1] > [mid+1], on right slope, end = mid +- init: start == 1, end = n - 2; + + + +--- + +**462. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**463. [110. Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/110.%20Balanced%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + +给一个binary tree, 看是否是height-balanced + +#### DFS with end state +- 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 above solution, but cost more traversal efforts +- 试图计算所有情况 + + + +--- + +**464. [1040. Moving Stones Until Consecutive II.java](https://github.com/awangdev/LintCode/blob/master/Java/1040.%20Moving%20Stones%20Until%20Consecutive%20II.java)** Level: Medium Tags: [Array, Sliding Window] + + + +#### Analyze to understand +- Make sure to sort array: we need to use the actual number range `A[j] - A[i]`, which requires the array to be sorted +- we want to form a new array where A[n-1] - A[0] + 1 == n; order does not matter but all slots need to be filled consecutivly +- max moves: https://leetcode.com/problems/moving-stones-until-consecutive-ii/discuss/289357/c%2B%2B-with-picture + - A interval will be automatically dropped between A[0] and A[1], if moving A[0] first + - Same, a interval between A[n-2] and A[n-1] will be dropped when moving A[n-1] first + - so largest possible move = firstItem + remaining range size - n items = 1 + (A[n-1] - A[1] + 1) - n = A[n-1] - A[1] -n + 2 + - or A[n-2] - A[0] - n + 2 +- min moves: `Sliding Window` + - use slinding window to assume a right pointer, to make sure A[right] - A[left] + 1 < n; otherwise, move left++ + - check # of included stones + - calculate remaining, which is remaining moves +- Handle min move edge case: + - Consecutive Array up to right = n - 1; need 2 moves to finish + + + +--- + +**465. [246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math, Two Pointers] + + +根据题意枚举, 再根据规则basic implementation + +#### HashTable + Two Pointer +- compare left/right + +#### Alter input +- flip number (6 and 9), and then reverse the string, see if the string is the same. +- takes more + + + + +--- + +**466. [100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### Method1: DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### Method2: BFS with 2 queues +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**467. [307. Range Sum Query - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/307.%20Range%20Sum%20Query%20-%20Mutable.java)** Level: Medium Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree, devide and conquer +- sample problem for segment tree +- build(), update(), rangeQuery() + - build and update are standard + - rangeQuery: handle the range split check +- Null leaf node handling: NO, ideally it will not encounter null leaf. + - in update/rangeQuery: when final state (`start==end`) is reached, the recursive call ends + - there is no way for any node to dive futher into null child. +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. + + + +--- + +**468. [88. Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素: 从尾部,是大数字优先排末尾的. +- Deal with remaining: + - When A values are used up, put remian of B into it + - When B values are finished, there is nothing todo. The remain of A is already in place. + + + +--- + +**469. [319. Bulb Switcher.java](https://github.com/awangdev/LintCode/blob/master/Java/319.%20Bulb%20Switcher.java)** Level: Medium Tags: [Brainteaser, Math] + + +#### Brainteaser +- https://leetcode.com/problems/bulb-switcher/discuss/77104/Math-solution.. + +#### Brutle: +- if just impl, it take O(n^2): +- repating: some pos are toggled mutiple times: if we know total times, easy to determin each pos. +- loop over [2, n], count times on each index + + + +--- + +**470. [112. Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/112.%20Path%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. + + + + +--- + +**471. [463. Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/463.%20Island%20Perimeter.java)** Level: Easy Tags: [Hash Table] + +#### Brutle, Count Blocks and Walls +- 每个格子 +4 个墙; +- 每个shared的墙要减去: 从每个island走去另外一个, 都-1 (最终没面墙, -2) + +#### Hash Table +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么就可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不大,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + + + +--- + +**472. [170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)** Level: Easy Tags: [Design, Hash Table, Memoization] + + +#### Hash Table, Memo +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +- time: O(n), loop over all elements in map +- space: O(n), store all elements in map & memoization set + + + +--- + +**473. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**474. [715. Range Module.java](https://github.com/awangdev/LintCode/blob/master/Java/715.%20Range%20Module.java)** Level: Hard Tags: [Segment Tree, TreeSet] + + +#### TreeSet +- start with considering array structure but operation are all O(n) + - what if we can easily find range, and update +- TreeSet: + - build a class `Interval {int start, end;}` + - build a customized `compareTo` that sorts the interval by start at default, but sort by end if a.start==b.start + - Query: TreeSet allow us to find element in O(logn) + - Add Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + - Remove Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + + + +--- + +**475. [12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)** Level: Medium Tags: [Basic Implementation, Math, String] + + +#### String, Basic implementation +- Parse each digit based on rules +- 1) parse: analyze the situations + + +--- + +**476. [14. Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/14.%20Longest%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) + + + +--- + +**477. [243. Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/243.%20Shortest%20Word%20Distance.java)** Level: Easy Tags: [Array, Two Pointers] + + + +#### Two Pointers +- Use 2 pointers to record **most recent** pos of word1 and word2 + - move pointer i [0 ~ n) and keep refreshing pos1 and pos2 + - both pos1 and pos2 will be as adjacent as possible since they both moving towards same direction +- keep recalculating best distance when either word is matched +- 而同一时间,只需要计算一个最近的curr distance: greedy不断变更A/B index, 做比较 + + + + +--- + +**478. [414. Third Maximum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/414.%20Third%20Maximum%20Number.java)** Level: Easy Tags: [Array, PriorityQueue] + +#### pq +- 注意special case: `when less than 3 items, return maximum` +- PQ是natural order: remain peek() will be the 3rd maximum + + + + +--- + +**479. [1267. Count Servers that Communicate.java](https://github.com/awangdev/LintCode/blob/master/Java/1267.%20Count%20Servers%20that%20Communicate.java)** Level: Medium Tags: [Array, Graph] + + +#### two axis array, cross-reference +- analyze problem, and realize we want to eliminate `isolated servers` +- count row[], count col[] +- cross-reference row[] and col[]: `row[i]==1 & col[j]==1` indicates a isolated server + +### DFS brutle +- Unfortunately, this problems checks unconnected items, so dfs needs to brutlely check entire row or column +- Only add if `vertical + horizontal count` > 1 +- time: O(mn) * O(m + n) + + + +--- + +**480. [20. Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/20.%20Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +#### Stack +- 剥皮过程。解铃还须系铃人 +- 左边的外皮'{['在stack底部 +- 右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**481. [893. Groups of Special-Equivalent Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/893.%20Groups%20of%20Special-Equivalent%20Strings.java)** Level: Easy Tags: [Basic Implementation, String] + +Mark # of characters can be useful to print string signature + + + +--- + +**482. [427. Construct Quad Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/427.%20Construct%20Quad%20Tree.java)** Level: Medium Tags: [Tree] + + +#### Basic Impl +- build tree recursively by definition +- O(n^2) time and space due to single visit to all nodes + + +--- + +**483. [981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)** Level: Medium Tags: [Binary Search, Hash Table, TreeMap] + + +#### Method1: Binary Search +- use hash to store +- binary serach on list of values + +#### Method2: TreeMap +- use hash to store > +- treemap.floorKey(timestamp) finds the top item below certain timestamp + + + +--- + +**484. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**485. [234. Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/234.%20Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单: 两边回溯相等. However: + - 1) cannot random access index on linkded list + - 2) cannot reverse iterating linked list +- solution: reverse linked list: 遍历接开头 + - 1) 用快慢指正找到mid point + - 2) reverse 2nd half + - 3) compare leftList and rightList +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + + + +--- + +**486. [202. Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/202.%20Happy%20Number.java)** Level: Easy Tags: [Hash Table, Math] + + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + + + +--- + +**487. [69. Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/69.%20Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + +#### sqrt(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 + + + +--- + +**488. [876. Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/876.%20Middle%20of%20Linked%20List.java)** Level: Easy Tags: [Linked List] + +找Linked List的中间node + +#### 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + + + +--- + +**489. [1026. Maximum Difference Between Node and Ancestor.java](https://github.com/awangdev/LintCode/blob/master/Java/1026.%20Maximum%20Difference%20Between%20Node%20and%20Ancestor.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top-Down DFS +- cache parent max and min => produce current max and min +- pass the max and min to dfs +- compare and return the max of dfs(left), dfs(right) +- time: O(n) +- space: O(logn) +- easy to write, a bit hard to think of + +#### Method2: Bottom-up DFS +- pass up the local (min, max) as object `Val{max, min}` +- easy to think of, but more code to write + + + +--- + +**490. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**491. [432. All One Data Structure.java](https://github.com/awangdev/LintCode/blob/master/Java/432.%20All%20One%20Data%20Structure.java)** Level: Hard Tags: [Design, Doubly Linked List] + + +#### Doubly Linked List +- IMPORTANT: the problem aims to put keys of same frequency in same node! This affects the design of node +- Main a class `Node {keySet, count, last/next pointers}` +- Each operation: + - 1) finds target node and extract the key + - 2) calculate: count +/- 1 + - 3) find new spot to store the key (prior positions or later positions) +- Be careful when handling the cases in inc() and dec() + + + +--- + +**492. [380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + + +#### Hash Table, Swap when removing +- Map: `map, Lis: tracks `index->value` +- list maintain 用来 insert/remove/random operations. +- Remove: swap input valueIndex & tialIndex = list.size() -1. + - list.remove(object) 应该是要O(logn) 做一个search的. + - list.remove(list.size() - 1) is cheapter + + + +--- + +**493. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**494. [219. Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/219.%20Contains%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 +- Time O(nm), m = # of duplicates. 太慢 +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k + + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**495. [91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Method1: DP, Bottom-Up by calculating base case first +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- there can be 2 final states at dp[i]: single digit or double digit. + - validate if `single digit`, dp[i] += dp[i - 1]. Last 1 digit can be decoded. + - validate if `double digit`, dp[i] += dp[i - 2]. Last 2 digits can be decoded. +- note: + - get number from char: `c - '0'` + - validatae single digit != '0', 因为'0' 不在条件之中(A-Z) +- Option1: dp[i] as # ways to decode at index i, including letter i + - The validation is done on: 1) single digit i, or 2) double digit [i-1, i] +- Option2: Partition DP, dp[i] as # ways to decode for first i letters (excluding letter i) + - 定义`dp[i] = 前i个digits最多有多少种decode的方法`: new dp[n + 1]. + - dp[i] += dp[i - x], where x = 1, 2 + - The validation is done on: 1) single digit [i-1], or 2) double digit [i-2, i-1] + - Option2 is better in terms of clean coding. We assume `dp[0]=1` as 1 way to decode 0 digits. + - No need to specially handle length == 1, because it is covered later + - No need to manualy init the first 2-digit case as in Option1 + - Return of `dp[n]` is clean +- 引申: 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +#### Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 + - especially when i = n - 2, handle 2-digt edge case carefully: + - 1) check if two-digit range [i, i+1] is valid + - 2) check if single-digit [i] is valid; if so, += dfs(s, i + 1) +- memo[i]: # ways to decode from [i, n). init with `memo[i]=-1` + + + +--- + +**496. [205. Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/205.%20Isomorphic%20Strings.java)** Level: Easy Tags: [Hash Table] + + +#### HashMap +- check 2 failture cases: + - same key, value not matching + - two key maps to same value + + + +--- + +**497. [639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%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]) +- Enumeration: + - 具体分析 '*' 出现的位置, 枚举出数字, 基本功. + - 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) + - 枚举好以后, 其实这个题目的写法和思考过程都不难 +- Mode: + 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. + + +#### DFS + memoization +- DFS top-down approach is used to analyze the problem. The logic flow: +- 1) consider the case of 1 letter or 2 letters. +- 2) one letter: + - [*]: + 9 * dfs(s, i + 1) + - [0~9]: + dfs(s, i + 1) +- 3) two letters: + - [_, *]: depends + - [*, _]: depends + - [*, *]: + 15 * dfs(s, i + 2) +- memo[i] records # of ways to decode from [i ~ n] +- space: O(n), Size of recursion tree can go upto n +- time: O(n), `memo array is filled exactly once`!!! + + + +--- + +**498. [346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison +- Note: if we it is calculate moving-window-product, better to use deque :) +- Sliding window? + - It has the spirit of slinding window: 1) maintain a range; 2) check range size `if (queue.size() > size)` + - Though, the solution must use a data structure to store data; it is not the traditional sliding window type of `left/right` pointer problem + + + +--- + +**499. [145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Method1: DFS, Recursive +- trivial, 先加left recursively, 再加right recursively, 然后组成头部. +- Option1 w/o helper; option2 with dfs helper. + +#### Method2, Iterative, Stack +- Option1: reversely add to list +- 双stack的思想, 需要在图纸上画一画 + - 原本需要的顺序是: 先leftChild, rightChild, currNode. + - 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild + - 这样出来的结果是reverse的, 那么翻转一下就可以了. +- reverse add: `list.add(0, x)`; +- 利用stack的特点 + - 每次加element进stack的时候, 想要在 bottom/后process的, 先加 + - 想要下一轮立刻process的, 最后push进stack. +- Option2: regular sequence add to stack: add curr, right, left + - Use set to contain the processed children + - only process curr if its children is processed + + + + +--- + +**500. [938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)** Level: Easy Tags: [BST, Recursion, Tree] + +#### DFS based on BST rules +- sum up the matching L & R + - Find (L,R) on left child + - Find (L,R) on right child + - Find (L,R) covering the root node +- space O(n), worst case O(logn), height of dfs. +- time O(n) to find all nodes between (L, R) + +#### Iterative +- Using stack, or queue, list: any data structure (we are not doing ordered search) +- space O(n) +- time O(n) + + + +--- + +**501. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + +**502. [68. Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/68.%20Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- greedy approach: line up as many words as possible; once exceed the MaxLength, justify the list of words +- Steps + - 1) Split & group + - 2) Juststify a row of words + - 3) clean up last row +- Calcualte bounded row length = `width + (list.size() - 1)`. `list.size()-1` = Minimum amount of slot/space used. +- Calculate max ave spaces ot insert into each slot = `totalExtraSpace/slot` +- `Juststify a row of words`: + - 1) take a list of words and assume minimum 1 space in-between words + - 2) distribute the remaining spaces evenly to each slot +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**503. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**504. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + +**505. [242. Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/242.%20Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +#### int[26] + +#### HashMap + + + +--- + +**506. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**507. [217. Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/217.%20Contains%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 + + + +--- + +**508. [103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + + +#### 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 + + + +--- + +**509. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**510. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**511. [64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%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(N) + - 1) 需要在同一个for loop里面完成initialization, 2) reuse dp[i][j] + - Reason: dp[i % 2][j] 在被计算出来的时候, 马上在下一轮会被用. 被覆盖前不用,就白算 + - Option3 below initialize dp outside of loop: 看起来固然简单, 但是不方便空间优化 + +#### DFS (top-down) Thinking process +- Enumerate the possible 2 paths: go right, go down +- sub problem: dfs(i+1,j), dfs(i,j+1); pick the min of the two +- memoization: after the path from a point (i,j) to end is computed, record memo[i][j] to avoid repatative calculation +- time: O(mn), only visite and record memo[i][j] once. +- space: O(mn) extrem case of m=100000, n = 2; the stack height is O(mn) + + +--- + +**512. [796. Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/796.%20Rotate%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 不变 + + + +--- + +**513. [229. Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/229.%20Majority%20Element%20II.java)** Level: Medium Tags: [Array, Moore Voting] + + +#### Two counters, Moore Voting +1. Moore voting: vote可以加减, 一旦为零, 换下一个candidate, 之前抵消掉的算作清零. +1. 一个array里面, 最多也只有2个数字 出现次数大于2次, 所以用A/B表示. +1. count overall apperance at the end for the two items: countA, countB +1. save to result as valA, valB +1. 有点 moore voting的意思: + - 当count == 0的时候, reset + - 两个candidate A/B都不等, 那么countA--, countB-- +1. 最终重新计数, 然后比较出结局. +1. 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +#### Sort + count +- O(nlogN) + + + +--- + +**514. [1041. Robot Bounded In Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/1041.%20Robot%20Bounded%20In%20Circle.java)** Level: Easy Tags: [String] + +简单的character checking. 各个方向, 加加减减. + + + +--- + +**515. [2. Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/2.%20Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. 跟Add Binary的理解方式一模一样. + +#### Math, Linked List +- reverse order helps calculation + - add additional carry to end + - not same length: align on left +- traverse till both ends +- 遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + + + + +--- + +**516. [157. Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/157.%20Read%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)` +- Edge Case: + - When there is not enough space to the result buffer, `i + 3 > n`, then only copy what we can: `Math.min(n - i, count)` + - `count < 4` meaning there is not enough content to read, break + + + +--- + +**517. [114. Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/114.%20Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### Method1: DFS return tail node +- DFS to return the tail of the flattened list +- Careful handling the null child. Choose one and both works: + - Option1: put non-null as right child and continue dfs + - Option2: put non-null as left child and continue dfs + +#### Method2: void DFS +- latten the tree, no extra space. + - 1. Set right node in buffer, and connect: `root.right = root.left`, DFS flatten(root.right) + - 3. 移花接木: traverse to tail of the current root.right and attach the buffer node. + - 3. flatten the remaining of buffer + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**518. [121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### min[n] +- 每天都就交易价格,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 dont need to buyin at 5, 3, 6, 4 later on; + - only need to sell on higher prices. + + + +--- + +**519. [1004. Max Consecutive Ones III.java](https://github.com/awangdev/LintCode/blob/master/Java/1004.%20Max%20Consecutive%20Ones%20III.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + + +#### Sliding window + Left/Right Two Pointers +- https://leetcode.com/problems/max-consecutive-ones-iii/solution/ +- Start with DFS thought, but realize redundant calculations: + - we never need to flip 2 indexes [A], [C] from 0 -> 1, if there is a [B] in middle that is 0 too + - the flipped k zeroes must be consecutive too +- we can utilize two pointers to establish a window that captures k zeroes + - always expend right pointer; if seeing an zero, k-- + - note: `len = right - left + 1` is the ongoing max length + - when k < 0 (too many zeros), we need to slide the left side of the window to make sure: + - keep window len + - potentially do k++ when A[left]==0 +- goal: matain a max size of the window, until right == n +- return (right - left). at this moment, right == n, so no need to (right - left + 1) + + + +--- + +**520. [1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)** Level: Medium Tags: [Array, Hash Table, TreeMap] + + + +#### Hash Table, TreeMap; atomic save +- store efficiently: use List>. only preserve changed itemd +- if no match, find last modifed item based on snapId, use TreeMap.floorEntry + - map.floorEntry(id) return the item.key lower or equal to id +- Utilize a `buffer: Map` and perform atomic save + + + +--- + +**521. [273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%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来找到结果, 每段的处理方法都是一样的 +- Note: + - StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 + - 注意加 " " 的时候, 如果多余, 要`trim()` + - 注意, `小于20的数字, 有自己的特殊写法, 需要额外handle` + - 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 +- Thinking process: + - `1 ~ 19`: [one, two ... nine, ten, eleven, ...., ninteen] + - `20 ~ x0`: [twenty, thirty, fourty, ... ninety] + - `x00`: hundred: 100 + - thousand: 10^3 + - million: 10^6 + - billion: 10^9 + - trillian: 10^12 way over 2^31, not needed +- plan: + - parse 3 digits at a time + - convert the 3 digit to [xx hundred xx-ty x] + - come up with a string[] + - insert the thousands/million/billion to the string[] + + + +--- + +**522. [304. Range Sum Query 2D - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/304.%20Range%20Sum%20Query%202D%20-%20Immutable.java)** Level: Medium Tags: [DP, PreSum] + + +#### Method1: rangeSum/caching +- build rangeSum[i][j]: square range sum from (0,0) to (i,j), O(mn) to init +- query: time O(1) + +#### Method2: presum/caching +- build rowPreSum[i][j]: row i sum from [0 ~ j], O(mn) to init +- callign takes O(m); space O(mn) + + + + +--- + +**523. [605. Can Place Flowers.java](https://github.com/awangdev/LintCode/blob/master/Java/605.%20Can%20Place%20Flowers.java)** Level: Easy Tags: [Array, Greedy] + + +#### Array +- just check flowerbed[i-1] & flowerbed[i+1] and count + + + +--- + +**524. [1. Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1.%20Two%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. + + + + +--- + +**525. [118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)** Level: Easy Tags: [Array, Basic Implementation, List] + + + + +--- + +**526. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**527. [283. Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/283.%20Move%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 + + + +--- + +**528. [208. Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/208.%20Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- Trie Structure: + - trace the char to children node: Map + - boolean isEnd to indicate if there is string end with this node + - `TrieNode {boolean isEnd; Map children}`. +- No need to store letter c in TrieNode +- 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。 + + + + + +--- + +**529. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + + +--- + +**530. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**531. [430. Flatten a Multilevel Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/430.%20Flatten%20a%20Multilevel%20Doubly%20Linked%20List.java)** Level: Medium Tags: [DFS, Linked List] + + +#### DFS +- Depth-first: + - 1) process curr.child, return tailChild + - 2) connect tailChild.next = curr.next +- function: link(Node a, Node b); + + + +--- + +**532. [63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%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] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + + + +--- + +**533. [52. N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/52.%20N-Queens%20II.java)** Level: Hard Tags: [Backtracking] + + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking (with replacement) +- Each row has just 1 Queen value +- As CC book suggests, use `int[] columns` of length n to store all queen col positions for n rows + - `int[] columns` is slightly easier to backtrack by updating certain index i with new col + - list will usualy has the add/remove pattern for backtracking + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + + + + +--- + +**534. [1033. Moving Stones Until Consecutive.java](https://github.com/awangdev/LintCode/blob/master/Java/1033.%20Moving%20Stones%20Until%20Consecutive.java)** Level: Easy Tags: [Basic Implementation, Sort] + + +#### Analyze to understand +- put 3 elements into array, sort and follow below rules: +- min: + - if 3 elements consecutive, 0 move. + - if only 1 pair of the two elemnets consecutive or if they have 1 slot in between, it needs exactly 1 move + - otherwise, at most 2 moves +- max: # of open slots between them (high - low + 1) - n, where n = 3 +- Follow up: `1040. Moving Stones Until Consecutive` is more interesting with special rulese (cannot move to `ending spot`), and it uses sliding window concept + + + +--- + +**535. [139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- Bottom-up, test simply case. Sequence DP. +- true/false problem, think about dp + - 子问题: 前i个字母, 是否可以有valid break + - check: 1) dp[j] && 2) `if substring(j, i) valid`, for all j = [0 ~ i] + - dp = new boolean[n + 1]; dp[0] = true; + - test: `dp[i] |= dp[j] == true && word[j, n] in dict`. + - Need iterate over i = [0 ~ n], also j = [0, i] + - When there is a way to make dp[i] == true, then break the [j ~ i] loop, move on to test dp[i++] +- Use set dict: `dict.contains()` +- Improvement: O(n) to figure out max length, so we can skip some substring[j~i] dict.contains() +- overall O(n^2) time since the double for loop + +#### DFS +- Top-Down, break into small problems: Check front subString, and put the rest substring into dfs to test +- Memoization: for tested failed substring, record and do NOT test them again. +- Same Improvement as in DP: use max/min length of dict words as boundary + + + +--- + +**536. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**537. [125. Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/125.%20Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Two Pointers +- Time O(n), Space O(1). +- 普通方法, 两边check, 速度相比较regular expression更快. leetcode 4ms. +- Use helper functions. + +#### 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/ + + + +--- + +**538. [449. Serialize and Deserialize BST.java](https://github.com/awangdev/LintCode/blob/master/Java/449.%20Serialize%20and%20Deserialize%20BST.java)** Level: Medium Tags: [Tree] + + +#### DFS, Divide and Conquer, Preorder (utilizing BST) +- with BST, we can: + - skip adding the null nodes into the serialized string: `String NULL = "#"` + - In deserialization: use min/max boundary to check if queue.peek() can be added: + - if not meeting BST condition, skip this dfs and let other call to consume the queue +- Faster because it shortens the serialized string + + +#### DFS, Divide and Conquer, Preorder (w/o using BST) +- Take reference in Serialize and Deserialize Binary Tree +- The approach works but does not utilize Binary Search Tree properties + + + +--- + +**539. [274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.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. + +#### Couting Sort / 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 + +#### More about Counting Sort +1. Application: for number/character range +1. Steps: + - Find range, define countArray + - Count element and record in the array + - PreSum the countArray + - Start from beginning of the array, `print & decrese count` to produce the sorted elements + + + + +--- + +**540. [160. Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/160.%20Intersection%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 + + + +--- + +**541. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + +**542. [724. Find Pivot Index.java](https://github.com/awangdev/LintCode/blob/master/Java/724.%20Find%20Pivot%20Index.java)** Level: Easy Tags: [Array, PreSum] + + +#### PreSum +- want to find `nums[i - 1] == nums[n - 1] - nums[i]`, given: + - preSum[i], sum from [0, i] inclusive + - preSum[j] - preSum[i] = [i+1, j] inclusive +- O(n) to build preSum +- O(n) to find pivot + + + +--- + +**543. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + +**544. [65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**545. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**546. [364. Nested List Weight Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/364.%20Nested%20List%20Weight%20Sum%20II.java)** Level: Medium Tags: [DFS, NestedInteger] + + +#### Method1: DFS +- Build a list of NestedInt +- DFS: + - sum up integers in the list are integers + - dfs on nested list + - overallSum = sum * (depth+1) + - End state: if no nested list (no more child dfs), return depth 1 +- Parent level: sum up all ints and times the (depth+1) + + +#### Method2: BFS +- Using stack to flatten all nestedList, and process in the end +- Can actually use list, does not need to be stack. +- uses more memory + + + +--- + +**547. [49. Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/49.%20Group%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 (too slow) +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**548. [720. Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/720.%20Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序(lexicographically), 排序以后才能逐个看是否partial string已经存在 + - 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 + - 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: + - 长 string 排在前; + - 相等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) + +#### 从最长的word开始做 +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**549. [438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)** Level: Medium Tags: [Hash Table, Sliding Window, Two Pointers] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### Method1: Sliding Window, Hash Table. +- A creative way of using anagram char count `hash[c] >= 0` to determine if the curr c is a target char of the deesired anagram. + - because we always reduce hash[c]-- for all characters + - so only the anagram chars would be `hash[c] >= 0` after reducing. +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +- Slinding window always has left/right pointer: + - 1) at any given time move 1 index at a time: expand right window, process rsult, shrink left window + - 2) one of the basic goal is to maintain fixed window size +- algo: + - calc char freq of the target p, and store in a hash[256]; it will be used to distinguish anagram chars: `hash[c] >= 0` indicates a anagram char + - expand right window: move right to expand the window; ONLY when meeting a anagram char, count-- + - process result: if count reduces to 0, one anagram is found + - shrink left window: if (right - left) == p.length(), drop curr left char, and move forward +- how could we rely on only just `count == 0`? + - the hidden pre-condition is `right - left must already be p.length()`, which is validaterd in prev iteration +- time: O(n) +- space: O(1) + +#### Method2: HashTable +- count character apperance -> hash table, here just a int[26] + - use a window to record count++ and count--, in order to compare with countP +- prep the countP takes O(m) time +- time: O(n) + O(m) +- space: O(n) + + + + + +--- + +**550. [632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)** Level: Hard Tags: [Hash Table, Sliding Window, Two Pointers] + + +#### Method1: Sliding Window +- First sort all of the items together by actual val using `Node {int val, int row}` +- Slinding window goal: + - 1) use right to find range that touches all rows, + - 2) use left to shrink the range +- Sliding Window Template + - move right pointer + - Counts[i] = # of elements used in left/right range + - when counts[i] == 0, countUnique++; the number of row/list being included + - when count == row size: + - processing & save shorter range by using left/right Pointers + - move left pointer; when counts[i] == 0, countUnique-- +- time: O(nlogn) for initial sort and then O(n) to process +- space: O(n) +- What is hard here? To think of the idea of counting one usage of each row: + - when each all rows are used at least 1 time + - calculate the min dist + +#### Method2 PQ?, Similar to merging k array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104893/Java-Code-using-PriorityQueue.-similar-to-merge-k-array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/solution/ + + + +--- + +**551. [138. Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/138.%20Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List, time, space: O(n) +- Basic Implementation of copy linked list: + - use a iterator node to iterate over the list: 遍历head.next .... null. + - use a dummy node to hold reference to the iterator node. +- Map: 1. avoid creating same node; 2. return the new node if existing + - 每一步都check map里面有没有head. 没有? 加上 + - 每一步都check map里面有没有head.random. 没有? 加上 +- Note, there is a way to skip the extra map O(n): https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43491/A-solution-with-constant-space-complexity-O(1)-and-linear-time-complexity-O(N) + - However, creating a deep clone of the list is already O(n) extra space, so it is NOT effectively O(1) w/o map + - It may be beneficial, if we can not hold all nodes in memory, then the approach w/o map is more applicable. + + + +--- + +**552. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + +**553. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + +**554. [33. Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/33.%20Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 + - 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` + - 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- Binary search template: + - 1) `start + 1 < end` (adjacent indexes) + - 2) start/end = mid, + - 3) compare start and end individually + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**555. [760. Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/760.%20Find%20Anagram%20Mappings.java)** Level: Easy Tags: [Hash Table] + + +- HashMap 存index list +- 遍历一遍数组A, 列举出所有元素 + + + +--- + +**556. [133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%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: `map` + - 1) make new curr node; + - 2) clone all neibhors and add them +- Use the map to avoid visited nodes +- time: O(n). visit all nodes +- space: O(n). Technically only travels n levels/stacks to circle all nodes (undirected & connected) + +#### 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. + + + +--- + +**557. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**558. [636. Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/636.%20Exclusive%20Time%20of%20Functions.java)** Level: Medium Tags: [Stack] + + +#### Stack +- Task time range: + - start range = next task timestamp - start.timestamp + - end range = curr task timestamp - last task timestamp + 1; because end node is inclusive. +- How to think of using stack: a task cannot finish until end is met; a early task cannot stop until a later task ends + - Alternatively, we can use a hashmap to track as well +- Keep track of the timestamp +- make sure to +1 when end node is met because end task is inclusive to this finishing task + + + + +--- + +**559. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**560. [1170. Compare Strings by Frequency of the Smallest Character.java](https://github.com/awangdev/LintCode/blob/master/Java/1170.%20Compare%20Strings%20by%20Frequency%20of%20the%20Smallest%20Character.java)** Level: Easy Tags: [Array, String] + + +#### Method1: letter frequency map, kinda bucket sort +- Goal: find word count that fits into `f(queries[i]) < f(W)` +- What if: we can store the f(W) as preSum, then goal: `rst[i] = preSum[end] - preSum[queryWordCount]` + - count(W) and store in count[i] + - calc preSum + - processs queries array +- kinda bucket sort: + - 1) we know the boundary of the word length, so we can create `bucket` + - 2) `function count(w)` can produce a value that sort a word into a specific bucket slot + - extend: the bucket can store keys that links back to the word (if there are follow up questions) +- time: O(m + n) +- space: O(m + n) + +#### Method2: No brain solution, basic impl based on the desc, w/o search. +- time: + - O(nm) to count all words, O(nlogn) to sort the wordCount + - O(nm) to to count all queries + - O(n^2) to perform the match +- space: O(n) + + + +--- + +**561. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**562. [745. Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/745.%20Prefix%20and%20Suffix%20Search.java)** Level: Hard Tags: [Trie] + + +#### Chain `suffix # prefix` +- Build Trie for all combinations of `suffix#prefix`; all assigned with weight +- how does it make sure to return largest weight/index? + - when we build trie, always update weight for the path nodes it goes through + - yes, it overrides, but this problem does not care if some words are not found +- Time: + - build: go through all words O(n) * word length * 2 => O(n) + - query: O(1) tree height is just at most 20. +- Space: O(N) store all words + + + +--- + +**563. [8. String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/8.%20String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- Handling use cases +- Parse steps: + - 0. trim space + - 1 parse operator + - 2 trim leading zero + - 3. get number string +- Validation: + - 1. max length over max integer length + - 2. exceed min/max value +- Alternatively, regular expression, but not applicable in interview: if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**564. [361. Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/361.%20Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Method1: 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 的变换. + +#### Method2: Analyze, col count array: +- Inspired by: https://leetcode.com/problems/bomb-enemy/discuss/83387/Short-O(mn)-time-O(n)-space-solution +- Analyize the problem: need to add up 2 directions at any given point. + - Notice 1: if I traverse row by row, each colSum at a specific col j is likely to be the same + - Unless there is a Wall in last row, so we have to calclate the col sum starting from current row, below the Wall + - Notice 2: for row it is the same: + - If I in a new spot row[i][j], where (i-1) is Wall, i need to sum row from 0 +- we will process each point: + - process row by row and add up row sum + - buffer col[j] in an array vertically so we can resue + - make sure to recalculate row sum or col sum if last row index or last col index is Wall +- time: O(mn) traversal +- space: O(n) only use a column array + + + +--- + +**565. [94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Method1: DFS +- option1: dfs + rst list to carry results +- option2: Divide and Conquer, 在自己的基础上recursive, 不用helper function +- O(n) time + +#### Method2: Iterative, Stack inorder traversal +- 1) Add root.leftPath all the way to leaf, 2) process curr 3) Move to right if applicable 4) add all right.leftPath +- O(n) time, O(h) space + + + + +--- + +**566. [402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)** Level: Medium Tags: [Greedy, Monotonous Stack, Stack] + + +#### Monotonous Stack (Increasing) +- Greedy: Remove 1) earlier digits(数位靠前权值大), 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. + + + + +--- + +**567. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**568. [1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS, top-down +- key concetpL the `common ancester of deppest leaves` must have its `two branch being same depth`. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +- time: O(n) traversal 1 pass +- space: O(n) dfs worst case depth + +#### Method2: bottom-up, find leaves first and traverse backwards +- 1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +- 2) use leaf nodes to find way backwards till common node is found; return +- time: O(n) but two passes +- space: O(n) dsf + map storage +- this approach is more brutle and uses exrtra spaces + + + +--- + +**569. [921. Minimum Add to Make Parentheses Valid.java](https://github.com/awangdev/LintCode/blob/master/Java/921.%20Minimum%20Add%20to%20Make%20Parentheses%20Valid.java)** Level: Medium Tags: [] + + + +#### Method1: Stack +- use stack to verify the input/output of '(' and ')' +- time, space: O(n) + +#### Method1: Simpilfy stack with open parentheses count +- time:(n), space: O(1) + + + +--- + +**570. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + +**571. [785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)** Level: Medium Tags: [BFS, DFS, Garph] + + +#### DFS marking each node with a state +- `bipartite` require each node to be in exact 1 party, which means it only has 1 state +- DFS to mark node with one state; and mark its edges as reversed state + - If any node state has been assigned by different from desired one, return false. + +#### BFS, Queue +- Use `Boolean states[i]` to represent visted & state +- Try all nodes with for loop, and skip visited nodes (similar validation rules as in dfs) +- In `int next : graph[curr]`, test next level first before adding. + + + +--- + +**572. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**573. [71. Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/71.%20Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path: + - 1. `.` 代表current directory, 可以忽略. + - 2. `../` 表示previous level. + - 3. double slash 可以忽略. + - 4. empty string 要output `/` +- parse by '/', and go over using stack + - put [folder] in stack + - ".." pop() 1 element of the stack, if anything + - "." stays the same +- output stack reversely: connect with '/', skip tail + + + +--- + +**574. [34. Find First and Last Position of Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/34.%20Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- need search left bound & right bound. +- use input parameter `direction` to binary search function +- Option0: simplification, inspired by `278. First Bad Version - Method1: Check is-NOT-BadVersion` + - 1) if found match, but NOT sure it is desired boundary, just leave it and keep going + - 2) check the final results after `binary search while loop` completes + - WHY? code is easier to read in this way. + + + +--- + +**575. [278. First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/278.%20First%20Bad%20Version.java)** Level: Easy Tags: [Binary Search] + + +#### Method1: Check is-NOT-BadVersion +- simply binary Search: if not bad, assign `start = mid+1` + +#### Method2: Check ifBadVersion +- 根据isBadVersion的性质,判断还如何end=mid or start=mid. +- A bit more code to handle + + + +--- + +**576. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + +**577. [721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Union Find] + +- time O(mn) + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Method1: 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, 最终用来输出. + +#### Method2: 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) + +### DFS +- TODO + + + +--- + +**578. [689. Maximum Sum of 3 Non-Overlapping Subarrays.java](https://github.com/awangdev/LintCode/blob/master/Java/689.%20Maximum%20Sum%20of%203%20Non-Overlapping%20Subarrays.java)** Level: Hard Tags: [Array, DP] + + +#### DP, Divide and conquer +- split into 3 parts [0, i -1], [i, i + k -1]. [i + k, n - 1] +- NOTE: be very careful about index handling: + - `presum[i + 1] - presum[0]` gives inclusive range of `[0, i]` +- Use DP to record the starting position of max sum, +- inspired by: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C%2B%2BJava-DP-with-explanation-O(n) + - 1) calculate preSum with range [0, n] + - 2) calculate leftMaxIndex[], rightMaxIndex[] + - 3) test middle range to find max solution +- Note: the test range for 1, 2, 3 always start with assumption that k has been consumed from one side +- Note: When need to record at max/min value change, we can check/assign it manually (rather than use a object to carry & sort) + + + + +--- + +**579. [101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### Method1: DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Method2: interative with queue +- put left or right children in pair + +#### Method3: iterative with Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**580. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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" + + + +--- + +**581. [698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)** Level: Medium Tags: [DFS, DP, Recursion] + + +#### Method1: Brutle DFS to find subsets and return results +- Target = total / k, fixed. +- DFS: Pick one num and dfs with the remaining nums for subproblem + - subproblem1: accumulate local sum to target + - EndState: accumulatedSum == target, continue with below + - subproblem2: after accumulatedSum == target, continue dfs with k-1 + - EndState: k == 0, return overall true +- Option1: DFS with nums, and boolean[] visited. Fast +- Option2: DFS with queue. + - Specially handling: dfs(size+1) to prevent `while(size-- >0)` from skipping last item at index 0 + + +#### Method2: DP +- the problem aims to find true/false capability +- bit-masking. TODO. The DP approach is kinda hard-level +- https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/335668/DP-with-Bit-Masking-Solution-%3A-Best-for-Interviews + + + +--- + +**582. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**583. [13. Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/13.%20Roman%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 + +#### use map to store combinations + + + + +--- + +**584. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + +**585. [671. Second Minimum Node In a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/671.%20Second%20Minimum%20Node%20In%20a%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +#### BFS +- min tree: parent node is the min of left/right child +- BFS to traverse the tree and find 1st non-root smallest val +- Improvement area: when `node.val >= nextMin`, no need to dive into node children since it is a min Tree. + +#### DFS +- Find left and right val: + - if left/right equals root.val, that means the left or right sub children could have larger number + - Therefore DFS into left or right +- compare and return min(left, right) + + + +--- + +**586. [366. Find Leaves of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/366.%20Find%20Leaves%20of%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS: store nodes at the same depth +- the leaves are at depth 0 and the root is at highest depth +- dfs: the depth = index of the rst, start from depth = 0 at leaf +- end state: leaf node, add to rst, and return depth + + + + +--- + +**587. [235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的 path +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Beofre lasts common ancestor is found: p and q should follow same search pattern: + - compare with root, then dfs(left) or dfs(right) + - util p and q fall on two sides of root, then return root + - 非常巧妙, 但是也比较局限; 稍微变条件, 就很难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(logn) extra space: recursive stack space +- O(logn) time + + + +--- + +**588. [156. Binary Tree Upside Down.java](https://github.com/awangdev/LintCode/blob/master/Java/156.%20Binary%20Tree%20Upside%20Down.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS +- Given that left & right nodes are always available in pair, at each level: + - perform dfs to find new root: return deepest left node as root + - pick out curr left node and fix current level: + - add right node as left node + - add root as right node + + + +--- + +**589. [416. Partition Equal Subset Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/416.%20Partition%20Equal%20Subset%20Sum.java)** Level: Medium Tags: [Backpack, DP] + +#### Backpack DP +- the problem turns into: can we find a subset of items that sum up to target sum? +- create `boolean dp[j]` to represent if we can sum up to j, where j = sum value + - want to try out all items in num, + +#### DFS +- use dfs to find a subset of items that sum up to target sum? + + + +--- + +**590. [611. Valid Triangle Number.java](https://github.com/awangdev/LintCode/blob/master/Java/611.%20Valid%20Triangle%20Number.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Method1: Fix max and backward counting +- Sort nums: O(nlogn) +- Set max value fixed on right side at k + - set 2nd value from right index j + - set last value at min index i + - if `nums[i] + nums[j] > nums[k]`: with fixed j, i can pick from [i, j-1] combinations + - then j--, to pick another j candidate + - maintain a window [i,j]; if invalid, move i++ +- time: O(n^2) +- Note: very similar to 3-sum, fixing 1 index and use 2 pointers to move window + +#### Method2: Fix min and forward counting +- Sort nums: O(nlogn) +- Set min value at i + - set 2nd value at j=i+1; and 3rd value at k=i+2 + - find max of k that fits into triangle + - count all possible k candidates from [j+1, k] + - then move j to a new candidate +- O(n^2) + + + +--- + +**591. [341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, NestedInteger, Stack] + + +#### Method1: Stack holds items from back of the list +- Option1: always set integer on top of the stack everywhere + - if not, poping stack until the top is integer + - code is easy +- Option2: in hasNext(), faltten the list in stack + +#### Method2: DFS preprocessing. +- 用queue to store all items. Kinda hack. Defeat the purpose of the problem. +- Super fast to query next(), however, needs to holds everything in memory +- O(n) + + + +--- + +**592. [254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)** Level: Medium Tags: [BFS, Backtracking, DFS] + + + +#### Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs + + +#### Method2: BFS +- Check if the number can be devided by [2, sqrt(n)], return a list of possible factors. Only check till `Math.sqrt(n)` + - build suffixes: use the factor to devide last element of list and replace last element + - build candidate: replace last element of the queue item with new list of suffixes; add to rst + - add success item back to queue: in case last element can be simplified +- **remove dupilcates**: since we start factor from [2, sqrt(n)], the final factor list should be **ascending**!! +- time: O(x), x is the # of results +- space: O(y), y is all ongoing candidates in queue + + + +--- + +**593. [739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)** Level: Medium Tags: [Hash Table, Monotonous Stack, Stack] + + +#### Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) + +#### Method2: `Map `, kinda of like bucket sort +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: + - 1) record tempIndex[currTemp] = i; + - 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result + + +--- + +**594. [373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)** Level: Medium Tags: [Heap, MaxHeap, MinHeap] + + +#### Method1: MinHeap wiht k size +- This approach follows the pattern of finding min pair: + - 1) only need to store k pairs + - 2) always start from min of A list and min of B list + - 3) pre-build k pairs honoring A list, and then pick the min pair, and start swapping with min of list B +- First attemp all first k pairs from nums1[i] against nums2[0] <=k : O(k) +- Use queue to pull min node and save results +- Use the nums1 val from the min node, pair up with nums2[j], add back to queue to sort +- overall runtime: O(klogk) +- space: O(k) + +#### Method2: MaxHeap with k size +- Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +- overall time: O(mnLogK) +- space: O(k) + + + +--- + +**595. [256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, depends on the color of dp[n-1] + - 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- Need to have status with dp array: int[index][color status] + - dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. + - dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- time: O(nm), m = # of colors +- space: O(nm) + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 +- space:O(1) + + + +--- + +**596. [265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + + +一排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) + + + +--- + +**597. [272. Closest Binary Search Tree Value II.java](https://github.com/awangdev/LintCode/blob/master/Java/272.%20Closest%20Binary%20Search%20Tree%20Value%20II.java)** Level: Hard Tags: [Stack, Tree] + + +#### Method1: Stack, DFS, Inorder Traversal +- find successors and predecessors using BST (both list will be sorted); in the end, we can easily get top k from the two sorted list + - with BST: **inorder traversal gives us sorted predecessors + - with BST: **reversed-inorder traversal gives us sorted successors + - smallest on top of the stack +- time: O(n) visit all nodes, O(k) to output +- space overall: O(n) to store all nodes + +#### Method2: BFS, brutle force +- Itereate over all nodes and maintain pq (improvemenet point: how to avoid traversing entire tree?) +- prioritize nodes that are closer to target, so we may stop early when result reaches k candidates +- time: O(n*logn) +- kinds slow and not utilizing BST + + + +--- + +**598. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + +**599. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high> 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..e26160f --- /dev/null +++ b/TagREADME.md @@ -0,0 +1,2454 @@ + +Table of Contents +================= + +* [Array (126)](#array-126) +* [DFS (121)](#dfs-121) +* [DP (94)](#dp-94) +* [Hash Table (86)](#hash-table-86) +* [String (78)](#string-78) +* [Tree (69)](#tree-69) +* [Two Pointers (57)](#two-pointers-57) +* [BFS (54)](#bfs-54) +* [Math (45)](#math-45) +* [Binary Search (45)](#binary-search-45) +* [Stack (38)](#stack-38) +* [Divide and Conquer (38)](#divide-and-conquer-38) +* [Backtracking (35)](#backtracking-35) +* [Linked List (34)](#linked-list-34) +* [Sort (31)](#sort-31) +* [Lint (27)](#lint-27) +* [Design (27)](#design-27) +* [Greedy (24)](#greedy-24) +* [BST (23)](#bst-23) +* [PriorityQueue (23)](#priorityqueue-23) +* [Heap (22)](#heap-22) +* [Sequence DP (21)](#sequence-dp-21) +* [Graph (20)](#graph-20) +* [Bit Manipulation (19)](#bit-manipulation-19) +* [Basic Implementation (18)](#basic-implementation-18) +* [Segment Tree (17)](#segment-tree-17) +* [Coordinate DP (17)](#coordinate-dp-17) +* [Union Find (16)](#union-find-16) +* [Enumeration (15)](#enumeration-15) +* [Memoization (15)](#memoization-15) +* [Binary Tree (14)](#binary-tree-14) +* [PreSum (13)](#presum-13) +* [Sliding Window (13)](#sliding-window-13) +* [Trie (11)](#trie-11) +* [MinHeap (11)](#minheap-11) +* [Subarray (11)](#subarray-11) +* [Backpack DP (8)](#backpack-dp-8) +* [Status DP (8)](#status-dp-8) +* [Sweep Line (7)](#sweep-line-7) +* [Quick Sort (7)](#quick-sort-7) +* [Double Sequence DP (6)](#double-sequence-dp-6) +* [Topological Sort (6)](#topological-sort-6) +* [Merge Sort (5)](#merge-sort-5) +* [Permutation (5)](#permutation-5) +* [Partition DP (5)](#partition-dp-5) +* [MaxHeap (5)](#maxheap-5) +* [Expression Tree (5)](#expression-tree-5) +* [TreeMap (5)](#treemap-5) +* [Game Theory (4)](#game-theory-4) +* [Combination (4)](#combination-4) +* [TreeSet (4)](#treeset-4) +* [Interval DP (4)](#interval-dp-4) +* [Binary Indexed Tree (4)](#binary-indexed-tree-4) +* [Bucket Sort (4)](#bucket-sort-4) +* [Doubly Linked List (3)](#doubly-linked-list-3) +* [HashHeap (3)](#hashheap-3) +* [Cycle Detection (3)](#cycle-detection-3) +* [Minimum Binary Tree (3)](#minimum-binary-tree-3) +* [MiniMax (3)](#minimax-3) +* [NestedInteger (3)](#nestedinteger-3) +* [Queue (3)](#queue-3) +* [Monotonous Stack (3)](#monotonous-stack-3) +* [Partition (3)](#partition-3) +* [Slow Fast Pointer (3)](#slow-fast-pointer-3) +* [Sequence DFS (2)](#sequence-dfs-2) +* [Double Recursive (2)](#double-recursive-2) +* [Moore Voting (2)](#moore-voting-2) +* [Recursion (2)](#recursion-2) +* [Brainteaser (2)](#brainteaser-2) +* [Matrix DFS (2)](#matrix-dfs-2) +* [Quick Select (2)](#quick-select-2) +* [PreProduct (2)](#preproduct-2) +* [BIT (2)](#bit-2) +* [Deque (2)](#deque-2) +* [Geometry (2)](#geometry-2) +* [Edge Case (2)](#edge-case-2) +* [PQ (1)](#pq-1) +* [LinkedHashMap (1)](#linkedhashmap-1) +* [List (1)](#list-1) +* [Garph (1)](#garph-1) +* [KMP (1)](#kmp-1) +* [Binary Search on Value (1)](#binary-search-on-value-1) +* [Analysis (1)](#analysis-1) +* [Interval (1)](#interval-1) +* [Bitwise DP (1)](#bitwise-dp-1) +* [Semaphore (1)](#semaphore-1) +* [Pruning (1)](#pruning-1) +* [Reservior Sampling (1)](#reservior-sampling-1) +* [Rotation (1)](#rotation-1) +* [Adjacency Matrix (1)](#adjacency-matrix-1) +* [Lock (1)](#lock-1) +* [Thread (1)](#thread-1) +* [Backpack (1)](#backpack-1) +* [Two Stacks (1)](#two-stacks-1) +* [Tree DP (1)](#tree-dp-1) + + + + + + +## Array (126) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|277|[277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)|Medium|[Adjacency Matrix, Array, Graph, Greedy, Pruning]|O(n)|O(1)|Java|0| +|41|[41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)|Hard|[Analysis, Array, Edge Case]|O(n)|O(1)|Java|1| +|N/A|[Missing Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Ranges.java)|Medium|[Array]|||Java|2| +|N/A|[Summary Ranges.java](https://github.com/awangdev/LintCode/blob/master/Java/Summary%20Ranges.java)|Medium|[Array]|||Java|3| +|N/A|[Triangle Count.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangle%20Count.java)|Medium|[Array]|||Java|4| +|N/A|[Game of Life.java](https://github.com/awangdev/LintCode/blob/master/Java/Game%20of%20Life.java)|Medium|[Array]|||Java|5| +|N/A|[The Spiral Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Spiral%20Matrix%20II.java)|Medium|[Array]|||Java|6| +|766|[766. Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/766.%20Toeplitz%20Matrix.java)|Easy|[Array]|O(mn)|O(1)|Java|7| +|665|[665. Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/665.%20Non-decreasing%20Array.java)|Easy|[Array]|O(n)|O(1)|Java|8| +|896|[896. Monotonic Array.java](https://github.com/awangdev/LintCode/blob/master/Java/896.%20Monotonic%20Array.java)|Easy|[Array]|||Java|9| +|717|[717. 1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/717.%201-bit%20and%202-bit%20Characters.java)|Easy|[Array]|||Java|10| +|747|[747. Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/747.%20Largest%20Number%20At%20Least%20Twice%20of%20Others.java)|Easy|[Array]|||Java|11| +|561|[561. Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/561.%20Array%20Partition%20I.java)|Easy|[Array]|O(nlogn)|O(1)|Java|12| +|78|[78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)|Medium|[Array, BFS, Backtracking, Bit Manipulation, DFS]|O(2^n)|O(2^n)|Java|13| +|N/A|[Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)|Medium|[Array, BFS, Backtracking, DFS]|O(2^n)||Java|14| +|N/A|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|[Array, BFS, Backtracking, DFS, Hash Table, String]|||Java|15| +|N/A|[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|[Array, BST, Binary Search, DP, Queue, TreeSet]|||Java|16| +|N/A|[K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)|Hard|[Array, BST, TreeSet]|||Java|17| +|N/A|[Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)|Medium|[Array, Backpack DP, DP]|||Java|18| +|N/A|[Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)|Medium|[Array, Backtracking, Combination, DFS]|||Java|19| +|39|[39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)|Medium|[Array, Backtracking, Combination, DFS]|O(k * 2^n), k = avg rst length|O(k) stack depth, if not counting result size|Java|20| +|40|[40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%20Sum%20II.java)|Medium|[Array, Backtracking, Combination, DFS]|O(k * 2^n), k = avg rst length|O(n) stack depth, if not counting result size|Java|21| +|N/A|[Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)|Medium|[Array, Backtracking, DFS]|||Java|22| +|485|[485. Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/485.%20Max%20Consecutive%20Ones.java)|Easy|[Array, Basic Implementation]|O(n)|O(1)|Java|23| +|119|[119. Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/119.%20Pascal's%20Triangle%20II.java)|Easy|[Array, Basic Implementation]|O(k^2), pascal triangle size|O(k^2)|Java|24| +|118|[118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)|Easy|[Array, Basic Implementation, List]|O(n^2) based on pascal triangle size|O(n^2)|Java|25| +|849|[849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)|Easy|[Array, Basic Implementation, Two Pointers]|O(n)|O(1)|Java|26| +|N/A|[Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)|Medium|[Array, Binary Search]|||Java|27| +|N/A|[Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)|Medium|[Array, Binary Search]|||Java|28| +|N/A|[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|[Array, Binary Search]|||Java|29| +|N/A|[Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)|Medium|[Array, Binary Search]|||Java|30| +|N/A|[Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)|Medium|[Array, Binary Search]|||Java|31| +|88|[88. Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Search%20in%20Rotated%20Sorted%20Array%20II.java)|Medium|[Array, Binary Search]|O(logn), worst O(n)|O(1)|Java|32| +|33|[33. Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/33.%20Search%20in%20Rotated%20Sorted%20Array.java)|Medium|[Array, Binary Search]|O(logn)|O(1)|Java|33| +|34|[34. Find First and Last Position of Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/34.%20Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array.java)|Medium|[Array, Binary Search]|O(logn)|O(1)|Java|34| +|287|[287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)|Medium|[Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|35| +|N/A|[Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)|Hard|[Array, Binary Search, DFS, Divide and Conquer]|||Java|36| +|[lint]|[[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)|Medium|[Array, Binary Search, Lint, Two Pointers]|||Java|37| +|N/A|[Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)|Review|[Array, Binary Search, PreSum]|||Java|38| +|N/A|[Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)|Medium|[Array, Binary Search, Subarray, Two Pointers]|O(n)|O(1)|Java|39| +|N/A|[Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)|Hard|[Array, Binary Search, Two Pointers]|||Java|40| +|N/A|[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|[Array, Binary Search, Two Pointers]|||Java|41| +|169|[169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)|Easy|[Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort]|O(n)|O(1)|Java|42| +|N/A|[Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)|Easy|[Array, Bit Manipulation, Math]|||Java|43| +|448|[448. Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/448.%20Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)|Easy|[Array, Bucket Sort]|O(n)|O(1)|Java|44| +|N/A|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|[Array, Coordinate DP, DFS, DP, Memoization]|||Java|45| +|N/A|[Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)|Easy|[Array, Coordinate DP, DP]|||Java|46| +|62|[62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(mn), rolling array O(n)|Java|47| +|64|[64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%20Path%20Sum.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(n) rolling array|Java|48| +|63|[63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%20Paths%20II.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(mn)|Java|49| +|N/A|[Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)|Hard|[Array, Coordinate DP, DP, Greedy]|O(n)|O(1)|Java|50| +|N/A|[Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)|Medium|[Array, Coordinate DP, DP, Memoization]|||Java|51| +|674|[674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)|Easy|[Array, Coordinate DP, DP, Sliding Window]|O(n)|O(1)|Java|52| +|N/A|[Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)|Easy|[Array, DFS]|||Java|53| +|53|[53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)|Easy|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|O(n)|O(n), O(1) rolling array|Java|54| +|105|[105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|[Array, DFS, Divide and Conquer, Hash Table, Tree]|O(n)|O(n)|Java|55| +|N/A|[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|[Array, DFS, Divide and Conquer, Tree]|||Java|56| +|689|[689. Maximum Sum of 3 Non-Overlapping Subarrays.java](https://github.com/awangdev/LintCode/blob/master/Java/689.%20Maximum%20Sum%20of%203%20Non-Overlapping%20Subarrays.java)|Hard|[Array, DP]|O(n)|O(n)|Java|57| +|N/A|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|[Array, DP, Game Theory, Interval DP, Memoization]|||Java|58| +|N/A|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|[Array, DP, Game Theory, Memoization, MiniMax]|||Java|59| +|55|[55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%20Game.java)|Medium|[Array, DP, Greedy]|O(n)|O(1)|Java|60| +|N/A|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|||Java|61| +|N/A|[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|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(n), O(1) rolling array|Java|62| +|122|[122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(1) greedy, O(n) dp|Java|63| +|N/A|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|[Array, DP, Greedy, Sequence DP, Subarray]|O(m)|O(1)|Java|64| +|N/A|[Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)|Hard|[Array, DP, Hash Table, Stack]|||Java|65| +|152|[152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)|Medium|[Array, DP, PreProduct, Subarray]|O(n)|O(1)|Java|66| +|N/A|[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|[Array, DP, Sequence DP]|||Java|67| +|121|[121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)|Easy|[Array, DP, Sequence DP]|||Java|68| +|380|[380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)|Medium|[Array, Design, Hash Table]|O(1) avg|O(n)|Java|69| +|244|[244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)|Medium|[Array, Design, Hash Table, Two Pointers]|O(n) to build map, O(a + b) to query|O(n)|Java|70| +|245|[245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)|Medium|[Array, Design, Hash Table, Two Pointers]|O(n)|O(1)|Java|71| +|N/A|[Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)|Medium|[Array, Enumeration]|||Java|72| +|N/A|[Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)|Medium|[Array, Enumeration]|||Java|73| +|621|[621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)|Medium|[Array, Enumeration, Greedy, PriorityQueue, Queue]|O(n)|O(1)|Java|74| +|149|[149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%20Points%20on%20a%20Line.java)|Hard|[Array, Geometry, Hash Table, Math]|O(n^2)|O()|Java|75| +|1267|[1267. Count Servers that Communicate.java](https://github.com/awangdev/LintCode/blob/master/Java/1267.%20Count%20Servers%20that%20Communicate.java)|Medium|[Array, Graph]|O(mn)|O(m + n)|Java|76| +|1007|[1007. Minimum Domino Rotations For Equal Row.java](https://github.com/awangdev/LintCode/blob/master/Java/1007.%20Minimum%20Domino%20Rotations%20For%20Equal%20Row.java)|Medium|[Array, Greedy]|O(n)|O(1)|Java|77| +|605|[605. Can Place Flowers.java](https://github.com/awangdev/LintCode/blob/master/Java/605.%20Can%20Place%20Flowers.java)|Easy|[Array, Greedy]|O(n)|O(1)|Java|78| +|1053|[1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)|Medium|[Array, Greedy, Permutation]|O(n)|O(1)|Java|79| +|219|[219. Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/219.%20Contains%20Duplicate%20II.java)|Easy|[Array, Hash Table]|O(n)|O(n)|Java|80| +|217|[217. Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/217.%20Contains%20Duplicate.java)|Easy|[Array, Hash Table]|O(n)|O(1)|Java|81| +|1|[1. Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1.%20Two%20Sum.java)|Easy|[Array, Hash Table]|O(n)|O(n)|Java|82| +|[lint]|[[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)|Medium|[Array, Hash Table, Lint]|O(n)|O(n)|Java|83| +|[lint]|[[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)|Easy|[Array, Hash Table, Lint, PreSum, Subarray]|O(n)|O(n)|Java|84| +|N/A|[Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)|Medium|[Array, Hash Table, PreSum]|||Java|85| +|560|[560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)|Medium|[Array, Hash Table, PreSum, Subarray]|O(n)|O(n)|Java|86| +|1146|[1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)|Medium|[Array, Hash Table, TreeMap]|O(1) set, O(logn) get, O(x) snap, x = # of changes|O(n * m), n = array size, m = # of snaps|Java|87| +|N/A|[Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)|Hard|[Array, Hash Table, Union Find]|||Java|88| +|N/A|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|[Array, Interval, PriorityQueue, Sort, Sweep Line]|||Java|89| +|[lint]|[[lint]. Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Product%20of%20Array%20Exclude%20Itself.java)|Medium|[Array, Lint]|||Java|90| +|[lint]|[[lint]. Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Recover%20Rotated%20Sorted%20Array.java)|Easy|[Array, Lint]|||Java|91| +|N/A|[[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)|Easy|[Array, Lint, Quick Select, Quick Sort, Two Pointers]|O(n)|O(logN)|Java|92| +|[lint]|[[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)|Easy|[Array, Lint, String]|||Java|93| +|[lint]|[[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)|Medium|[Array, Lint, Two Pointers]|||Java|94| +|N/A|[Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)|Easy|[Array, Math]|||Java|95| +|N/A|[Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)|Medium|[Array, Math]|||Java|96| +|N/A|[Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)|Hard|[Array, Monotonous Stack, Stack]|||Java|97| +|229|[229. Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/229.%20Majority%20Element%20II.java)|Medium|[Array, Moore Voting]|O(n)|(1)|Java|98| +|N/A|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|[Array, Partition, Quick Sort, Sort, Two Pointers]|||Java|99| +|31|[31. Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/31.%20Next%20Permutation.java)|Medium|[Array, Permutation]|O(n)|O(1)|Java|100| +|238|[238. Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/238.%20Product%20of%20Array%20Except%20Self.java)|Medium|[Array, PreProduct]|O(n)|O(1)|Java|101| +|724|[724. Find Pivot Index.java](https://github.com/awangdev/LintCode/blob/master/Java/724.%20Find%20Pivot%20Index.java)|Easy|[Array, PreSum]|O(n)|O(1)|Java|102| +|414|[414. Third Maximum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/414.%20Third%20Maximum%20Number.java)|Easy|[Array, PriorityQueue]|||Java|103| +|56|[56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)|Medium|[Array, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|104| +|57|[57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)|Hard|[Array, PriorityQueue, Sort, Sweep Line]|O(n)|O(n)|Java|105| +|N/A|[Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)|Medium|[Array, Quick Sort, Sort, Two Pointers]|||Java|106| +|189|[189. Rotate Array.java](https://github.com/awangdev/LintCode/blob/master/Java/189.%20Rotate%20Array.java)|Easy|[Array, Rotation]|||Java|107| +|1040|[1040. Moving Stones Until Consecutive II.java](https://github.com/awangdev/LintCode/blob/master/Java/1040.%20Moving%20Stones%20Until%20Consecutive%20II.java)|Medium|[Array, Sliding Window]|O(nlogn)|O(n)|Java|108| +|N/A|[Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)|Medium|[Array, Sort]|||Java|109| +|N/A|[The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)|Medium|[Array, Sort, Two Pointers]|||Java|110| +|259|[259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)|Medium|[Array, Sort, Two Pointers]|||Java|111| +|15|[15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)|Medium|[Array, Sort, Two Pointers]|O(n^2)||Java|112| +|42|[42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)|Hard|[Array, Stack, Two Pointers]|O(n)|O(1)|Java|113| +|1170|[1170. Compare Strings by Frequency of the Smallest Character.java](https://github.com/awangdev/LintCode/blob/master/Java/1170.%20Compare%20Strings%20by%20Frequency%20of%20the%20Smallest%20Character.java)|Easy|[Array, String]|O(m + n)|O(m + n)|Java|114| +|N/A|[Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)|Easy|[Array, Subarray]|O(n)|O(1)|Java|115| +|N/A|[My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)|Medium|[Array, TreeMap]|||Java|116| +|N/A|[Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)|Medium|[Array, Two Pointers]|||Java|117| +|N/A|[Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)|Easy|[Array, Two Pointers]|||Java|118| +|80|[80.Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/80.Remove%20Duplicates%20from%20Sorted%20Array%20II.java)|Medium|[Array, Two Pointers]|||Java|119| +|26|[26.Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/26.Remove%20Duplicates%20from%20Sorted%20Array.java)|Easy|[Array, Two Pointers]|||Java|120| +|977|[977. Squares of a Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/977.%20Squares%20of%20a%20Sorted%20Array.java)|Easy|[Array, Two Pointers]|O(n)|O(n)|Java|121| +|88|[88. Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Merge%20Sorted%20Array.java)|Easy|[Array, Two Pointers]|O(n)|O(1)|Java|122| +|243|[243. Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/243.%20Shortest%20Word%20Distance.java)|Easy|[Array, Two Pointers]|O(n)|O(1)|Java|123| +|283|[283. Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/283.%20Move%20Zeroes.java)|Easy|[Array, Two Pointers]|O(n)|O(1)|Java|124| +|611|[611. Valid Triangle Number.java](https://github.com/awangdev/LintCode/blob/master/Java/611.%20Valid%20Triangle%20Number.java)|Medium|[Array, Two Pointers]|O(n^2)|O(logn), sorting space|Java|125| + + + + + + +## DFS (121) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|78|[78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)|Medium|[Array, BFS, Backtracking, Bit Manipulation, DFS]|O(2^n)|O(2^n)|Java|0| +|N/A|[Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)|Medium|[Array, BFS, Backtracking, DFS]|O(2^n)||Java|1| +|N/A|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|[Array, BFS, Backtracking, DFS, Hash Table, String]|||Java|2| +|N/A|[Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)|Medium|[Array, Backtracking, Combination, DFS]|||Java|3| +|39|[39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)|Medium|[Array, Backtracking, Combination, DFS]|O(k * 2^n), k = avg rst length|O(k) stack depth, if not counting result size|Java|4| +|40|[40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%20Sum%20II.java)|Medium|[Array, Backtracking, Combination, DFS]|O(k * 2^n), k = avg rst length|O(n) stack depth, if not counting result size|Java|5| +|N/A|[Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)|Medium|[Array, Backtracking, DFS]|||Java|6| +|N/A|[Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)|Hard|[Array, Binary Search, DFS, Divide and Conquer]|||Java|7| +|N/A|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|[Array, Coordinate DP, DFS, DP, Memoization]|||Java|8| +|N/A|[Max Area of Island.java](https://github.com/awangdev/LintCode/blob/master/Java/Max%20Area%20of%20Island.java)|Easy|[Array, DFS]|||Java|9| +|53|[53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)|Easy|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|O(n)|O(n), O(1) rolling array|Java|10| +|105|[105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|[Array, DFS, Divide and Conquer, Hash Table, Tree]|O(n)|O(n)|Java|11| +|N/A|[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|[Array, DFS, Divide and Conquer, Tree]|||Java|12| +|254|[254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)|Medium|[BFS, Backtracking, DFS]|O(x), x is the # of results|O(y), y is all ongoing candidates in queue|Java|13| +|269|[269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)|Hard|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n), n = # of graph edges|O(n)|Java|14| +|207|[207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)|Medium|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|15| +|46|[46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)|Medium|[BFS, Backtracking, DFS, Permutation]|O(n!)|O(n!)|Java|16| +|987|[987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)|Medium|[BFS, Binary Tree, DFS, Hash Table, Tree]|||Java|17| +|N/A|[Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)|Medium|[BFS, DFS]|||Java|18| +|N/A|[The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)|Medium|[BFS, DFS]|||Java|19| +|N/A|[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|[BFS, DFS]|||Java|20| +|301|[301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)|Hard|[BFS, DFS, DP]|||Java|21| +|297|[297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|O(n)|O(n)|Java|22| +|785|[785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)|Medium|[BFS, DFS, Garph]|O(n)|O(n)|Java|23| +|1161|[1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)|Medium|[BFS, DFS, Graph]|O(n) visit all nodes|O(n)|Java|24| +|133|[133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%20Graph.java)|Medium|[BFS, DFS, Graph]|O(n)|O(n)|Java|25| +|743|[743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)|Medium|[BFS, DFS, Graph, Heap, PQ]|O(nlogn)|O(n)|Java|26| +|1203|[1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)|Hard|[BFS, DFS, Graph, Topological Sort]|O(V + E) to traverse the graph, #nodes + #edges|O(V + E)|Java|27| +|210|[210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)|Medium|[BFS, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|28| +|N/A|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|[BFS, DFS, Graph, Tree, Union Find]|||Java|29| +|N/A|[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|[BFS, DFS, Graph, Union Find]|||Java|30| +|261|[261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)|Medium|[BFS, DFS, Graph, Union Find]|||Java|31| +|399|[399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%20Division.java)|Medium|[BFS, DFS, Graph, Union Find]|||Java|32| +|314|[314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)|Medium|[BFS, DFS, Hash Table, Tree]|O(n)|O(n)|Java|33| +|[tool]|[[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)|Medium|[BFS, DFS, Lint, Topological Sort]|O(V + E)|O(V + E)|Java|34| +|N/A|[Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)|Medium|[BFS, DFS, Matrix DFS, Union Find]|||Java|35| +|200|[200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)|Medium|[BFS, DFS, Matrix DFS, Union Find]|O(n)|O(n)|Java|36| +|339|[339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)|Easy|[BFS, DFS, NestedInteger]|O(n)|O(h), h = levels|Java|37| +|N/A|[The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)|Medium|[BFS, DFS, PriorityQueue]|||Java|38| +|N/A|[The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)|Hard|[BFS, DFS, PriorityQueue]|||Java|39| +|144|[144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)|Medium|[BFS, DFS, Stack, Tree]|O(n)|O(n)|Java|40| +|N/A|[Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)|Easy|[BFS, DFS, Tree]|||Java|41| +|102|[102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|42| +|111|[111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%20Depth%20of%20Binary%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(n)|Java|43| +|199|[199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%20Tree%20Right%20Side%20View.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|44| +|515|[515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|45| +|100|[100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(logn)|Java|46| +|1123|[1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|47| +|101|[101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(n)|Java|48| +|N/A|[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|[BST, DFS, Divide and Conquer, Linked List]|||Java|49| +|426|[426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|[BST, DFS, Divide and Conquer, Linked List, Tree]|O(n)|O(1)|Java|50| +|98|[98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)|Medium|[BST, DFS, Divide and Conquer, Tree]|O(n)|O(logn)|Java|51| +|N/A|[Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)|Medium|[BST, DFS, Stack, Tree]|||Java|52| +|N/A|[Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)|Hard|[BST, DFS, Tree]|||Java|53| +|235|[235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)|Easy|[BST, DFS, Tree]|O(logn)|O(logn)|Java|54| +|322|[322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)|Medium|[Backpack DP, DFS, DP, Memoization]|O(n * S)|O(S)|Java|55| +|257|[257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%20Tree%20Paths.java)|Easy|[Backtracking, Binary Tree, DFS]|O(n)|O(nlogn)|Java|56| +|N/A|[Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)|Medium|[Backtracking, Combination, DFS]|||Java|57| +|N/A|[Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)|Hard|[Backtracking, DFS]|||Java|58| +|131|[131. Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/131.%20Palindrome%20Partitioning.java)|Medium|[Backtracking, DFS]|O(2^n)|O(n^2)|Java|59| +|1219|[1219. Path with Maximum Gold.java](https://github.com/awangdev/LintCode/blob/master/Java/1219.%20Path%20with%20Maximum%20Gold.java)|Medium|[Backtracking, DFS]|O(n^2)|O(n) recursive depth|Java|60| +|47|[47. Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/47.%20Permutations%20II.java)|Medium|[Backtracking, DFS]|||Java|61| +|N/A|[Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)|Medium|[Backtracking, DFS, DP]|||Java|62| +|140|[140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)|Hard|[Backtracking, DFS, DP, Hash Table, Memoization]|O(n!)|O(n!)|Java|63| +|N/A|[Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)|Hard|[Backtracking, DFS, Divide and Conquer, String]|O(4^n)|O(4^n)|Java|64| +|332|[332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)|Medium|[Backtracking, DFS, Graph]|O(n^n)|O(m)|Java|65| +|22|[22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%20Parentheses.java)|Medium|[Backtracking, DFS, Sequence DFS, String]|O(2^n)|O(2^n)|Java|66| +|N/A|[Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)|Medium|[Backtracking, DFS, String]|||Java|67| +|N/A|[Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)|Easy|[Backtracking, DFS, Tree]|||Java|68| +|N/A|[Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)|Hard|[Backtracking, DFS, Trie]|||Java|69| +|N/A|[Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)|Hard|[Binary Search, DFS, Divide and Conquer]|||Java|70| +|222|[222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)|Medium|[Binary Search, DFS, Tree]|O(n)|O(h)|Java|71| +|114|[114. Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/114.%20Flatten%20Binary%20Tree%20to%20Linked%20List.java)|Medium|[Binary Tree, DFS]|O(n)|O(n), stacks|Java|72| +|N/A|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|73| +|N/A|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|74| +|[lint]|[[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Query.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|75| +|N/A|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|||Java|76| +|N/A|[Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)|Hard|[Binary Tree, DFS, Expression Tree, Stack]|||Java|77| +|N/A|[Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)|Hard|[Binary Tree, DFS, Expression Tree, Stack]|||Java|78| +|N/A|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|||Java|79| +|N/A|[Flood Fill.java](https://github.com/awangdev/LintCode/blob/master/Java/Flood%20Fill.java)|Easy|[DFS]|||Java|80| +|N/A|[Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)|Medium|[DFS, DP]|||Java|81| +|741|[741. Cherry Pickup.java](https://github.com/awangdev/LintCode/blob/master/Java/741.%20Cherry%20Pickup.java)|Hard|[DFS, DP]|O(n^3)|O(n^3), memo size|Java|82| +|1043|[1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)|Medium|[DFS, DP, Graph, Memoization]|O(n), calc memo[n]|O(n)|Java|83| +|516|[516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%20Palindromic%20Subsequence.java)|Medium|[DFS, DP, Interval DP, Memoization]|O(n^2)|O(n^2)|Java|84| +|1216|[1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)|Hard|[DFS, DP, Memoization, String]|O(n^2)|O(n^2)|Java|85| +|698|[698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)|Medium|[DFS, DP, Recursion]|O(k^(n-k) * k!)|O(n)|Java|86| +|N/A|[House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)|Medium|[DFS, DP, Status DP, Tree]|||Java|87| +|124|[124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)|Hard|[DFS, DP, Tree, Tree DP]|O(n)|O(logn)|Java|88| +|N/A|[Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)|Medium|[DFS, Divide and Conquer]|||Java|89| +|N/A|[Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)|Medium|[DFS, Divide and Conquer, Double Recursive, Tree]|||Java|90| +|N/A|[Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)|Medium|[DFS, Divide and Conquer, Stack]|||Java|91| +|N/A|[Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)|Easy|[DFS, Divide and Conquer, Tree]|||Java|92| +|N/A|[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|[DFS, Divide and Conquer, Tree]|||Java|93| +|N/A|[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|[DFS, Divide and Conquer, Tree]|||Java|94| +|N/A|[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|[DFS, Divide and Conquer, Tree]|O(n)|O(n)|Java|95| +|N/A|[Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)|Medium|[DFS, Divide and Conquer, Tree]|||Java|96| +|1110|[1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)|Medium|[DFS, Divide and Conquer, Tree]|O(n)|O(logn)|Java|97| +|N/A|[Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)|Easy|[DFS, Double Recursive, Tree]|||Java|98| +|N/A|[Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)|Medium|[DFS, Enumeration, Math, Sequence DFS]|||Java|99| +|N/A|[Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)|Hard|[DFS, Graph, Tree, Union Find]|||Java|100| +|N/A|[Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)|Hard|[DFS, Greedy, Math]|||Java|101| +|694|[694. Number of Distinct Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/694.%20Number%20of%20Distinct%20Islands.java)|Medium|[DFS, Hash Table]|O(n)|O(n)|Java|102| +|N/A|[Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)|Medium|[DFS, Hash Table, Tree]|||Java|103| +|721|[721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)|Medium|[DFS, Hash Table, Union Find]|||Java|104| +|430|[430. Flatten a Multilevel Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/430.%20Flatten%20a%20Multilevel%20Doubly%20Linked%20List.java)|Medium|[DFS, Linked List]|O(n)|O(1)|Java|105| +|364|[364. Nested List Weight Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/364.%20Nested%20List%20Weight%20Sum%20II.java)|Medium|[DFS, NestedInteger]|O(n), visit all nodes|O(h), depth|Java|106| +|1106|[1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)|Hard|[DFS, Stack, String]|||Java|107| +|N/A|[Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)|Medium|[DFS, Tree]|||Java|108| +|N/A|[Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)|Easy|[DFS, Tree]|||Java|109| +|N/A|[Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)|Easy|[DFS, Tree]|||Java|110| +|N/A|[Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)|Easy|[DFS, Tree]|||Java|111| +|N/A|[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|[DFS, Tree]|O(n)|O(1)|Java|112| +|236|[236. Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/236.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)|Medium|[DFS, Tree]|O(n)|O(n)|Java|113| +|1008|[1008. Construct Binary Search Tree from Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/1008.%20Construct%20Binary%20Search%20Tree%20from%20Preorder%20Traversal.java)|Medium|[DFS, Tree]|O(n)|O(n)|Java|114| +|104|[104. Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/104.%20Maximum%20Depth%20of%20Binary%20Tree.java)|Easy|[DFS, Tree]|||Java|115| +|110|[110. Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/110.%20Balanced%20Binary%20Tree.java)|Easy|[DFS, Tree]|||Java|116| +|112|[112. Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/112.%20Path%20Sum.java)|Easy|[DFS, Tree]|||Java|117| +|1026|[1026. Maximum Difference Between Node and Ancestor.java](https://github.com/awangdev/LintCode/blob/master/Java/1026.%20Maximum%20Difference%20Between%20Node%20and%20Ancestor.java)|Medium|[DFS, Tree]|O(n)|O(logn)|Java|118| +|366|[366. Find Leaves of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/366.%20Find%20Leaves%20of%20Binary%20Tree.java)|Medium|[DFS, Tree]|O(n)|O(h)|Java|119| +|156|[156. Binary Tree Upside Down.java](https://github.com/awangdev/LintCode/blob/master/Java/156.%20Binary%20Tree%20Upside%20Down.java)|Medium|[DFS, Tree]|O(n)|O(h)|Java|120| + + + + + + +## DP (94) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[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|[Array, BST, Binary Search, DP, Queue, TreeSet]|||Java|0| +|N/A|[Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)|Medium|[Array, Backpack DP, DP]|||Java|1| +|N/A|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|[Array, Coordinate DP, DFS, DP, Memoization]|||Java|2| +|N/A|[Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)|Easy|[Array, Coordinate DP, DP]|||Java|3| +|62|[62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(mn), rolling array O(n)|Java|4| +|64|[64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%20Path%20Sum.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(n) rolling array|Java|5| +|63|[63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%20Paths%20II.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(mn)|Java|6| +|N/A|[Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)|Hard|[Array, Coordinate DP, DP, Greedy]|O(n)|O(1)|Java|7| +|N/A|[Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)|Medium|[Array, Coordinate DP, DP, Memoization]|||Java|8| +|674|[674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)|Easy|[Array, Coordinate DP, DP, Sliding Window]|O(n)|O(1)|Java|9| +|53|[53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)|Easy|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|O(n)|O(n), O(1) rolling array|Java|10| +|689|[689. Maximum Sum of 3 Non-Overlapping Subarrays.java](https://github.com/awangdev/LintCode/blob/master/Java/689.%20Maximum%20Sum%20of%203%20Non-Overlapping%20Subarrays.java)|Hard|[Array, DP]|O(n)|O(n)|Java|11| +|N/A|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|[Array, DP, Game Theory, Interval DP, Memoization]|||Java|12| +|N/A|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|[Array, DP, Game Theory, Memoization, MiniMax]|||Java|13| +|55|[55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%20Game.java)|Medium|[Array, DP, Greedy]|O(n)|O(1)|Java|14| +|N/A|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|||Java|15| +|N/A|[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|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(n), O(1) rolling array|Java|16| +|122|[122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(1) greedy, O(n) dp|Java|17| +|N/A|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|[Array, DP, Greedy, Sequence DP, Subarray]|O(m)|O(1)|Java|18| +|N/A|[Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)|Hard|[Array, DP, Hash Table, Stack]|||Java|19| +|152|[152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)|Medium|[Array, DP, PreProduct, Subarray]|O(n)|O(1)|Java|20| +|N/A|[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|[Array, DP, Sequence DP]|||Java|21| +|121|[121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)|Easy|[Array, DP, Sequence DP]|||Java|22| +|301|[301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)|Hard|[BFS, DFS, DP]|||Java|23| +|N/A|[Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)|Medium|[BFS, DP, Math, Partition DP]|||Java|24| +|N/A|[Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)|Medium|[BST, DP, Divide and Conquer, Tree]|||Java|25| +|N/A|[Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)|Medium|[BST, DP, Tree]|||Java|26| +|322|[322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)|Medium|[Backpack DP, DFS, DP, Memoization]|O(n * S)|O(S)|Java|27| +|N/A|[Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)|Medium|[Backpack DP, DP]|||Java|28| +|N/A|[Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)|Medium|[Backpack DP, DP]|||Java|29| +|N/A|[Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)|Medium|[Backpack DP, DP]|||Java|30| +|N/A|[Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)|Medium|[Backpack DP, DP]|||Java|31| +|N/A|[Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)|Hard|[Backpack DP, DP]|||Java|32| +|518|[518. Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/518.%20Coin%20Change%202.java)|Medium|[Backpack DP, DP]|O(n)|O(n)|Java|33| +|416|[416. Partition Equal Subset Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/416.%20Partition%20Equal%20Subset%20Sum.java)|Medium|[Backpack, DP]|||Java|34| +|N/A|[Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)|Medium|[Backtracking, DFS, DP]|||Java|35| +|140|[140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)|Hard|[Backtracking, DFS, DP, Hash Table, Memoization]|O(n!)|O(n!)|Java|36| +|N/A|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|||Java|37| +|10|[10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|||Java|38| +|N/A|[Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)|Hard|[Binary Search, Coordinate DP, DP]|||Java|39| +|N/A|[Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)|Medium|[Binary Search, Coordinate DP, DP, Memoization]|O(n^2) dp, O(nLogN) binary search|O(n)|Java|40| +|N/A|[Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)|Hard|[Binary Search, DP, Partition DP]|||Java|41| +|N/A|[Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)|Medium|[Bit Manipulation, Bitwise DP, DP]|||Java|42| +|N/A|[Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)|Easy|[Brainteaser, DP, Game Theory]|||Java|43| +|1048|[1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)|Medium|[Bucket Sort, DP, Hash Table, Sort]|O(n)|O(n)|Java|44| +|N/A|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|||Java|45| +|N/A|[Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)|Medium|[Coordinate DP, DP]|O(n^2)||Java|46| +|221|[221. Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/221.%20Maximal%20Square.java)|Medium|[Coordinate DP, DP]|O(mn)|O(mn)|Java|47| +|361|[361. Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/361.%20Bomb%20Enemy.java)|Medium|[Coordinate DP, DP]|O(mn)|O(n) by calculating column sum|Java|48| +|523|[523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)|Medium|[Coordinate DP, DP, Math, PreSum, Subarray]|O(n)|O(k)|Java|49| +|N/A|[Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)|Medium|[Coordinate DP, DP, Status DP]|||Java|50| +|N/A|[Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)|Medium|[DFS, DP]|||Java|51| +|741|[741. Cherry Pickup.java](https://github.com/awangdev/LintCode/blob/master/Java/741.%20Cherry%20Pickup.java)|Hard|[DFS, DP]|O(n^3)|O(n^3), memo size|Java|52| +|1043|[1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)|Medium|[DFS, DP, Graph, Memoization]|O(n), calc memo[n]|O(n)|Java|53| +|516|[516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%20Palindromic%20Subsequence.java)|Medium|[DFS, DP, Interval DP, Memoization]|O(n^2)|O(n^2)|Java|54| +|1216|[1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)|Hard|[DFS, DP, Memoization, String]|O(n^2)|O(n^2)|Java|55| +|698|[698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)|Medium|[DFS, DP, Recursion]|O(k^(n-k) * k!)|O(n)|Java|56| +|N/A|[House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)|Medium|[DFS, DP, Status DP, Tree]|||Java|57| +|124|[124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)|Hard|[DFS, DP, Tree, Tree DP]|O(n)|O(logn)|Java|58| +|N/A|[Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)|Medium|[DP]|||Java|59| +|N/A|[Ones and Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/Ones%20and%20Zeroes.java)|Hard|[DP]|||Java|60| +|N/A|[k Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/k%20Sum.java)|Hard|[DP]|||Java|61| +|N/A|[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|[DP]|||Java|62| +|N/A|[Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)|Hard|[DP]|||Java|63| +|N/A|[Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)|Hard|[DP, Divide and Conquer, Interval DP, Memoization]|||Java|64| +|N/A|[Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)|Medium|[DP, Double Sequence DP, Sequence DP]|||Java|65| +|N/A|[Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)|Medium|[DP, Double Sequence DP, Sequence DP, String]|||Java|66| +|72|[72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)|Hard|[DP, Double Sequence DP, Sequence DP, String]|O(MN)||Java|67| +|N/A|[K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)|Hard|[DP, Double Sequence DP, Sequence DP, Trie]|||Java|68| +|N/A|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|[DP, Enumeration, Heap, Math, PriorityQueue]|O(n)|O(n)|Java|69| +|639|[639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%20Ways%20II.java)|Hard|[DP, Enumeration, Partition DP]|O(n)|O(n)|Java|70| +|N/A|[Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)|Medium|[DP, Game Theory, Greedy]|||Java|71| +|N/A|[Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)|Hard|[DP, Hash Table]|||Java|72| +|139|[139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)|Medium|[DP, Hash Table, Sequence DP]|O(n^2)|O(n)|Java|73| +|727|[727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)|Hard|[DP, Hash Table, Sliding Window, String, Two Pointers]|O(n^2)|O(1)|Java|74| +|N/A|[Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)|Hard|[DP, Interval DP, String]|||Java|75| +|N/A|[Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)|Medium|[DP, Math]|||Java|76| +|509|[509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.java)|Easy|[DP, Math, Memoization]|||Java|77| +|70|[70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%20Stairs.java)|Easy|[DP, Memoization, Sequence DP]|||Java|78| +|N/A|[Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)|Medium|[DP, MiniMax]|||Java|79| +|N/A|[Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)|Hard|[DP, Partition DP]|||Java|80| +|91|[91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)|Medium|[DP, Partition DP, String]|O(n)|O(n)|Java|81| +|303|[303. Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/303.%20Range%20Sum%20Query%20-%20Immutable.java)|Easy|[DP, PreSum]|O(1) query, O(n) setup|O(n)|Java|82| +|304|[304. Range Sum Query 2D - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/304.%20Range%20Sum%20Query%202D%20-%20Immutable.java)|Medium|[DP, PreSum]|O(mn) build, O(1) query|O(mn)|Java|83| +|N/A|[Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)|Easy|[DP, Sequence DP]|O(n)|O(n)|Java|84| +|N/A|[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|[DP, Sequence DP]|||Java|85| +|N/A|[House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)|Medium|[DP, Sequence DP, Status DP]|||Java|86| +|198|[198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)|Easy|[DP, Sequence DP, Status DP]|O(n)|O(n) or rolling array O(1)|Java|87| +|256|[256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)|Easy|[DP, Sequence DP, Status DP]|O(nm), m = # of colors|O(nm), or O(1) with rolling array|Java|88| +|265|[265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)|Hard|[DP, Sequence DP, Status DP]|O(NK^2):|O(K) with rolling array|Java|89| +|N/A|[Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)|Medium|[DP, String]|||Java|90| +|N/A|[Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)|Hard|[DP, String]|||Java|91| +|N/A|[Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)|Hard|[DP, String]|||Java|92| +|5|[5. Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/5.%20Longest%20Palindromic%20Substring.java)|Medium|[DP, String]|O(n^2)|O(n^2)|Java|93| + + + + + + +## Hash Table (86) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|[Array, BFS, Backtracking, DFS, Hash Table, String]|||Java|0| +|105|[105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|[Array, DFS, Divide and Conquer, Hash Table, Tree]|O(n)|O(n)|Java|1| +|N/A|[Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)|Hard|[Array, DP, Hash Table, Stack]|||Java|2| +|380|[380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)|Medium|[Array, Design, Hash Table]|O(1) avg|O(n)|Java|3| +|244|[244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)|Medium|[Array, Design, Hash Table, Two Pointers]|O(n) to build map, O(a + b) to query|O(n)|Java|4| +|245|[245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)|Medium|[Array, Design, Hash Table, Two Pointers]|O(n)|O(1)|Java|5| +|149|[149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%20Points%20on%20a%20Line.java)|Hard|[Array, Geometry, Hash Table, Math]|O(n^2)|O()|Java|6| +|219|[219. Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/219.%20Contains%20Duplicate%20II.java)|Easy|[Array, Hash Table]|O(n)|O(n)|Java|7| +|217|[217. Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/217.%20Contains%20Duplicate.java)|Easy|[Array, Hash Table]|O(n)|O(1)|Java|8| +|1|[1. Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1.%20Two%20Sum.java)|Easy|[Array, Hash Table]|O(n)|O(n)|Java|9| +|[lint]|[[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)|Medium|[Array, Hash Table, Lint]|O(n)|O(n)|Java|10| +|[lint]|[[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)|Easy|[Array, Hash Table, Lint, PreSum, Subarray]|O(n)|O(n)|Java|11| +|N/A|[Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)|Medium|[Array, Hash Table, PreSum]|||Java|12| +|560|[560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)|Medium|[Array, Hash Table, PreSum, Subarray]|O(n)|O(n)|Java|13| +|1146|[1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)|Medium|[Array, Hash Table, TreeMap]|O(1) set, O(logn) get, O(x) snap, x = # of changes|O(n * m), n = array size, m = # of snaps|Java|14| +|N/A|[Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)|Hard|[Array, Hash Table, Union Find]|||Java|15| +|987|[987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)|Medium|[BFS, Binary Tree, DFS, Hash Table, Tree]|||Java|16| +|314|[314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)|Medium|[BFS, DFS, Hash Table, Tree]|O(n)|O(n)|Java|17| +|140|[140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)|Hard|[Backtracking, DFS, DP, Hash Table, Memoization]|O(n!)|O(n!)|Java|18| +|349|[349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)|Easy|[Binary Search, Hash Table, Sort, Two Pointers]|O(m + n)|O(m + n)|Java|19| +|350|[350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)|Easy|[Binary Search, Hash Table, Sort, Two Pointers]|(n)|(n)|Java|20| +|981|[981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)|Medium|[Binary Search, Hash Table, TreeMap]|set O(1), get(logn)|O(n)|Java|21| +|136|[136. Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/136.%20Single%20Number.java)|Easy|[Bit Manipulation, Hash Table]|||Java|22| +|1048|[1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)|Medium|[Bucket Sort, DP, Hash Table, Sort]|O(n)|O(n)|Java|23| +|274|[274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.H-Index.java)|Medium|[Bucket Sort, Hash Table, Sort]|O(n)|O(n)|Java|24| +|694|[694. Number of Distinct Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/694.%20Number%20of%20Distinct%20Islands.java)|Medium|[DFS, Hash Table]|O(n)|O(n)|Java|25| +|N/A|[Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)|Medium|[DFS, Hash Table, Tree]|||Java|26| +|721|[721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)|Medium|[DFS, Hash Table, Union Find]|||Java|27| +|N/A|[Frog Jump.java](https://github.com/awangdev/LintCode/blob/master/Java/Frog%20Jump.java)|Hard|[DP, Hash Table]|||Java|28| +|139|[139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)|Medium|[DP, Hash Table, Sequence DP]|O(n^2)|O(n)|Java|29| +|727|[727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)|Hard|[DP, Hash Table, Sliding Window, String, Two Pointers]|O(n^2)|O(1)|Java|30| +|146|[146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)|Medium|[Design, Doubly Linked List, Hash Table, Linked List]|O(1)|O(1)|Java|31| +|N/A|[Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)|Hard|[Design, Geometry, Hash Table]|||Java|32| +|N/A|[ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)|Medium|[Design, Hash Table]|||Java|33| +|N/A|[LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)|Hard|[Design, Hash Table]|||Java|34| +|N/A|[Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)|Medium|[Design, Hash Table]|||Java|35| +|359|[359. Logger Rate Limiter.java](https://github.com/awangdev/LintCode/blob/master/Java/359.%20Logger%20Rate%20Limiter.java)|Easy|[Design, Hash Table]|O(1)|O(n)|Java|36| +|170|[170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)|Easy|[Design, Hash Table, Memoization]|O(n)|O(n)|Java|37| +|N/A|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words|O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map`|Java|38| +|36|[36. Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/36.%20Valid%20Sudoku.java)|Easy|[Enumeration, Hash Table]|(mn)|(mn)|Java|39| +|246|[246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)|Easy|[Enumeration, Hash Table, Math, Two Pointers]|O(n)|O(1)|Java|40| +|N/A|[Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)|Hard|[Greedy, Hash Table, Heap]|||Java|41| +|767|[767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)|Medium|[Greedy, Hash Table, Heap, Sort, String]|O(m), m = # of unique letters|O(nLogm), n = length|Java|42| +|N/A|[Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)|Hard|[Greedy, Hash Table, Stack]|||Java|43| +|N/A|[Rehashing.java](https://github.com/awangdev/LintCode/blob/master/Java/Rehashing.java)|Medium|[Hash Table]|||Java|44| +|N/A|[4Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/4Sum.java)|Medium|[Hash Table]|||Java|45| +|N/A|[Contiguous Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Contiguous%20Array.java)|Medium|[Hash Table]|||Java|46| +|N/A|[Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)|Medium|[Hash Table]|O(mn)|O(X), X = max wall width|Java|47| +|N/A|[HashWithCustomizedClass(LinkedList).java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithCustomizedClass(LinkedList).java)|Medium|[Hash Table]|||Java|48| +|953|[953. Verifying an Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/953.%20Verifying%20an%20Alien%20Dictionary.java)|Easy|[Hash Table]|O(nm)|O(1)|Java|49| +|299|[299. Bulls and Cows.java](https://github.com/awangdev/LintCode/blob/master/Java/299.%20Bulls%20and%20Cows.java)|Medium|[Hash Table]|O(n)|O(n)|Java|50| +|266|[266. Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/266.%20Palindrome%20Permutation.java)|Easy|[Hash Table]|O(n)|O(n)|Java|51| +|311|[311. Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/311.%20Sparse%20Matrix%20Multiplication.java)|Medium|[Hash Table]|O(mnk), where `m = A.row`, `n = B.col`, `k = A.col = B.row`|O(1) extra|Java|52| +|771|[771. Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/771.%20Jewels%20and%20Stones.java)|Easy|[Hash Table]|O(n)|O(n)|Java|53| +|463|[463. Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/463.%20Island%20Perimeter.java)|Easy|[Hash Table]|O(n)||Java|54| +|205|[205. Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/205.%20Isomorphic%20Strings.java)|Easy|[Hash Table]|O(n)|O(n)|Java|55| +|760|[760. Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/760.%20Find%20Anagram%20Mappings.java)|Easy|[Hash Table]|O(n)|O(n)|Java|56| +|347|[347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|O(n)|O(n)|Java|57| +|692|[692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|O(n)|O(n)|Java|58| +|N/A|[Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)|Medium|[Hash Table, Linked List]|||Java|59| +|138|[138. Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/138.%20Copy%20List%20with%20Random%20Pointer.java)|Medium|[Hash Table, Linked List]|O(n)|O(n)|Java|60| +|340|[340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)|Hard|[Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers]|O(n)|O(k)|Java|61| +|[tool]|[[tool]. Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Hash%20Function.java)|Easy|[Hash Table, Lint]|O(1) get|O(n) store map|Java|62| +|[lint]|[[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)|Easy|[Hash Table, Lint, String]|||Java|63| +|[lint]|[[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)|Easy|[Hash Table, Lint, Tree]|||Java|64| +|N/A|[Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)|Medium|[Hash Table, Math]|O(n)|O(n)|Java|65| +|N/A|[Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)|Medium|[Hash Table, Math]|||Java|66| +|N/A|[Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)|Medium|[Hash Table, Math]|||Java|67| +|204|[204. Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/204.%20Count%20Primes.java)|Easy|[Hash Table, Math]|||Java|68| +|202|[202. Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/202.%20Happy%20Number.java)|Easy|[Hash Table, Math]|O(m), m iterations|O(m), m number in set|Java|69| +|739|[739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)|Medium|[Hash Table, Monotonous Stack, Stack]|O(n)|O(n)|Java|70| +|N/A|[Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)|Medium|[Hash Table, PreSum, Subarray]|O(n)|O(n)|Java|71| +|76|[76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)|Hard|[Hash Table, Sliding Window, String, Two Pointers]|O(n)|O(1)|Java|72| +|159|[159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)|Medium|[Hash Table, Sliding Window, String, Two Pointers]|O(n)|O(1)|Java|73| +|438|[438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)|Medium|[Hash Table, Sliding Window, Two Pointers]|O(n)|O(1)|Java|74| +|632|[632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)|Hard|[Hash Table, Sliding Window, Two Pointers]|O(nlogn), n = total elements|O(n) to store sorted list|Java|75| +|242|[242. Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/242.%20Valid%20Anagram.java)|Easy|[Hash Table, Sort]|O(n)|O(1), unique chars|Java|76| +|496|[496. Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/496.%20Next%20Greater%20Element%20I.java)|Easy|[Hash Table, Stack]|O(n)|O(n)|Java|77| +|94|[94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)|Easy|[Hash Table, Stack, Tree]|O(n)|O(logn)|Java|78| +|N/A|[Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)|Medium|[Hash Table, String]|||Java|79| +|387|[387. First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/387.%20First%20Unique%20Character%20in%20a%20String.java)|Easy|[Hash Table, String]|O(n)|O(256) = O(1)|Java|80| +|49|[49. Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/49.%20Group%20Anagrams.java)|Medium|[Hash Table, String]|O(nk)|O(nk)|Java|81| +|N/A|[Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)|Hard|[Hash Table, String, Trie]|||Java|82| +|N/A|[Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)|Medium|[Hash Table, String, Two Pointers]|||Java|83| +|720|[720. Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/720.%20Longest%20Word%20in%20Dictionary.java)|Easy|[Hash Table, Trie]|O(nlogn)|O(n)|Java|84| +|1213|[1213. Intersection of Three Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/1213.%20Intersection%20of%20Three%20Sorted%20Arrays.java)|Easy|[Hash Table, Two Pointers]|O(m + n + h) two pointers approach|O(1)|Java|85| + + + + + + +## String (78) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|[Array, BFS, Backtracking, DFS, Hash Table, String]|||Java|0| +|[lint]|[[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)|Easy|[Array, Lint, String]|||Java|1| +|1170|[1170. Compare Strings by Frequency of the Smallest Character.java](https://github.com/awangdev/LintCode/blob/master/Java/1170.%20Compare%20Strings%20by%20Frequency%20of%20the%20Smallest%20Character.java)|Easy|[Array, String]|O(m + n)|O(m + n)|Java|2| +|N/A|[Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)|Hard|[Backtracking, DFS, Divide and Conquer, String]|O(4^n)|O(4^n)|Java|3| +|22|[22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%20Parentheses.java)|Medium|[Backtracking, DFS, Sequence DFS, String]|O(2^n)|O(2^n)|Java|4| +|N/A|[Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)|Medium|[Backtracking, DFS, String]|||Java|5| +|N/A|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|||Java|6| +|10|[10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|||Java|7| +|N/A|[Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)|Medium|[Backtracking, String]|||Java|8| +|686|[686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)|Easy|[Basic Implementation, Edge Case, String]|||Java|9| +|N/A|[Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)|Medium|[Basic Implementation, Enumeration, String]|||Java|10| +|415|[415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)|Easy|[Basic Implementation, Math, String]|O(n)|O(n)|Java|11| +|12|[12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)|Medium|[Basic Implementation, Math, String]|O(n)|O(n)|Java|12| +|N/A|[Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)|Easy|[Basic Implementation, String]|||Java|13| +|788|[788. Rotated Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/788.%20Rotated%20Digits.java)|Easy|[Basic Implementation, String]|O(n)|O(n)|Java|14| +|408|[408. Valid Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/408.%20Valid%20Word%20Abbreviation.java)|Easy|[Basic Implementation, String]|||Java|15| +|1108|[1108. Defanging an IP Address.java](https://github.com/awangdev/LintCode/blob/master/Java/1108.%20Defanging%20an%20IP%20Address.java)|Easy|[Basic Implementation, String]|||Java|16| +|383|[383. Ransom Note.java](https://github.com/awangdev/LintCode/blob/master/Java/383.%20Ransom%20Note.java)|Easy|[Basic Implementation, String]|||Java|17| +|824|[824. Goat Latin.java](https://github.com/awangdev/LintCode/blob/master/Java/824.%20Goat%20Latin.java)|Easy|[Basic Implementation, String]|O(n)|O(1)|Java|18| +|443|[443. String Compression.java](https://github.com/awangdev/LintCode/blob/master/Java/443.%20String%20Compression.java)|Easy|[Basic Implementation, String]|||Java|19| +|893|[893. Groups of Special-Equivalent Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/893.%20Groups%20of%20Special-Equivalent%20Strings.java)|Easy|[Basic Implementation, String]|||Java|20| +|N/A|[Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)|Hard|[Bit Manipulation, String]|||Java|21| +|N/A|[Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)|Hard|[Coordinate DP, Stack, String]|||Java|22| +|1216|[1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)|Hard|[DFS, DP, Memoization, String]|O(n^2)|O(n^2)|Java|23| +|1106|[1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)|Hard|[DFS, Stack, String]|||Java|24| +|N/A|[Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)|Medium|[DP, Double Sequence DP, Sequence DP, String]|||Java|25| +|72|[72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)|Hard|[DP, Double Sequence DP, Sequence DP, String]|O(MN)||Java|26| +|727|[727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)|Hard|[DP, Hash Table, Sliding Window, String, Two Pointers]|O(n^2)|O(1)|Java|27| +|N/A|[Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)|Hard|[DP, Interval DP, String]|||Java|28| +|91|[91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)|Medium|[DP, Partition DP, String]|O(n)|O(n)|Java|29| +|N/A|[Palindromic Substrings.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindromic%20Substrings.java)|Medium|[DP, String]|||Java|30| +|N/A|[Distinct Subsequences.java](https://github.com/awangdev/LintCode/blob/master/Java/Distinct%20Subsequences.java)|Hard|[DP, String]|||Java|31| +|N/A|[Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)|Hard|[DP, String]|||Java|32| +|5|[5. Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/5.%20Longest%20Palindromic%20Substring.java)|Medium|[DP, String]|O(n^2)|O(n^2)|Java|33| +|273|[273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%20to%20English%20Words.java)|Hard|[Enumeration, Math, String]|O(n)|O(1)|Java|34| +|65|[65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)|Hard|[Enumeration, Math, String]|O(n)|O(1)|Java|35| +|158|[158. Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/158.%20Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)|Hard|[Enumeration, String]|O(n)|O(n)|Java|36| +|68|[68. Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/68.%20Text%20Justification.java)|Hard|[Enumeration, String]|O(n) go over words|O(maxLength) buffer list|Java|37| +|157|[157. Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/157.%20Read%20N%20Characters%20Given%20Read4.java)|Easy|[Enumeration, String]|||Java|38| +|767|[767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)|Medium|[Greedy, Hash Table, Heap, Sort, String]|O(m), m = # of unique letters|O(nLogm), n = length|Java|39| +|340|[340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)|Hard|[Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers]|O(n)|O(k)|Java|40| +|[lint]|[[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)|Easy|[Hash Table, Lint, String]|||Java|41| +|76|[76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)|Hard|[Hash Table, Sliding Window, String, Two Pointers]|O(n)|O(1)|Java|42| +|159|[159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)|Medium|[Hash Table, Sliding Window, String, Two Pointers]|O(n)|O(1)|Java|43| +|N/A|[Group Shifted Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Group%20Shifted%20Strings.java)|Medium|[Hash Table, String]|||Java|44| +|387|[387. First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/387.%20First%20Unique%20Character%20in%20a%20String.java)|Easy|[Hash Table, String]|O(n)|O(256) = O(1)|Java|45| +|49|[49. Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/49.%20Group%20Anagrams.java)|Medium|[Hash Table, String]|O(nk)|O(nk)|Java|46| +|N/A|[Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)|Hard|[Hash Table, String, Trie]|||Java|47| +|N/A|[Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)|Medium|[Hash Table, String, Two Pointers]|||Java|48| +|N/A|[Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)|Hard|[KMP, String]|||Java|49| +|[lint]|[[lint]. Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Compare%20Strings.java)|Easy|[Lint, String]|||Java|50| +|N/A|[Orderly Queue.java](https://github.com/awangdev/LintCode/blob/master/Java/Orderly%20Queue.java)|Hard|[Math, String]|||Java|51| +|43|[43. Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/43.%20Multiply%20Strings.java)|Medium|[Math, String]|O(mn)|O(mn)|Java|52| +|8|[8. String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/8.%20String%20to%20Integer%20(atoi).java)|Medium|[Math, String]|O(n)|O(n)|Java|53| +|13|[13. Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/13.%20Roman%20to%20Integer.java)|Easy|[Math, String]|O(n)|O(1)|Java|54| +|67|[67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%20Binary.java)|Easy|[Math, String, Two Pointers]|||Java|55| +|N/A|[Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)|Medium|[Partition, Sort, String, Two Pointers]|||Java|56| +|1249|[1249. Minimum Remove to Make Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1249.%20Minimum%20Remove%20to%20Make%20Valid%20Parentheses.java)|Medium|[Stack, String]|O(n)|O(n)|Java|57| +|20|[20. Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/20.%20Valid%20Parentheses.java)|Easy|[Stack, String]|O(n)|O(n)|Java|58| +|71|[71. Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/71.%20Simplify%20Path.java)|Medium|[Stack, String]|O(n)|O(n)|Java|59| +|N/A|[Compare Version Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Compare%20Version%20Numbers.java)|Medium|[String]|||Java|60| +|N/A|[Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)|Medium|[String]|||Java|61| +|N/A|[Reverse Words in a String II.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Words%20in%20a%20String%20II.java)|Medium|[String]|||Java|62| +|N/A|[One Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/One%20Edit%20Distance.java)|Medium|[String]|||Java|63| +|N/A|[Encode and Decode Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20Strings.java)|Medium|[String]|||Java|64| +|N/A|[[HackerRank]. Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/[HackerRank].%20Change%20to%20Anagram.java)|Easy|[String]|||Java|65| +|293|[293. Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/293.%20Flip%20Game.java)|Easy|[String]|||Java|66| +|58|[58. Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/58.%20Length%20of%20Last%20Word.java)|Easy|[String]|||Java|67| +|151|[151. Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/151.%20Reverse%20Words%20in%20a%20String.java)|Medium|[String]|O(n)||Java|68| +|557|[557. Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/557.%20Reverse%20Words%20in%20a%20String%20III.java)|Easy|[String]|||Java|69| +|680|[680. Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/680.%20Valid%20Palindrome%20II.java)|Easy|[String]|||Java|70| +|14|[14. Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/14.%20Longest%20Common%20Prefix.java)|Easy|[String]|||Java|71| +|796|[796. Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/796.%20Rotate%20String.java)|Easy|[String]|||Java|72| +|1041|[1041. Robot Bounded In Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/1041.%20Robot%20Bounded%20In%20Circle.java)|Easy|[String]|||Java|73| +|N/A|[Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)|Easy|[String, Two Pointers]|||Java|74| +|345|[345. Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/345.%20Reverse%20Vowels%20of%20a%20String.java)|Easy|[String, Two Pointers]|||Java|75| +|28|[28. Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/28.%20Implement%20strStr().java)|Easy|[String, Two Pointers]|||Java|76| +|125|[125. Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/125.%20Valid%20Palindrome.java)|Easy|[String, Two Pointers]|||Java|77| + + + + + + +## Tree (69) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|105|[105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|[Array, DFS, Divide and Conquer, Hash Table, Tree]|O(n)|O(n)|Java|0| +|N/A|[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|[Array, DFS, Divide and Conquer, Tree]|||Java|1| +|987|[987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)|Medium|[BFS, Binary Tree, DFS, Hash Table, Tree]|||Java|2| +|297|[297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|O(n)|O(n)|Java|3| +|N/A|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|[BFS, DFS, Graph, Tree, Union Find]|||Java|4| +|314|[314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)|Medium|[BFS, DFS, Hash Table, Tree]|O(n)|O(n)|Java|5| +|144|[144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)|Medium|[BFS, DFS, Stack, Tree]|O(n)|O(n)|Java|6| +|N/A|[Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)|Easy|[BFS, DFS, Tree]|||Java|7| +|102|[102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|8| +|111|[111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%20Depth%20of%20Binary%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(n)|Java|9| +|199|[199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%20Tree%20Right%20Side%20View.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|10| +|515|[515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|11| +|100|[100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(logn)|Java|12| +|1123|[1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|13| +|101|[101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(n)|Java|14| +|103|[103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)|Medium|[BFS, Stack, Tree]|O(n)|O(n)|Java|15| +|N/A|[Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)|Medium|[BFS, Tree]|||Java|16| +|N/A|[Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)|Easy|[BFS, Tree]|||Java|17| +|429|[429. N-ary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/429.%20N-ary%20Tree%20Level%20Order%20Traversal.java)|Medium|[BFS, Tree]|O(n)|O(n)|Java|18| +|671|[671. Second Minimum Node In a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/671.%20Second%20Minimum%20Node%20In%20a%20Binary%20Tree.java)|Easy|[BFS, Tree]|O(n)|O(n) leaf nodes|Java|19| +|270|[270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)|Easy|[BST, Binary Search, Tree]|O(logn)|O(1)|Java|20| +|426|[426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|[BST, DFS, Divide and Conquer, Linked List, Tree]|O(n)|O(1)|Java|21| +|98|[98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)|Medium|[BST, DFS, Divide and Conquer, Tree]|O(n)|O(logn)|Java|22| +|N/A|[Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)|Medium|[BST, DFS, Stack, Tree]|||Java|23| +|N/A|[Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)|Hard|[BST, DFS, Tree]|||Java|24| +|235|[235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)|Easy|[BST, DFS, Tree]|O(logn)|O(logn)|Java|25| +|N/A|[Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)|Medium|[BST, DP, Divide and Conquer, Tree]|||Java|26| +|N/A|[Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)|Medium|[BST, DP, Tree]|||Java|27| +|173|[173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%20Search%20Tree%20Iterator.java)|Medium|[BST, Design, Stack, Tree]|O(1) average|O(h)|Java|28| +|938|[938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)|Easy|[BST, Recursion, Tree]|||Java|29| +|N/A|[Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)|Medium|[BST, Tree]|||Java|30| +|N/A|[Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)|Easy|[BST, Tree]|||Java|31| +|N/A|[Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)|Easy|[Backtracking, DFS, Tree]|||Java|32| +|222|[222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)|Medium|[Binary Search, DFS, Tree]|O(n)|O(h)|Java|33| +|N/A|[House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)|Medium|[DFS, DP, Status DP, Tree]|||Java|34| +|124|[124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)|Hard|[DFS, DP, Tree, Tree DP]|O(n)|O(logn)|Java|35| +|N/A|[Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)|Medium|[DFS, Divide and Conquer, Double Recursive, Tree]|||Java|36| +|N/A|[Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)|Easy|[DFS, Divide and Conquer, Tree]|||Java|37| +|N/A|[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|[DFS, Divide and Conquer, Tree]|||Java|38| +|N/A|[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|[DFS, Divide and Conquer, Tree]|||Java|39| +|N/A|[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|[DFS, Divide and Conquer, Tree]|O(n)|O(n)|Java|40| +|N/A|[Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)|Medium|[DFS, Divide and Conquer, Tree]|||Java|41| +|1110|[1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)|Medium|[DFS, Divide and Conquer, Tree]|O(n)|O(logn)|Java|42| +|N/A|[Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)|Easy|[DFS, Double Recursive, Tree]|||Java|43| +|N/A|[Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)|Hard|[DFS, Graph, Tree, Union Find]|||Java|44| +|N/A|[Path Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20IV.java)|Medium|[DFS, Hash Table, Tree]|||Java|45| +|N/A|[Binary Tree Maximum Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Maximum%20Path%20Sum%20II.java)|Medium|[DFS, Tree]|||Java|46| +|N/A|[Subtree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree.java)|Easy|[DFS, Tree]|||Java|47| +|N/A|[Tweaked Identical Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Tweaked%20Identical%20Binary%20Tree.java)|Easy|[DFS, Tree]|||Java|48| +|N/A|[Merge Two Binary Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Merge%20Two%20Binary%20Trees.java)|Easy|[DFS, Tree]|||Java|49| +|N/A|[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|[DFS, Tree]|O(n)|O(1)|Java|50| +|236|[236. Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/236.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Tree.java)|Medium|[DFS, Tree]|O(n)|O(n)|Java|51| +|1008|[1008. Construct Binary Search Tree from Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/1008.%20Construct%20Binary%20Search%20Tree%20from%20Preorder%20Traversal.java)|Medium|[DFS, Tree]|O(n)|O(n)|Java|52| +|104|[104. Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/104.%20Maximum%20Depth%20of%20Binary%20Tree.java)|Easy|[DFS, Tree]|||Java|53| +|110|[110. Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/110.%20Balanced%20Binary%20Tree.java)|Easy|[DFS, Tree]|||Java|54| +|112|[112. Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/112.%20Path%20Sum.java)|Easy|[DFS, Tree]|||Java|55| +|1026|[1026. Maximum Difference Between Node and Ancestor.java](https://github.com/awangdev/LintCode/blob/master/Java/1026.%20Maximum%20Difference%20Between%20Node%20and%20Ancestor.java)|Medium|[DFS, Tree]|O(n)|O(logn)|Java|56| +|366|[366. Find Leaves of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/366.%20Find%20Leaves%20of%20Binary%20Tree.java)|Medium|[DFS, Tree]|O(n)|O(h)|Java|57| +|156|[156. Binary Tree Upside Down.java](https://github.com/awangdev/LintCode/blob/master/Java/156.%20Binary%20Tree%20Upside%20Down.java)|Medium|[DFS, Tree]|O(n)|O(h)|Java|58| +|[lint]|[[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)|Easy|[Hash Table, Lint, Tree]|||Java|59| +|94|[94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)|Easy|[Hash Table, Stack, Tree]|O(n)|O(logn)|Java|60| +|N/A|[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|[Linked List, Stack, Tree]|O(n)|O(n)|Java|61| +|N/A|[Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)|Medium|[Stack, Tree]|||Java|62| +|272|[272. Closest Binary Search Tree Value II.java](https://github.com/awangdev/LintCode/blob/master/Java/272.%20Closest%20Binary%20Search%20Tree%20Value%20II.java)|Hard|[Stack, Tree]|O(n)|O(n)|Java|63| +|145|[145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)|Medium|[Stack, Tree, Two Stacks]|O(n)|O(n)|Java|64| +|N/A|[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|[Tree]|||Java|65| +|543|[543. Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/543.%20Diameter%20of%20Binary%20Tree.java)|Easy|[Tree]|O(n) when non-balanced|O(n) when non-balanced|Java|66| +|427|[427. Construct Quad Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/427.%20Construct%20Quad%20Tree.java)|Medium|[Tree]|O(n^2)|O(n^2)|Java|67| +|449|[449. Serialize and Deserialize BST.java](https://github.com/awangdev/LintCode/blob/master/Java/449.%20Serialize%20and%20Deserialize%20BST.java)|Medium|[Tree]|O(n)|O(n)|Java|68| + + + + + + +## Two Pointers (57) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|849|[849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)|Easy|[Array, Basic Implementation, Two Pointers]|O(n)|O(1)|Java|0| +|287|[287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)|Medium|[Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|1| +|[lint]|[[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)|Medium|[Array, Binary Search, Lint, Two Pointers]|||Java|2| +|N/A|[Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)|Medium|[Array, Binary Search, Subarray, Two Pointers]|O(n)|O(1)|Java|3| +|N/A|[Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)|Hard|[Array, Binary Search, Two Pointers]|||Java|4| +|N/A|[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|[Array, Binary Search, Two Pointers]|||Java|5| +|244|[244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)|Medium|[Array, Design, Hash Table, Two Pointers]|O(n) to build map, O(a + b) to query|O(n)|Java|6| +|245|[245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)|Medium|[Array, Design, Hash Table, Two Pointers]|O(n)|O(1)|Java|7| +|N/A|[[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)|Easy|[Array, Lint, Quick Select, Quick Sort, Two Pointers]|O(n)|O(logN)|Java|8| +|[lint]|[[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)|Medium|[Array, Lint, Two Pointers]|||Java|9| +|N/A|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|[Array, Partition, Quick Sort, Sort, Two Pointers]|||Java|10| +|N/A|[Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)|Medium|[Array, Quick Sort, Sort, Two Pointers]|||Java|11| +|N/A|[The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)|Medium|[Array, Sort, Two Pointers]|||Java|12| +|259|[259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)|Medium|[Array, Sort, Two Pointers]|||Java|13| +|15|[15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)|Medium|[Array, Sort, Two Pointers]|O(n^2)||Java|14| +|42|[42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)|Hard|[Array, Stack, Two Pointers]|O(n)|O(1)|Java|15| +|N/A|[Container With Most Water.java](https://github.com/awangdev/LintCode/blob/master/Java/Container%20With%20Most%20Water.java)|Medium|[Array, Two Pointers]|||Java|16| +|N/A|[Partition Array by Odd and Even.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array%20by%20Odd%20and%20Even.java)|Easy|[Array, Two Pointers]|||Java|17| +|80|[80.Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/80.Remove%20Duplicates%20from%20Sorted%20Array%20II.java)|Medium|[Array, Two Pointers]|||Java|18| +|26|[26.Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/26.Remove%20Duplicates%20from%20Sorted%20Array.java)|Easy|[Array, Two Pointers]|||Java|19| +|977|[977. Squares of a Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/977.%20Squares%20of%20a%20Sorted%20Array.java)|Easy|[Array, Two Pointers]|O(n)|O(n)|Java|20| +|88|[88. Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Merge%20Sorted%20Array.java)|Easy|[Array, Two Pointers]|O(n)|O(1)|Java|21| +|243|[243. Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/243.%20Shortest%20Word%20Distance.java)|Easy|[Array, Two Pointers]|O(n)|O(1)|Java|22| +|283|[283. Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/283.%20Move%20Zeroes.java)|Easy|[Array, Two Pointers]|O(n)|O(1)|Java|23| +|611|[611. Valid Triangle Number.java](https://github.com/awangdev/LintCode/blob/master/Java/611.%20Valid%20Triangle%20Number.java)|Medium|[Array, Two Pointers]|O(n^2)|O(logn), sorting space|Java|24| +|349|[349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)|Easy|[Binary Search, Hash Table, Sort, Two Pointers]|O(m + n)|O(m + n)|Java|25| +|350|[350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)|Easy|[Binary Search, Hash Table, Sort, Two Pointers]|(n)|(n)|Java|26| +|142|[142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)|Medium|[Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|27| +|141|[141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)|Easy|[Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|28| +|727|[727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)|Hard|[DP, Hash Table, Sliding Window, String, Two Pointers]|O(n^2)|O(1)|Java|29| +|246|[246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)|Easy|[Enumeration, Hash Table, Math, Two Pointers]|O(n)|O(1)|Java|30| +|340|[340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)|Hard|[Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers]|O(n)|O(k)|Java|31| +|76|[76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)|Hard|[Hash Table, Sliding Window, String, Two Pointers]|O(n)|O(1)|Java|32| +|159|[159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)|Medium|[Hash Table, Sliding Window, String, Two Pointers]|O(n)|O(1)|Java|33| +|438|[438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)|Medium|[Hash Table, Sliding Window, Two Pointers]|O(n)|O(1)|Java|34| +|632|[632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)|Hard|[Hash Table, Sliding Window, Two Pointers]|O(nlogn), n = total elements|O(n) to store sorted list|Java|35| +|N/A|[Longest Substring Without Repeating Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Substring%20Without%20Repeating%20Characters.java)|Medium|[Hash Table, String, Two Pointers]|||Java|36| +|1213|[1213. Intersection of Three Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/1213.%20Intersection%20of%20Three%20Sorted%20Arrays.java)|Easy|[Hash Table, Two Pointers]|O(m + n + h) two pointers approach|O(1)|Java|37| +|N/A|[Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)|Medium|[Linked List, Math, Two Pointers]|||Java|38| +|N/A|[Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)|Medium|[Linked List, Two Pointers]|||Java|39| +|N/A|[Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)|Medium|[Linked List, Two Pointers]|||Java|40| +|19|[19. Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/19.%20Remove%20Nth%20Node%20From%20End%20of%20List.java)|Medium|[Linked List, Two Pointers]|O(n)|O(1)|Java|41| +|234|[234. Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/234.%20Palindrome%20Linked%20List.java)|Easy|[Linked List, Two Pointers]|O(n)|O(1)|Java|42| +|67|[67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%20Binary.java)|Easy|[Math, String, Two Pointers]|||Java|43| +|360|[360. Sort Transformed Array.java](https://github.com/awangdev/LintCode/blob/master/Java/360.%20Sort%20Transformed%20Array.java)|Medium|[Math, Two Pointers]|O(n)|O(n) store result|Java|44| +|N/A|[Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)|Medium|[Partition, Quick Sort, Sort, Two Pointers]|||Java|45| +|N/A|[Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)|Medium|[Partition, Sort, String, Two Pointers]|||Java|46| +|567|[567. Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/567.%20Permutation%20in%20String.java)|Medium|[Sliding Window, Two Pointers]|O(m + n)|O(1)|Java|47| +|1004|[1004. Max Consecutive Ones III.java](https://github.com/awangdev/LintCode/blob/master/Java/1004.%20Max%20Consecutive%20Ones%20III.java)|Medium|[Sliding Window, Two Pointers]|O(n)|O(1)|Java|48| +|N/A|[Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)|Easy|[Stack, Two Pointers]|||Java|49| +|844|[844. Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/844.%20Backspace%20String%20Compare.java)|Easy|[Stack, Two Pointers]|O(n)|O(1)|Java|50| +|N/A|[Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)|Easy|[String, Two Pointers]|||Java|51| +|345|[345. Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/345.%20Reverse%20Vowels%20of%20a%20String.java)|Easy|[String, Two Pointers]|||Java|52| +|28|[28. Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/28.%20Implement%20strStr().java)|Easy|[String, Two Pointers]|||Java|53| +|125|[125. Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/125.%20Valid%20Palindrome.java)|Easy|[String, Two Pointers]|||Java|54| +|N/A|[Interleaving Positive and Negative Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20Positive%20and%20Negative%20Numbers.java)|Medium|[Two Pointers]|||Java|55| +|986|[986. Interval List Intersections.java](https://github.com/awangdev/LintCode/blob/master/Java/986.%20Interval%20List%20Intersections.java)|Medium|[Two Pointers]|O(n)|O(1)|Java|56| + + + + + + +## BFS (54) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|78|[78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)|Medium|[Array, BFS, Backtracking, Bit Manipulation, DFS]|O(2^n)|O(2^n)|Java|0| +|N/A|[Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)|Medium|[Array, BFS, Backtracking, DFS]|O(2^n)||Java|1| +|N/A|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|[Array, BFS, Backtracking, DFS, Hash Table, String]|||Java|2| +|N/A|[Word Ladder.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder.java)|Medium|[BFS]|||Java|3| +|N/A|[Shortest Distance from All Buildings.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Distance%20from%20All%20Buildings.java)|Hard|[BFS]|||Java|4| +|N/A|[Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)|Hard|[BFS]|||Java|5| +|1091|[1091. Shortest Path in Binary Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/1091.%20Shortest%20Path%20in%20Binary%20Matrix.java)|Medium|[BFS]|O(n^2)||Java|6| +|1197|[1197. Minimum Knight Moves.java](https://github.com/awangdev/LintCode/blob/master/Java/1197.%20Minimum%20Knight%20Moves.java)|Medium|[BFS]|O(8^n)|O(8^n)|Java|7| +|254|[254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)|Medium|[BFS, Backtracking, DFS]|O(x), x is the # of results|O(y), y is all ongoing candidates in queue|Java|8| +|269|[269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)|Hard|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n), n = # of graph edges|O(n)|Java|9| +|207|[207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)|Medium|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|10| +|46|[46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)|Medium|[BFS, Backtracking, DFS, Permutation]|O(n!)|O(n!)|Java|11| +|987|[987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)|Medium|[BFS, Binary Tree, DFS, Hash Table, Tree]|||Java|12| +|N/A|[Walls and Gates.java](https://github.com/awangdev/LintCode/blob/master/Java/Walls%20and%20Gates.java)|Medium|[BFS, DFS]|||Java|13| +|N/A|[The Maze.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze.java)|Medium|[BFS, DFS]|||Java|14| +|N/A|[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|[BFS, DFS]|||Java|15| +|301|[301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)|Hard|[BFS, DFS, DP]|||Java|16| +|297|[297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|O(n)|O(n)|Java|17| +|785|[785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)|Medium|[BFS, DFS, Garph]|O(n)|O(n)|Java|18| +|1161|[1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)|Medium|[BFS, DFS, Graph]|O(n) visit all nodes|O(n)|Java|19| +|133|[133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%20Graph.java)|Medium|[BFS, DFS, Graph]|O(n)|O(n)|Java|20| +|743|[743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)|Medium|[BFS, DFS, Graph, Heap, PQ]|O(nlogn)|O(n)|Java|21| +|1203|[1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)|Hard|[BFS, DFS, Graph, Topological Sort]|O(V + E) to traverse the graph, #nodes + #edges|O(V + E)|Java|22| +|210|[210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)|Medium|[BFS, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|23| +|N/A|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|[BFS, DFS, Graph, Tree, Union Find]|||Java|24| +|N/A|[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|[BFS, DFS, Graph, Union Find]|||Java|25| +|261|[261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)|Medium|[BFS, DFS, Graph, Union Find]|||Java|26| +|399|[399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%20Division.java)|Medium|[BFS, DFS, Graph, Union Find]|||Java|27| +|314|[314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)|Medium|[BFS, DFS, Hash Table, Tree]|O(n)|O(n)|Java|28| +|[tool]|[[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)|Medium|[BFS, DFS, Lint, Topological Sort]|O(V + E)|O(V + E)|Java|29| +|N/A|[Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)|Medium|[BFS, DFS, Matrix DFS, Union Find]|||Java|30| +|200|[200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)|Medium|[BFS, DFS, Matrix DFS, Union Find]|O(n)|O(n)|Java|31| +|339|[339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)|Easy|[BFS, DFS, NestedInteger]|O(n)|O(h), h = levels|Java|32| +|N/A|[The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)|Medium|[BFS, DFS, PriorityQueue]|||Java|33| +|N/A|[The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)|Hard|[BFS, DFS, PriorityQueue]|||Java|34| +|144|[144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)|Medium|[BFS, DFS, Stack, Tree]|O(n)|O(n)|Java|35| +|N/A|[Invert Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Invert%20Binary%20Tree.java)|Easy|[BFS, DFS, Tree]|||Java|36| +|102|[102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|37| +|111|[111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%20Depth%20of%20Binary%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(n)|Java|38| +|199|[199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%20Tree%20Right%20Side%20View.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|39| +|515|[515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|40| +|100|[100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(logn)|Java|41| +|1123|[1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)|Medium|[BFS, DFS, Tree]|O(n)|O(n)|Java|42| +|101|[101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)|Easy|[BFS, DFS, Tree]|O(n)|O(n)|Java|43| +|N/A|[Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)|Medium|[BFS, DP, Math, Partition DP]|||Java|44| +|N/A|[Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)|Medium|[BFS, Graph]|||Java|45| +|N/A|[Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)|Hard|[BFS, Graph]|||Java|46| +|1306|[1306. Jump Game III.java](https://github.com/awangdev/LintCode/blob/master/Java/1306.%20Jump%20Game%20III.java)|Medium|[BFS, Graph]|O(n)|O(n)|Java|47| +|N/A|[Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)|Hard|[BFS, Heap, MinHeap, PriorityQueue]|||Java|48| +|103|[103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)|Medium|[BFS, Stack, Tree]|O(n)|O(n)|Java|49| +|N/A|[Binary Tree Level Order Traversal II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Level%20Order%20Traversal%20II.java)|Medium|[BFS, Tree]|||Java|50| +|N/A|[Complete Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Complete%20Binary%20Tree.java)|Easy|[BFS, Tree]|||Java|51| +|429|[429. N-ary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/429.%20N-ary%20Tree%20Level%20Order%20Traversal.java)|Medium|[BFS, Tree]|O(n)|O(n)|Java|52| +|671|[671. Second Minimum Node In a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/671.%20Second%20Minimum%20Node%20In%20a%20Binary%20Tree.java)|Easy|[BFS, Tree]|O(n)|O(n) leaf nodes|Java|53| + + + + + + +## Math (45) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)|Easy|[Array, Bit Manipulation, Math]|||Java|0| +|149|[149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%20Points%20on%20a%20Line.java)|Hard|[Array, Geometry, Hash Table, Math]|O(n^2)|O()|Java|1| +|N/A|[Plus One.java](https://github.com/awangdev/LintCode/blob/master/Java/Plus%20One.java)|Easy|[Array, Math]|||Java|2| +|N/A|[Friends Of Appropriate Ages.java](https://github.com/awangdev/LintCode/blob/master/Java/Friends%20Of%20Appropriate%20Ages.java)|Medium|[Array, Math]|||Java|3| +|N/A|[Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)|Medium|[BFS, DP, Math, Partition DP]|||Java|4| +|N/A|[Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)|Medium|[Backtracking, Math]|||Java|5| +|415|[415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)|Easy|[Basic Implementation, Math, String]|O(n)|O(n)|Java|6| +|12|[12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)|Medium|[Basic Implementation, Math, String]|O(n)|O(n)|Java|7| +|N/A|[Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)|Medium|[Binary Search, Math]|||Java|8| +|367|[367. Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/367.%20Valid%20Perfect%20Square.java)|Easy|[Binary Search, Math]|O(logN)|O(1)|Java|9| +|69|[69. Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/69.%20Sqrt(x).java)|Easy|[Binary Search, Math]|||Java|10| +|N/A|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|||Java|11| +|N/A|[Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)|Easy|[Bit Manipulation, Math]|||Java|12| +|319|[319. Bulb Switcher.java](https://github.com/awangdev/LintCode/blob/master/Java/319.%20Bulb%20Switcher.java)|Medium|[Brainteaser, Math]|O(1)|O(1)|Java|13| +|523|[523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)|Medium|[Coordinate DP, DP, Math, PreSum, Subarray]|O(n)|O(k)|Java|14| +|N/A|[Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)|Medium|[DFS, Enumeration, Math, Sequence DFS]|||Java|15| +|N/A|[Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)|Hard|[DFS, Greedy, Math]|||Java|16| +|N/A|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|[DP, Enumeration, Heap, Math, PriorityQueue]|O(n)|O(n)|Java|17| +|N/A|[Number Of Corner Rectangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20Of%20Corner%20Rectangles.java)|Medium|[DP, Math]|||Java|18| +|509|[509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.java)|Easy|[DP, Math, Memoization]|||Java|19| +|246|[246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)|Easy|[Enumeration, Hash Table, Math, Two Pointers]|O(n)|O(1)|Java|20| +|273|[273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%20to%20English%20Words.java)|Hard|[Enumeration, Math, String]|O(n)|O(1)|Java|21| +|65|[65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)|Hard|[Enumeration, Math, String]|O(n)|O(1)|Java|22| +|N/A|[Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)|Medium|[Hash Table, Math]|O(n)|O(n)|Java|23| +|N/A|[Encode and Decode TinyURL.java](https://github.com/awangdev/LintCode/blob/master/Java/Encode%20and%20Decode%20TinyURL.java)|Medium|[Hash Table, Math]|||Java|24| +|N/A|[Fraction to Recurring Decimal.java](https://github.com/awangdev/LintCode/blob/master/Java/Fraction%20to%20Recurring%20Decimal.java)|Medium|[Hash Table, Math]|||Java|25| +|204|[204. Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/204.%20Count%20Primes.java)|Easy|[Hash Table, Math]|||Java|26| +|202|[202. Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/202.%20Happy%20Number.java)|Easy|[Hash Table, Math]|O(m), m iterations|O(m), m number in set|Java|27| +|2|[2. Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/2.%20Add%20Two%20Numbers.java)|Medium|[Linked List, Math]|O(max(m,n))|O(max(m,n))|Java|28| +|N/A|[Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)|Medium|[Linked List, Math, Two Pointers]|||Java|29| +|N/A|[Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)|Easy|[Math]|||Java|30| +|N/A|[Ugly Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number.java)|Medium|[Math]|||Java|31| +|N/A|[Number of Digit One.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Digit%20One.java)|Hard|[Math]|||Java|32| +|N/A|[Power of Three.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Three.java)|Easy|[Math]|||Java|33| +|N/A|[Add Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Digits.java)|Easy|[Math]|||Java|34| +|N/A|[Excel Sheet Column Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Excel%20Sheet%20Column%20Number.java)|Easy|[Math]|||Java|35| +|7|[7. Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/7.%20Reverse%20Integer.java)|Easy|[Math]|O(n)|O(1)|Java|36| +|168|[168. Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/168.%20Excel%20Sheet%20Column%20Title.java)|Easy|[Math]|O(n)|O(1)|Java|37| +|9|[9. Palindrome Number.java](https://github.com/awangdev/LintCode/blob/master/Java/9.%20Palindrome%20Number.java)|Easy|[Math]|||Java|38| +|N/A|[Orderly Queue.java](https://github.com/awangdev/LintCode/blob/master/Java/Orderly%20Queue.java)|Hard|[Math, String]|||Java|39| +|43|[43. Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/43.%20Multiply%20Strings.java)|Medium|[Math, String]|O(mn)|O(mn)|Java|40| +|8|[8. String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/8.%20String%20to%20Integer%20(atoi).java)|Medium|[Math, String]|O(n)|O(n)|Java|41| +|13|[13. Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/13.%20Roman%20to%20Integer.java)|Easy|[Math, String]|O(n)|O(1)|Java|42| +|67|[67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%20Binary.java)|Easy|[Math, String, Two Pointers]|||Java|43| +|360|[360. Sort Transformed Array.java](https://github.com/awangdev/LintCode/blob/master/Java/360.%20Sort%20Transformed%20Array.java)|Medium|[Math, Two Pointers]|O(n)|O(n) store result|Java|44| + + + + + + +## Binary Search (45) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[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|[Array, BST, Binary Search, DP, Queue, TreeSet]|||Java|0| +|N/A|[Find Minimum in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Minimum%20in%20Rotated%20Sorted%20Array.java)|Medium|[Array, Binary Search]|||Java|1| +|N/A|[Find Peak Element.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element.java)|Medium|[Array, Binary Search]|||Java|2| +|N/A|[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|[Array, Binary Search]|||Java|3| +|N/A|[Search for a Range.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20for%20a%20Range.java)|Medium|[Array, Binary Search]|||Java|4| +|N/A|[Search a 2D Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix.java)|Medium|[Array, Binary Search]|||Java|5| +|88|[88. Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Search%20in%20Rotated%20Sorted%20Array%20II.java)|Medium|[Array, Binary Search]|O(logn), worst O(n)|O(1)|Java|6| +|33|[33. Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/33.%20Search%20in%20Rotated%20Sorted%20Array.java)|Medium|[Array, Binary Search]|O(logn)|O(1)|Java|7| +|34|[34. Find First and Last Position of Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/34.%20Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array.java)|Medium|[Array, Binary Search]|O(logn)|O(1)|Java|8| +|287|[287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)|Medium|[Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|9| +|N/A|[Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)|Hard|[Array, Binary Search, DFS, Divide and Conquer]|||Java|10| +|[lint]|[[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)|Medium|[Array, Binary Search, Lint, Two Pointers]|||Java|11| +|N/A|[Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)|Review|[Array, Binary Search, PreSum]|||Java|12| +|N/A|[Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)|Medium|[Array, Binary Search, Subarray, Two Pointers]|O(n)|O(1)|Java|13| +|N/A|[Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)|Hard|[Array, Binary Search, Two Pointers]|||Java|14| +|N/A|[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|[Array, Binary Search, Two Pointers]|||Java|15| +|315|[315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|O(nlogn)|O(n)|Java|16| +|270|[270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)|Easy|[BST, Binary Search, Tree]|O(logn)|O(1)|Java|17| +|N/A|[Closest Number in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Closest%20Number%20in%20Sorted%20Array.java)|Easy|[Binary Search]|||Java|18| +|N/A|[Classical Binary Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Classical%20Binary%20Search.java)|Easy|[Binary Search]|||Java|19| +|N/A|[Wood Cut.java](https://github.com/awangdev/LintCode/blob/master/Java/Wood%20Cut.java)|Medium|[Binary Search]|||Java|20| +|N/A|[Guess Number Higher or Lower.java](https://github.com/awangdev/LintCode/blob/master/Java/Guess%20Number%20Higher%20or%20Lower.java)|Easy|[Binary Search]|||Java|21| +|N/A|[Last Position of Target.java](https://github.com/awangdev/LintCode/blob/master/Java/Last%20Position%20of%20Target.java)|Easy|[Binary Search]|||Java|22| +|275|[275. H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/275.%20H-Index%20II.java)|Medium|[Binary Search]|O(logN)|O(1) extra|Java|23| +|1060|[1060. Missing Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1060.%20Missing%20Element%20in%20Sorted%20Array.java)|Medium|[Binary Search]|O(logn)|O(1)|Java|24| +|875|[875. Koko Eating Bananas.java](https://github.com/awangdev/LintCode/blob/master/Java/875.%20Koko%20Eating%20Bananas.java)|Medium|[Binary Search]|O(n*logM)|O(1)|Java|25| +|852|[852. Peak Index in a Mountain Array.java](https://github.com/awangdev/LintCode/blob/master/Java/852.%20Peak%20Index%20in%20a%20Mountain%20Array.java)|Easy|[Binary Search]|O(logn)|O(1)|Java|26| +|278|[278. First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/278.%20First%20Bad%20Version.java)|Easy|[Binary Search]|O(logN)|O(1)|Java|27| +|N/A|[Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)|Hard|[Binary Search, Coordinate DP, DP]|||Java|28| +|N/A|[Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)|Medium|[Binary Search, Coordinate DP, DP, Memoization]|O(n^2) dp, O(nLogN) binary search|O(n)|Java|29| +|N/A|[Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)|Hard|[Binary Search, DFS, Divide and Conquer]|||Java|30| +|222|[222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)|Medium|[Binary Search, DFS, Tree]|O(n)|O(h)|Java|31| +|N/A|[Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)|Hard|[Binary Search, DP, Partition DP]|||Java|32| +|N/A|[Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)|Medium|[Binary Search, Divide and Conquer]|||Java|33| +|N/A|[Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)|Medium|[Binary Search, Divide and Conquer, Lint, Segment Tree]|||Java|34| +|349|[349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)|Easy|[Binary Search, Hash Table, Sort, Two Pointers]|O(m + n)|O(m + n)|Java|35| +|350|[350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)|Easy|[Binary Search, Hash Table, Sort, Two Pointers]|(n)|(n)|Java|36| +|981|[981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)|Medium|[Binary Search, Hash Table, TreeMap]|set O(1), get(logn)|O(n)|Java|37| +|N/A|[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|[Binary Search, Heap]|O(n + klogn)|O(n)|Java|38| +|N/A|[Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)|Medium|[Binary Search, Lint, Segment Tree]|||Java|39| +|N/A|[Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)|Medium|[Binary Search, Lint, Segment Tree]|||Java|40| +|N/A|[Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)|Hard|[Binary Search, Lint, Segment Tree]|||Java|41| +|N/A|[Pow(x, n).java](https://github.com/awangdev/LintCode/blob/master/Java/Pow(x,%20n).java)|Medium|[Binary Search, Math]|||Java|42| +|367|[367. Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/367.%20Valid%20Perfect%20Square.java)|Easy|[Binary Search, Math]|O(logN)|O(1)|Java|43| +|69|[69. Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/69.%20Sqrt(x).java)|Easy|[Binary Search, Math]|||Java|44| + + + + + + +## Stack (38) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Maximal Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximal%20Rectangle.java)|Hard|[Array, DP, Hash Table, Stack]|||Java|0| +|N/A|[Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)|Hard|[Array, Monotonous Stack, Stack]|||Java|1| +|42|[42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)|Hard|[Array, Stack, Two Pointers]|O(n)|O(1)|Java|2| +|144|[144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)|Medium|[BFS, DFS, Stack, Tree]|O(n)|O(n)|Java|3| +|103|[103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)|Medium|[BFS, Stack, Tree]|O(n)|O(n)|Java|4| +|N/A|[Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)|Medium|[BST, DFS, Stack, Tree]|||Java|5| +|173|[173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%20Search%20Tree%20Iterator.java)|Medium|[BST, Design, Stack, Tree]|O(1) average|O(h)|Java|6| +|N/A|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|||Java|7| +|N/A|[Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)|Hard|[Binary Tree, DFS, Expression Tree, Stack]|||Java|8| +|N/A|[Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)|Hard|[Binary Tree, DFS, Expression Tree, Stack]|||Java|9| +|N/A|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|||Java|10| +|N/A|[Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)|Hard|[Binary Tree, Expression Tree, Minimum Binary Tree, Stack]|||Java|11| +|N/A|[Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)|Hard|[Coordinate DP, Stack, String]|||Java|12| +|N/A|[Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)|Medium|[DFS, Divide and Conquer, Stack]|||Java|13| +|1106|[1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)|Hard|[DFS, Stack, String]|||Java|14| +|716|[716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)|Medium|[Design, Doubly Linked List, Stack, TreeMap]|avg O(1), [O(logN) peekMax(), TreeMap]; [O(n) popMax(), TwoStack]|O(n)|Java|15| +|341|[341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)|Medium|[Design, NestedInteger, Stack]|O(n)|O(n)|Java|16| +|N/A|[Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)|Easy|[Design, Stack]|||Java|17| +|N/A|[Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)|Easy|[Design, Stack]|||Java|18| +|N/A|[Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)|Easy|[Design, Stack]|||Java|19| +|N/A|[Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)|Hard|[Greedy, Hash Table, Stack]|||Java|20| +|402|[402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)|Medium|[Greedy, Monotonous Stack, Stack]|O(n)|O(n)|Java|21| +|739|[739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)|Medium|[Hash Table, Monotonous Stack, Stack]|O(n)|O(n)|Java|22| +|496|[496. Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/496.%20Next%20Greater%20Element%20I.java)|Easy|[Hash Table, Stack]|O(n)|O(n)|Java|23| +|94|[94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)|Easy|[Hash Table, Stack, Tree]|O(n)|O(logn)|Java|24| +|N/A|[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|[Linked List, Stack, Tree]|O(n)|O(n)|Java|25| +|N/A|[Implement Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack.java)|Easy|[Stack]|||Java|26| +|N/A|[Evaluate Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Evaluate%20Reverse%20Polish%20Notation.java)|Medium|[Stack]|O(n)|O(n)|Java|27| +|1021|[1021. Remove Outermost Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1021.%20Remove%20Outermost%20Parentheses.java)|Easy|[Stack]|||Java|28| +|636|[636. Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/636.%20Exclusive%20Time%20of%20Functions.java)|Medium|[Stack]|O(n)|O(n)|Java|29| +|1249|[1249. Minimum Remove to Make Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1249.%20Minimum%20Remove%20to%20Make%20Valid%20Parentheses.java)|Medium|[Stack, String]|O(n)|O(n)|Java|30| +|20|[20. Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/20.%20Valid%20Parentheses.java)|Easy|[Stack, String]|O(n)|O(n)|Java|31| +|71|[71. Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/71.%20Simplify%20Path.java)|Medium|[Stack, String]|O(n)|O(n)|Java|32| +|N/A|[Maximum Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Binary%20Tree.java)|Medium|[Stack, Tree]|||Java|33| +|272|[272. Closest Binary Search Tree Value II.java](https://github.com/awangdev/LintCode/blob/master/Java/272.%20Closest%20Binary%20Search%20Tree%20Value%20II.java)|Hard|[Stack, Tree]|O(n)|O(n)|Java|34| +|145|[145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)|Medium|[Stack, Tree, Two Stacks]|O(n)|O(n)|Java|35| +|N/A|[Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)|Easy|[Stack, Two Pointers]|||Java|36| +|844|[844. Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/844.%20Backspace%20String%20Compare.java)|Easy|[Stack, Two Pointers]|O(n)|O(1)|Java|37| + + + + + + +## Divide and Conquer (38) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Median of Two Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/Median%20of%20Two%20Sorted%20Arrays.java)|Hard|[Array, Binary Search, DFS, Divide and Conquer]|||Java|0| +|169|[169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)|Easy|[Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort]|O(n)|O(1)|Java|1| +|53|[53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)|Easy|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|O(n)|O(n), O(1) rolling array|Java|2| +|105|[105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)|Medium|[Array, DFS, Divide and Conquer, Hash Table, Tree]|O(n)|O(n)|Java|3| +|N/A|[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|[Array, DFS, Divide and Conquer, Tree]|||Java|4| +|297|[297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|O(n)|O(n)|Java|5| +|218|[218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)|Hard|[BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line]|O(n^2logn)|O(n)|Java|6| +|327|[327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)|Hard|[BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree]|O(nlogn)|O(n)|Java|7| +|315|[315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|O(nlogn)|O(n)|Java|8| +|493|[493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)|Medium|[BST, Binary Indexed Tree, Divide and Conquer, Merge Sort, Segment Tree]|||Java|9| +|N/A|[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|[BST, DFS, Divide and Conquer, Linked List]|||Java|10| +|426|[426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|[BST, DFS, Divide and Conquer, Linked List, Tree]|O(n)|O(1)|Java|11| +|98|[98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)|Medium|[BST, DFS, Divide and Conquer, Tree]|O(n)|O(logn)|Java|12| +|N/A|[Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)|Medium|[BST, DP, Divide and Conquer, Tree]|||Java|13| +|N/A|[Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)|Hard|[Backtracking, DFS, Divide and Conquer, String]|O(4^n)|O(4^n)|Java|14| +|N/A|[Find Peak Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/Find%20Peak%20Element%20II.java)|Hard|[Binary Search, DFS, Divide and Conquer]|||Java|15| +|N/A|[Search a 2D Matrix II.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20a%202D%20Matrix%20II.java)|Medium|[Binary Search, Divide and Conquer]|||Java|16| +|N/A|[Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)|Medium|[Binary Search, Divide and Conquer, Lint, Segment Tree]|||Java|17| +|N/A|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|18| +|N/A|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|19| +|[lint]|[[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Query.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|20| +|[lint]|[[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build%20II.java)|Medium|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|||Java|21| +|[lint]|[[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)|Medium|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|||Java|22| +|N/A|[Fast Power.java](https://github.com/awangdev/LintCode/blob/master/Java/Fast%20Power.java)|Medium|[DFS, Divide and Conquer]|||Java|23| +|N/A|[Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)|Medium|[DFS, Divide and Conquer, Double Recursive, Tree]|||Java|24| +|N/A|[Decode String.java](https://github.com/awangdev/LintCode/blob/master/Java/Decode%20String.java)|Medium|[DFS, Divide and Conquer, Stack]|||Java|25| +|N/A|[Subtree of Another Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Subtree%20of%20Another%20Tree.java)|Easy|[DFS, Divide and Conquer, Tree]|||Java|26| +|N/A|[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|[DFS, Divide and Conquer, Tree]|||Java|27| +|N/A|[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|[DFS, Divide and Conquer, Tree]|||Java|28| +|N/A|[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|[DFS, Divide and Conquer, Tree]|O(n)|O(n)|Java|29| +|N/A|[Binary Tree Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence.java)|Medium|[DFS, Divide and Conquer, Tree]|||Java|30| +|1110|[1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)|Medium|[DFS, Divide and Conquer, Tree]|O(n)|O(logn)|Java|31| +|N/A|[Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)|Hard|[DP, Divide and Conquer, Interval DP, Memoization]|||Java|32| +|23|[23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)|Medium|[Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue]|O(nlogk)|O(logk)|Java|33| +|215|[215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort]|O(nlogk)|O(k)|Java|34| +|N/A|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|||Java|35| +|973|[973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)|Medium|[Divide and Conquer, Heap, Sort]|O(klogk)|O(k)|Java|36| +|N/A|[Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)|Medium|[Divide and Conquer, Linked List, Merge Sort, Sort]|||Java|37| + + + + + + +## Backtracking (35) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|78|[78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)|Medium|[Array, BFS, Backtracking, Bit Manipulation, DFS]|O(2^n)|O(2^n)|Java|0| +|N/A|[Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)|Medium|[Array, BFS, Backtracking, DFS]|O(2^n)||Java|1| +|N/A|[Word Ladder II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Ladder%20II.java)|Hard|[Array, BFS, Backtracking, DFS, Hash Table, String]|||Java|2| +|N/A|[Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)|Medium|[Array, Backtracking, Combination, DFS]|||Java|3| +|39|[39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)|Medium|[Array, Backtracking, Combination, DFS]|O(k * 2^n), k = avg rst length|O(k) stack depth, if not counting result size|Java|4| +|40|[40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%20Sum%20II.java)|Medium|[Array, Backtracking, Combination, DFS]|O(k * 2^n), k = avg rst length|O(n) stack depth, if not counting result size|Java|5| +|N/A|[Word Search.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search.java)|Medium|[Array, Backtracking, DFS]|||Java|6| +|254|[254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)|Medium|[BFS, Backtracking, DFS]|O(x), x is the # of results|O(y), y is all ongoing candidates in queue|Java|7| +|269|[269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)|Hard|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n), n = # of graph edges|O(n)|Java|8| +|207|[207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)|Medium|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|9| +|46|[46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)|Medium|[BFS, Backtracking, DFS, Permutation]|O(n!)|O(n!)|Java|10| +|N/A|[Gray Code.java](https://github.com/awangdev/LintCode/blob/master/Java/Gray%20Code.java)|Medium|[Backtracking]|||Java|11| +|51|[51. N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/51.%20N-Queens.java)|Hard|[Backtracking]|O(n!)|O(n^2)|Java|12| +|52|[52. N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/52.%20N-Queens%20II.java)|Hard|[Backtracking]|O(n!)|O(n)|Java|13| +|257|[257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%20Tree%20Paths.java)|Easy|[Backtracking, Binary Tree, DFS]|O(n)|O(nlogn)|Java|14| +|N/A|[Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)|Medium|[Backtracking, Combination, DFS]|||Java|15| +|N/A|[Robot Room Cleaner.java](https://github.com/awangdev/LintCode/blob/master/Java/Robot%20Room%20Cleaner.java)|Hard|[Backtracking, DFS]|||Java|16| +|131|[131. Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/131.%20Palindrome%20Partitioning.java)|Medium|[Backtracking, DFS]|O(2^n)|O(n^2)|Java|17| +|1219|[1219. Path with Maximum Gold.java](https://github.com/awangdev/LintCode/blob/master/Java/1219.%20Path%20with%20Maximum%20Gold.java)|Medium|[Backtracking, DFS]|O(n^2)|O(n) recursive depth|Java|18| +|47|[47. Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/47.%20Permutations%20II.java)|Medium|[Backtracking, DFS]|||Java|19| +|N/A|[Flip Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Flip%20Game%20II.java)|Medium|[Backtracking, DFS, DP]|||Java|20| +|140|[140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)|Hard|[Backtracking, DFS, DP, Hash Table, Memoization]|O(n!)|O(n!)|Java|21| +|N/A|[Expression Add Operators.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Add%20Operators.java)|Hard|[Backtracking, DFS, Divide and Conquer, String]|O(4^n)|O(4^n)|Java|22| +|332|[332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)|Medium|[Backtracking, DFS, Graph]|O(n^n)|O(m)|Java|23| +|22|[22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%20Parentheses.java)|Medium|[Backtracking, DFS, Sequence DFS, String]|O(2^n)|O(2^n)|Java|24| +|N/A|[Restore IP Addresses.java](https://github.com/awangdev/LintCode/blob/master/Java/Restore%20IP%20Addresses.java)|Medium|[Backtracking, DFS, String]|||Java|25| +|N/A|[Path Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20II.java)|Easy|[Backtracking, DFS, Tree]|||Java|26| +|N/A|[Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)|Hard|[Backtracking, DFS, Trie]|||Java|27| +|N/A|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|||Java|28| +|10|[10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|||Java|29| +|211|[211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)|Medium|[Backtracking, Design, Trie]|O(n) to search and to add word|< O(mn), depends on the input. m = # of words|Java|30| +|N/A|[Permutation Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Permutation%20Sequence.java)|Medium|[Backtracking, Math]|||Java|31| +|N/A|[Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)|Medium|[Backtracking, Permutation]|||Java|32| +|N/A|[Letter Combinations of a Phone Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Letter%20Combinations%20of%20a%20Phone%20Number.java)|Medium|[Backtracking, String]|||Java|33| +|N/A|[Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)|Hard|[Backtracking, Trie]|||Java|34| + + + + + + +## Linked List (34) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[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|[BST, DFS, Divide and Conquer, Linked List]|||Java|0| +|426|[426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|[BST, DFS, Divide and Conquer, Linked List, Tree]|O(n)|O(1)|Java|1| +|142|[142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)|Medium|[Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|2| +|141|[141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)|Easy|[Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|3| +|430|[430. Flatten a Multilevel Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/430.%20Flatten%20a%20Multilevel%20Doubly%20Linked%20List.java)|Medium|[DFS, Linked List]|O(n)|O(1)|Java|4| +|146|[146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)|Medium|[Design, Doubly Linked List, Hash Table, Linked List]|O(1)|O(1)|Java|5| +|23|[23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)|Medium|[Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue]|O(nlogk)|O(logk)|Java|6| +|N/A|[Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)|Medium|[Divide and Conquer, Linked List, Merge Sort, Sort]|||Java|7| +|N/A|[Majority Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20III.java)|Medium|[Hash Table, Linked List]|||Java|8| +|138|[138. Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/138.%20Copy%20List%20with%20Random%20Pointer.java)|Medium|[Hash Table, Linked List]|O(n)|O(n)|Java|9| +|N/A|[Two Lists Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Two%20Lists%20Sum.java)|Medium|[Linked List]|||Java|10| +|N/A|[Swap Nodes in Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Nodes%20in%20Pairs.java)|Medium|[Linked List]|||Java|11| +|N/A|[Reverse Linked List II .java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20Linked%20List%20II%20.java)|Medium|[Linked List]|||Java|12| +|N/A|[Reorder List.java](https://github.com/awangdev/LintCode/blob/master/Java/Reorder%20List.java)|Medium|[Linked List]|||Java|13| +|N/A|[Remove Duplicates from Unsorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Unsorted%20List.java)|Medium|[Linked List]|||Java|14| +|N/A|[Remove Duplicates from Sorted List II.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicates%20from%20Sorted%20List%20II.java)|Medium|[Linked List]|||Java|15| +|N/A|[Add Two Numbers II.java](https://github.com/awangdev/LintCode/blob/master/Java/Add%20Two%20Numbers%20II.java)|Medium|[Linked List]|||Java|16| +|21|[21. Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/21.%20Merge%20Two%20Sorted%20Lists.java)|Easy|[Linked List]|O(n)|O(1)|Java|17| +|237|[237. Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/237.%20Delete%20Node%20in%20a%20Linked%20List.java)|Easy|[Linked List]|||Java|18| +|83|[83. Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/83.%20Remove%20Duplicates%20from%20Sorted%20List.java)|Easy|[Linked List]|||Java|19| +|203|[203. Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/203.%20Remove%20Linked%20List%20Elements.java)|Easy|[Linked List]|||Java|20| +|206|[206. Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/206.%20Reverse%20Linked%20List.java)|Easy|[Linked List]|||Java|21| +|369|[369. Plus One Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/369.%20Plus%20One%20Linked%20List.java)|Medium|[Linked List]|O(n)|O(1)|Java|22| +|876|[876. Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/876.%20Middle%20of%20Linked%20List.java)|Easy|[Linked List]|||Java|23| +|160|[160. Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/160.%20Intersection%20of%20Two%20Linked%20Lists.java)|Easy|[Linked List]|||Java|24| +|[lint]|[[lint]. Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Nth%20to%20Last%20Node%20in%20List.java)|Easy|[Linked List, Lint]|||Java|25| +|2|[2. Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/2.%20Add%20Two%20Numbers.java)|Medium|[Linked List, Math]|O(max(m,n))|O(max(m,n))|Java|26| +|N/A|[Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/Linked%20List%20Cycle%20II.java)|Medium|[Linked List, Math, Two Pointers]|||Java|27| +|N/A|[Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)|Medium|[Linked List, Sort]|||Java|28| +|N/A|[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|[Linked List, Stack, Tree]|O(n)|O(n)|Java|29| +|N/A|[Rotate List.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20List.java)|Medium|[Linked List, Two Pointers]|||Java|30| +|N/A|[Partition List.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20List.java)|Medium|[Linked List, Two Pointers]|||Java|31| +|19|[19. Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/19.%20Remove%20Nth%20Node%20From%20End%20of%20List.java)|Medium|[Linked List, Two Pointers]|O(n)|O(1)|Java|32| +|234|[234. Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/234.%20Palindrome%20Linked%20List.java)|Easy|[Linked List, Two Pointers]|O(n)|O(1)|Java|33| + + + + + + +## Sort (31) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|169|[169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)|Easy|[Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort]|O(n)|O(1)|Java|0| +|N/A|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|[Array, Interval, PriorityQueue, Sort, Sweep Line]|||Java|1| +|N/A|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|[Array, Partition, Quick Sort, Sort, Two Pointers]|||Java|2| +|56|[56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)|Medium|[Array, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|3| +|57|[57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)|Hard|[Array, PriorityQueue, Sort, Sweep Line]|O(n)|O(n)|Java|4| +|N/A|[Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)|Medium|[Array, Quick Sort, Sort, Two Pointers]|||Java|5| +|N/A|[Wiggle Sort.java](https://github.com/awangdev/LintCode/blob/master/Java/Wiggle%20Sort.java)|Medium|[Array, Sort]|||Java|6| +|N/A|[The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)|Medium|[Array, Sort, Two Pointers]|||Java|7| +|259|[259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)|Medium|[Array, Sort, Two Pointers]|||Java|8| +|15|[15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)|Medium|[Array, Sort, Two Pointers]|O(n^2)||Java|9| +|1033|[1033. Moving Stones Until Consecutive.java](https://github.com/awangdev/LintCode/blob/master/Java/1033.%20Moving%20Stones%20Until%20Consecutive.java)|Easy|[Basic Implementation, Sort]|O(1), only 3 elements|O(1)|Java|10| +|349|[349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)|Easy|[Binary Search, Hash Table, Sort, Two Pointers]|O(m + n)|O(m + n)|Java|11| +|350|[350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)|Easy|[Binary Search, Hash Table, Sort, Two Pointers]|(n)|(n)|Java|12| +|1048|[1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)|Medium|[Bucket Sort, DP, Hash Table, Sort]|O(n)|O(n)|Java|13| +|1057|[1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)|Medium|[Bucket Sort, Greedy, PriorityQueue, Sort]|O(mn)|O(mn)|Java|14| +|274|[274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.H-Index.java)|Medium|[Bucket Sort, Hash Table, Sort]|O(n)|O(n)|Java|15| +|973|[973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)|Medium|[Divide and Conquer, Heap, Sort]|O(klogk)|O(k)|Java|16| +|N/A|[Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)|Medium|[Divide and Conquer, Linked List, Merge Sort, Sort]|||Java|17| +|767|[767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)|Medium|[Greedy, Hash Table, Heap, Sort, String]|O(m), m = # of unique letters|O(nLogm), n = length|Java|18| +|1094|[1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)|Medium|[Greedy, Heap, PriorityQueue, Sort]|O(n)|O(1) only use bucket size 1000|Java|19| +|253|[253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)|Medium|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|20| +|242|[242. Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/242.%20Valid%20Anagram.java)|Easy|[Hash Table, Sort]|O(n)|O(1), unique chars|Java|21| +|N/A|[Insertion Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Insertion%20Sort%20List.java)|Medium|[Linked List, Sort]|||Java|22| +|[tool]|[[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)|Medium|[Lint, Merge Sort, Sort]|O(mlogn)|O(n)|Java|23| +|N/A|[Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)|Medium|[Partition, Quick Sort, Sort, Two Pointers]|||Java|24| +|N/A|[Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)|Medium|[Partition, Sort, String, Two Pointers]|||Java|25| +|N/A|[Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)|Medium|[PreSum, PriorityQueue, Sort, Subarray]|O(nlogn)|O(n)|Java|26| +|252|[252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)|Easy|[PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(1)|Java|27| +|855|[855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)|Medium|[PriorityQueue, Sort, TreeMap, TreeSet]|O(logn)|O(n)|Java|28| +|N/A|[QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)|Medium|[Quick Sort, Sort]|||Java|29| +|N/A|[Largest Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Number.java)|Medium|[Sort]|||Java|30| + + + + + + +## Lint (27) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|[lint]|[[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)|Medium|[Array, Binary Search, Lint, Two Pointers]|||Java|0| +|[lint]|[[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)|Medium|[Array, Hash Table, Lint]|O(n)|O(n)|Java|1| +|[lint]|[[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)|Easy|[Array, Hash Table, Lint, PreSum, Subarray]|O(n)|O(n)|Java|2| +|[lint]|[[lint]. Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Product%20of%20Array%20Exclude%20Itself.java)|Medium|[Array, Lint]|||Java|3| +|[lint]|[[lint]. Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Recover%20Rotated%20Sorted%20Array.java)|Easy|[Array, Lint]|||Java|4| +|N/A|[[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)|Easy|[Array, Lint, Quick Select, Quick Sort, Two Pointers]|O(n)|O(logN)|Java|5| +|[lint]|[[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)|Easy|[Array, Lint, String]|||Java|6| +|[lint]|[[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)|Medium|[Array, Lint, Two Pointers]|||Java|7| +|[tool]|[[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)|Medium|[BFS, DFS, Lint, Topological Sort]|O(V + E)|O(V + E)|Java|8| +|N/A|[Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)|Medium|[Binary Search, Divide and Conquer, Lint, Segment Tree]|||Java|9| +|N/A|[Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)|Medium|[Binary Search, Lint, Segment Tree]|||Java|10| +|N/A|[Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)|Medium|[Binary Search, Lint, Segment Tree]|||Java|11| +|N/A|[Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)|Hard|[Binary Search, Lint, Segment Tree]|||Java|12| +|N/A|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|13| +|N/A|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|14| +|[lint]|[[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Query.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|15| +|[lint]|[[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build%20II.java)|Medium|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|||Java|16| +|[lint]|[[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)|Medium|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|||Java|17| +|[tool]|[[tool]. Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Hash%20Function.java)|Easy|[Hash Table, Lint]|O(1) get|O(n) store map|Java|18| +|[lint]|[[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)|Easy|[Hash Table, Lint, String]|||Java|19| +|[lint]|[[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)|Easy|[Hash Table, Lint, Tree]|||Java|20| +|[lint]|[[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)|Hard|[HashHeap, Heap, Lint]|||Java|21| +|[lint]|[[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)|Medium|[HashHeap, Heap, Lint, MinHeap]|||Java|22| +|[lint]|[[lint]. Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Nth%20to%20Last%20Node%20in%20List.java)|Easy|[Linked List, Lint]|||Java|23| +|[tool]|[[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)|Medium|[Lint, Merge Sort, Sort]|O(mlogn)|O(n)|Java|24| +|[lint]|[[lint]. Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Compare%20Strings.java)|Easy|[Lint, String]|||Java|25| +|[tool]|[[tool]. UnionFind.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20UnionFind.java)|Medium|[Lint, Union Find]|O(n), with Path Compression O(mN), with Union by Rank O(logN)|O(n)|Java|26| + + + + + + +## Design (27) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|380|[380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)|Medium|[Array, Design, Hash Table]|O(1) avg|O(n)|Java|0| +|244|[244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)|Medium|[Array, Design, Hash Table, Two Pointers]|O(n) to build map, O(a + b) to query|O(n)|Java|1| +|245|[245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)|Medium|[Array, Design, Hash Table, Two Pointers]|O(n)|O(1)|Java|2| +|297|[297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|O(n)|O(n)|Java|3| +|173|[173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%20Search%20Tree%20Iterator.java)|Medium|[BST, Design, Stack, Tree]|O(1) average|O(h)|Java|4| +|211|[211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)|Medium|[Backtracking, Design, Trie]|O(n) to search and to add word|< O(mn), depends on the input. m = # of words|Java|5| +|N/A|[Flatten 2D Vector.java](https://github.com/awangdev/LintCode/blob/master/Java/Flatten%202D%20Vector.java)|Medium|[Design]|||Java|6| +|N/A|[Peeking Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Peeking%20Iterator.java)|Medium|[Design]|||Java|7| +|N/A|[Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)|Easy|[Design]|||Java|8| +|432|[432. All One Data Structure.java](https://github.com/awangdev/LintCode/blob/master/Java/432.%20All%20One%20Data%20Structure.java)|Hard|[Design, Doubly Linked List]|O(1)|O(n)|Java|9| +|146|[146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)|Medium|[Design, Doubly Linked List, Hash Table, Linked List]|O(1)|O(1)|Java|10| +|716|[716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)|Medium|[Design, Doubly Linked List, Stack, TreeMap]|avg O(1), [O(logN) peekMax(), TreeMap]; [O(n) popMax(), TwoStack]|O(n)|Java|11| +|N/A|[Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)|Hard|[Design, Geometry, Hash Table]|||Java|12| +|N/A|[ColorGrid.java](https://github.com/awangdev/LintCode/blob/master/Java/ColorGrid.java)|Medium|[Design, Hash Table]|||Java|13| +|N/A|[LFU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/LFU%20Cache.java)|Hard|[Design, Hash Table]|||Java|14| +|N/A|[Unique Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Word%20Abbreviation.java)|Medium|[Design, Hash Table]|||Java|15| +|359|[359. Logger Rate Limiter.java](https://github.com/awangdev/LintCode/blob/master/Java/359.%20Logger%20Rate%20Limiter.java)|Easy|[Design, Hash Table]|O(1)|O(n)|Java|16| +|170|[170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)|Easy|[Design, Hash Table, Memoization]|O(n)|O(n)|Java|17| +|N/A|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words|O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map`|Java|18| +|295|[295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)|Hard|[Design, Heap, MaxHeap, MinHeap]|O(1) get, O(logn) addNum|O(n)|Java|19| +|N/A|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|||Java|20| +|341|[341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)|Medium|[Design, NestedInteger, Stack]|O(n)|O(n)|Java|21| +|346|[346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)|Easy|[Design, Queue, Sliding Window]|O(1) for `next()`|O(size) for fixed storage|Java|22| +|N/A|[Implement Stack using Queues.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Stack%20using%20Queues.java)|Easy|[Design, Stack]|||Java|23| +|N/A|[Min Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/Min%20Stack.java)|Easy|[Design, Stack]|||Java|24| +|N/A|[Implement Queue using Stacks.java](https://github.com/awangdev/LintCode/blob/master/Java/Implement%20Queue%20using%20Stacks.java)|Easy|[Design, Stack]|||Java|25| +|208|[208. Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/208.%20Implement%20Trie%20(Prefix%20Tree).java)|Medium|[Design, Trie]|||Java|26| + + + + + + +## Greedy (24) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|277|[277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)|Medium|[Adjacency Matrix, Array, Graph, Greedy, Pruning]|O(n)|O(1)|Java|0| +|N/A|[Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)|Hard|[Array, Coordinate DP, DP, Greedy]|O(n)|O(1)|Java|1| +|55|[55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%20Game.java)|Medium|[Array, DP, Greedy]|O(n)|O(1)|Java|2| +|N/A|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|||Java|3| +|N/A|[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|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(n), O(1) rolling array|Java|4| +|122|[122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(1) greedy, O(n) dp|Java|5| +|N/A|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|[Array, DP, Greedy, Sequence DP, Subarray]|O(m)|O(1)|Java|6| +|621|[621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)|Medium|[Array, Enumeration, Greedy, PriorityQueue, Queue]|O(n)|O(1)|Java|7| +|1007|[1007. Minimum Domino Rotations For Equal Row.java](https://github.com/awangdev/LintCode/blob/master/Java/1007.%20Minimum%20Domino%20Rotations%20For%20Equal%20Row.java)|Medium|[Array, Greedy]|O(n)|O(1)|Java|8| +|605|[605. Can Place Flowers.java](https://github.com/awangdev/LintCode/blob/master/Java/605.%20Can%20Place%20Flowers.java)|Easy|[Array, Greedy]|O(n)|O(1)|Java|9| +|1053|[1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)|Medium|[Array, Greedy, Permutation]|O(n)|O(1)|Java|10| +|N/A|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|||Java|11| +|1057|[1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)|Medium|[Bucket Sort, Greedy, PriorityQueue, Sort]|O(mn)|O(mn)|Java|12| +|N/A|[Cracking the Safe.java](https://github.com/awangdev/LintCode/blob/master/Java/Cracking%20the%20Safe.java)|Hard|[DFS, Greedy, Math]|||Java|13| +|N/A|[Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)|Medium|[DP, Game Theory, Greedy]|||Java|14| +|N/A|[Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)|Medium|[Enumeration, Greedy]|||Java|15| +|N/A|[Queue Reconstruction by Height.java](https://github.com/awangdev/LintCode/blob/master/Java/Queue%20Reconstruction%20by%20Height.java)|Medium|[Greedy]|||Java|16| +|134|[134. Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/134.%20Gas%20Station.java)|Medium|[Greedy]|O(n)|O(1)|Java|17| +|N/A|[Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)|Hard|[Greedy, Hash Table, Heap]|||Java|18| +|767|[767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)|Medium|[Greedy, Hash Table, Heap, Sort, String]|O(m), m = # of unique letters|O(nLogm), n = length|Java|19| +|N/A|[Remove Duplicate Letters.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Duplicate%20Letters.java)|Hard|[Greedy, Hash Table, Stack]|||Java|20| +|1094|[1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)|Medium|[Greedy, Heap, PriorityQueue, Sort]|O(n)|O(1) only use bucket size 1000|Java|21| +|253|[253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)|Medium|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|22| +|402|[402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)|Medium|[Greedy, Monotonous Stack, Stack]|O(n)|O(n)|Java|23| + + + + + + +## BST (23) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[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|[Array, BST, Binary Search, DP, Queue, TreeSet]|||Java|0| +|N/A|[K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)|Hard|[Array, BST, TreeSet]|||Java|1| +|N/A|[Minimum Absolute Difference in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Absolute%20Difference%20in%20BST.java)|Easy|[BST]|||Java|2| +|N/A|[Remove Node in Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Remove%20Node%20in%20Binary%20Search%20Tree.java)|Hard|[BST]|||Java|3| +|N/A|[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|[BST]|||Java|4| +|N/A|[Zigzag Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/Zigzag%20Iterator.java)|Medium|[BST]|||Java|5| +|N/A|[Contains Duplicate III.java](https://github.com/awangdev/LintCode/blob/master/Java/Contains%20Duplicate%20III.java)|Medium|[BST]|||Java|6| +|315|[315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|O(nlogn)|O(n)|Java|7| +|493|[493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)|Medium|[BST, Binary Indexed Tree, Divide and Conquer, Merge Sort, Segment Tree]|||Java|8| +|270|[270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)|Easy|[BST, Binary Search, Tree]|O(logn)|O(1)|Java|9| +|N/A|[Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)|Medium|[BST, Binary Tree]|||Java|10| +|N/A|[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|[BST, DFS, Divide and Conquer, Linked List]|||Java|11| +|426|[426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)|Medium|[BST, DFS, Divide and Conquer, Linked List, Tree]|O(n)|O(1)|Java|12| +|98|[98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)|Medium|[BST, DFS, Divide and Conquer, Tree]|O(n)|O(logn)|Java|13| +|N/A|[Kth Smallest Element in a BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Smallest%20Element%20in%20a%20BST.java)|Medium|[BST, DFS, Stack, Tree]|||Java|14| +|N/A|[Recover Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Recover%20Binary%20Search%20Tree.java)|Hard|[BST, DFS, Tree]|||Java|15| +|235|[235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)|Easy|[BST, DFS, Tree]|O(logn)|O(logn)|Java|16| +|N/A|[Unique Binary Search Tree II.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree%20II.java)|Medium|[BST, DP, Divide and Conquer, Tree]|||Java|17| +|N/A|[Unique Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Unique%20Binary%20Search%20Tree.java)|Medium|[BST, DP, Tree]|||Java|18| +|173|[173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%20Search%20Tree%20Iterator.java)|Medium|[BST, Design, Stack, Tree]|O(1) average|O(h)|Java|19| +|938|[938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)|Easy|[BST, Recursion, Tree]|||Java|20| +|N/A|[Inorder Successor in BST.java](https://github.com/awangdev/LintCode/blob/master/Java/Inorder%20Successor%20in%20BST.java)|Medium|[BST, Tree]|||Java|21| +|N/A|[Trim a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/Trim%20a%20Binary%20Search%20Tree.java)|Easy|[BST, Tree]|||Java|22| + + + + + + +## PriorityQueue (23) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|621|[621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)|Medium|[Array, Enumeration, Greedy, PriorityQueue, Queue]|O(n)|O(1)|Java|0| +|N/A|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|[Array, Interval, PriorityQueue, Sort, Sweep Line]|||Java|1| +|414|[414. Third Maximum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/414.%20Third%20Maximum%20Number.java)|Easy|[Array, PriorityQueue]|||Java|2| +|56|[56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)|Medium|[Array, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|3| +|57|[57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)|Hard|[Array, PriorityQueue, Sort, Sweep Line]|O(n)|O(n)|Java|4| +|N/A|[The Maze II.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20II.java)|Medium|[BFS, DFS, PriorityQueue]|||Java|5| +|N/A|[The Maze III.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Maze%20III.java)|Hard|[BFS, DFS, PriorityQueue]|||Java|6| +|N/A|[Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)|Hard|[BFS, Heap, MinHeap, PriorityQueue]|||Java|7| +|218|[218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)|Hard|[BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line]|O(n^2logn)|O(n)|Java|8| +|1057|[1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)|Medium|[Bucket Sort, Greedy, PriorityQueue, Sort]|O(mn)|O(mn)|Java|9| +|N/A|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|[DP, Enumeration, Heap, Math, PriorityQueue]|O(n)|O(n)|Java|10| +|N/A|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words|O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map`|Java|11| +|23|[23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)|Medium|[Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue]|O(nlogk)|O(logk)|Java|12| +|215|[215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort]|O(nlogk)|O(k)|Java|13| +|N/A|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|||Java|14| +|1094|[1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)|Medium|[Greedy, Heap, PriorityQueue, Sort]|O(n)|O(1) only use bucket size 1000|Java|15| +|253|[253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)|Medium|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|16| +|347|[347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|O(n)|O(n)|Java|17| +|692|[692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|O(n)|O(n)|Java|18| +|[lint]|[[lint]. Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Merge%20k%20Sorted%20Arrays.java)|Medium|[Heap, MinHeap, PriorityQueue]|O(nlogk)|O(k)|Java|19| +|N/A|[Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)|Medium|[PreSum, PriorityQueue, Sort, Subarray]|O(nlogn)|O(n)|Java|20| +|252|[252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)|Easy|[PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(1)|Java|21| +|855|[855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)|Medium|[PriorityQueue, Sort, TreeMap, TreeSet]|O(logn)|O(n)|Java|22| + + + + + + +## Heap (22) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|743|[743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)|Medium|[BFS, DFS, Graph, Heap, PQ]|O(nlogn)|O(n)|Java|0| +|N/A|[Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)|Hard|[BFS, Heap, MinHeap, PriorityQueue]|||Java|1| +|218|[218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)|Hard|[BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line]|O(n^2logn)|O(n)|Java|2| +|N/A|[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|[Binary Search, Heap]|O(n + klogn)|O(n)|Java|3| +|N/A|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|[DP, Enumeration, Heap, Math, PriorityQueue]|O(n)|O(n)|Java|4| +|239|[239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)|Hard|[Deque, Heap, Sliding Window]|O(n)|O(n)|Java|5| +|295|[295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)|Hard|[Design, Heap, MaxHeap, MinHeap]|O(1) get, O(logn) addNum|O(n)|Java|6| +|N/A|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|||Java|7| +|23|[23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)|Medium|[Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue]|O(nlogk)|O(logk)|Java|8| +|215|[215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort]|O(nlogk)|O(k)|Java|9| +|N/A|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|||Java|10| +|973|[973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)|Medium|[Divide and Conquer, Heap, Sort]|O(klogk)|O(k)|Java|11| +|N/A|[Rearrange String k Distance Apart.java](https://github.com/awangdev/LintCode/blob/master/Java/Rearrange%20String%20k%20Distance%20Apart.java)|Hard|[Greedy, Hash Table, Heap]|||Java|12| +|767|[767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)|Medium|[Greedy, Hash Table, Heap, Sort, String]|O(m), m = # of unique letters|O(nLogm), n = length|Java|13| +|1094|[1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)|Medium|[Greedy, Heap, PriorityQueue, Sort]|O(n)|O(1) only use bucket size 1000|Java|14| +|253|[253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)|Medium|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|15| +|347|[347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|O(n)|O(n)|Java|16| +|692|[692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|O(n)|O(n)|Java|17| +|[lint]|[[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)|Hard|[HashHeap, Heap, Lint]|||Java|18| +|[lint]|[[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)|Medium|[HashHeap, Heap, Lint, MinHeap]|||Java|19| +|373|[373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)|Medium|[Heap, MaxHeap, MinHeap]|O(klogk)|O(k)|Java|20| +|[lint]|[[lint]. Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Merge%20k%20Sorted%20Arrays.java)|Medium|[Heap, MinHeap, PriorityQueue]|O(nlogk)|O(k)|Java|21| + + + + + + +## Sequence DP (21) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|53|[53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)|Easy|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|O(n)|O(n), O(1) rolling array|Java|0| +|N/A|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|||Java|1| +|N/A|[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|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(n), O(1) rolling array|Java|2| +|122|[122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(1) greedy, O(n) dp|Java|3| +|N/A|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|[Array, DP, Greedy, Sequence DP, Subarray]|O(m)|O(1)|Java|4| +|N/A|[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|[Array, DP, Sequence DP]|||Java|5| +|121|[121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)|Easy|[Array, DP, Sequence DP]|||Java|6| +|N/A|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|||Java|7| +|10|[10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|||Java|8| +|N/A|[Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)|Medium|[DP, Double Sequence DP, Sequence DP]|||Java|9| +|N/A|[Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)|Medium|[DP, Double Sequence DP, Sequence DP, String]|||Java|10| +|72|[72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)|Hard|[DP, Double Sequence DP, Sequence DP, String]|O(MN)||Java|11| +|N/A|[K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)|Hard|[DP, Double Sequence DP, Sequence DP, Trie]|||Java|12| +|139|[139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)|Medium|[DP, Hash Table, Sequence DP]|O(n^2)|O(n)|Java|13| +|70|[70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%20Stairs.java)|Easy|[DP, Memoization, Sequence DP]|||Java|14| +|N/A|[Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)|Easy|[DP, Sequence DP]|O(n)|O(n)|Java|15| +|N/A|[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|[DP, Sequence DP]|||Java|16| +|N/A|[House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)|Medium|[DP, Sequence DP, Status DP]|||Java|17| +|198|[198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)|Easy|[DP, Sequence DP, Status DP]|O(n)|O(n) or rolling array O(1)|Java|18| +|256|[256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)|Easy|[DP, Sequence DP, Status DP]|O(nm), m = # of colors|O(nm), or O(1) with rolling array|Java|19| +|265|[265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)|Hard|[DP, Sequence DP, Status DP]|O(NK^2):|O(K) with rolling array|Java|20| + + + + + + +## Graph (20) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|277|[277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)|Medium|[Adjacency Matrix, Array, Graph, Greedy, Pruning]|O(n)|O(1)|Java|0| +|1267|[1267. Count Servers that Communicate.java](https://github.com/awangdev/LintCode/blob/master/Java/1267.%20Count%20Servers%20that%20Communicate.java)|Medium|[Array, Graph]|O(mn)|O(m + n)|Java|1| +|269|[269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)|Hard|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n), n = # of graph edges|O(n)|Java|2| +|207|[207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)|Medium|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|3| +|1161|[1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)|Medium|[BFS, DFS, Graph]|O(n) visit all nodes|O(n)|Java|4| +|133|[133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%20Graph.java)|Medium|[BFS, DFS, Graph]|O(n)|O(n)|Java|5| +|743|[743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)|Medium|[BFS, DFS, Graph, Heap, PQ]|O(nlogn)|O(n)|Java|6| +|1203|[1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)|Hard|[BFS, DFS, Graph, Topological Sort]|O(V + E) to traverse the graph, #nodes + #edges|O(V + E)|Java|7| +|210|[210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)|Medium|[BFS, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|8| +|N/A|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|[BFS, DFS, Graph, Tree, Union Find]|||Java|9| +|N/A|[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|[BFS, DFS, Graph, Union Find]|||Java|10| +|261|[261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)|Medium|[BFS, DFS, Graph, Union Find]|||Java|11| +|399|[399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%20Division.java)|Medium|[BFS, DFS, Graph, Union Find]|||Java|12| +|N/A|[Minimum Height Trees.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Height%20Trees.java)|Medium|[BFS, Graph]|||Java|13| +|N/A|[Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)|Hard|[BFS, Graph]|||Java|14| +|1306|[1306. Jump Game III.java](https://github.com/awangdev/LintCode/blob/master/Java/1306.%20Jump%20Game%20III.java)|Medium|[BFS, Graph]|O(n)|O(n)|Java|15| +|332|[332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)|Medium|[Backtracking, DFS, Graph]|O(n^n)|O(m)|Java|16| +|1043|[1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)|Medium|[DFS, DP, Graph, Memoization]|O(n), calc memo[n]|O(n)|Java|17| +|N/A|[Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)|Hard|[DFS, Graph, Tree, Union Find]|||Java|18| +|1153|[1153. String Transforms Into Another String.java](https://github.com/awangdev/LintCode/blob/master/Java/1153.%20String%20Transforms%20Into%20Another%20String.java)|Hard|[Graph]|O(n)|O(n)|Java|19| + + + + + + +## Bit Manipulation (19) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|78|[78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)|Medium|[Array, BFS, Backtracking, Bit Manipulation, DFS]|O(2^n)|O(2^n)|Java|0| +|169|[169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)|Easy|[Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort]|O(n)|O(1)|Java|1| +|N/A|[Missing Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Missing%20Number.java)|Easy|[Array, Bit Manipulation, Math]|||Java|2| +|N/A|[Convert Integer A to Integer B.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Integer%20A%20to%20Integer%20B.java)|Easy|[Bit Manipulation]|||Java|3| +|N/A|[Single Number III.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20III.java)|Medium|[Bit Manipulation]|||Java|4| +|N/A|[Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)|Medium|[Bit Manipulation]|O(n)|O(1), 32-bit array|Java|5| +|N/A|[Count 1 in Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%201%20in%20Binary.java)|Easy|[Bit Manipulation]|||Java|6| +|N/A|[Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)|Easy|[Bit Manipulation]|O(n), n = # of bits|O(1)|Java|7| +|N/A|[Update Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Update%20Bits.java)|Medium|[Bit Manipulation]|||Java|8| +|N/A|[O(1) Check Power of 2.java](https://github.com/awangdev/LintCode/blob/master/Java/O(1)%20Check%20Power%20of%202.java)|Easy|[Bit Manipulation]|||Java|9| +|N/A|[Swap Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Swap%20Bits.java)|Easy|[Bit Manipulation]|||Java|10| +|N/A|[Single Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Single%20Number%20II.java)|Medium|[Bit Manipulation]|||Java|11| +|N/A|[Sum of Two Integers.java](https://github.com/awangdev/LintCode/blob/master/Java/Sum%20of%20Two%20Integers.java)|Easy|[Bit Manipulation]|||Java|12| +|405|[405. Convert a Number to Hexadecimal.java](https://github.com/awangdev/LintCode/blob/master/Java/405.%20Convert%20a%20Number%20to%20Hexadecimal.java)|Easy|[Bit Manipulation]|||Java|13| +|N/A|[Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)|Medium|[Bit Manipulation, Bitwise DP, DP]|||Java|14| +|136|[136. Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/136.%20Single%20Number.java)|Easy|[Bit Manipulation, Hash Table]|||Java|15| +|N/A|[Power of Two.java](https://github.com/awangdev/LintCode/blob/master/Java/Power%20of%20Two.java)|Easy|[Bit Manipulation, Math]|||Java|16| +|N/A|[Binary Representation.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Representation.java)|Hard|[Bit Manipulation, String]|||Java|17| +|N/A|[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|[Bit Manipulation, Trie]|||Java|18| + + + + + + +## Basic Implementation (18) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|485|[485. Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/485.%20Max%20Consecutive%20Ones.java)|Easy|[Array, Basic Implementation]|O(n)|O(1)|Java|0| +|119|[119. Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/119.%20Pascal's%20Triangle%20II.java)|Easy|[Array, Basic Implementation]|O(k^2), pascal triangle size|O(k^2)|Java|1| +|118|[118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)|Easy|[Array, Basic Implementation, List]|O(n^2) based on pascal triangle size|O(n^2)|Java|2| +|849|[849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)|Easy|[Array, Basic Implementation, Two Pointers]|O(n)|O(1)|Java|3| +|N/A|[Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)|Easy|[Basic Implementation]|||Java|4| +|686|[686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)|Easy|[Basic Implementation, Edge Case, String]|||Java|5| +|N/A|[Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)|Medium|[Basic Implementation, Enumeration, String]|||Java|6| +|415|[415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)|Easy|[Basic Implementation, Math, String]|O(n)|O(n)|Java|7| +|12|[12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)|Medium|[Basic Implementation, Math, String]|O(n)|O(n)|Java|8| +|1033|[1033. Moving Stones Until Consecutive.java](https://github.com/awangdev/LintCode/blob/master/Java/1033.%20Moving%20Stones%20Until%20Consecutive.java)|Easy|[Basic Implementation, Sort]|O(1), only 3 elements|O(1)|Java|9| +|N/A|[Count and Say.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20and%20Say.java)|Easy|[Basic Implementation, String]|||Java|10| +|788|[788. Rotated Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/788.%20Rotated%20Digits.java)|Easy|[Basic Implementation, String]|O(n)|O(n)|Java|11| +|408|[408. Valid Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/408.%20Valid%20Word%20Abbreviation.java)|Easy|[Basic Implementation, String]|||Java|12| +|1108|[1108. Defanging an IP Address.java](https://github.com/awangdev/LintCode/blob/master/Java/1108.%20Defanging%20an%20IP%20Address.java)|Easy|[Basic Implementation, String]|||Java|13| +|383|[383. Ransom Note.java](https://github.com/awangdev/LintCode/blob/master/Java/383.%20Ransom%20Note.java)|Easy|[Basic Implementation, String]|||Java|14| +|824|[824. Goat Latin.java](https://github.com/awangdev/LintCode/blob/master/Java/824.%20Goat%20Latin.java)|Easy|[Basic Implementation, String]|O(n)|O(1)|Java|15| +|443|[443. String Compression.java](https://github.com/awangdev/LintCode/blob/master/Java/443.%20String%20Compression.java)|Easy|[Basic Implementation, String]|||Java|16| +|893|[893. Groups of Special-Equivalent Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/893.%20Groups%20of%20Special-Equivalent%20Strings.java)|Easy|[Basic Implementation, String]|||Java|17| + + + + + + +## Segment Tree (17) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|218|[218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)|Hard|[BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line]|O(n^2logn)|O(n)|Java|0| +|327|[327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)|Hard|[BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree]|O(nlogn)|O(n)|Java|1| +|315|[315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|O(nlogn)|O(n)|Java|2| +|493|[493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)|Medium|[BST, Binary Indexed Tree, Divide and Conquer, Merge Sort, Segment Tree]|||Java|3| +|308|[308. Range Sum Query 2D - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/308.%20Range%20Sum%20Query%202D%20-%20Mutable.java)|Hard|[Binary Indexed Tree, Segment Tree]|build(n), update(logn), rangeRuery(logn + k)|O(n)|Java|4| +|307|[307. Range Sum Query - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/307.%20Range%20Sum%20Query%20-%20Mutable.java)|Medium|[Binary Indexed Tree, Segment Tree]|build O(n), query (logn +k), update O(logn)|O(n)|Java|5| +|N/A|[Interval Minimum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Minimum%20Number.java)|Medium|[Binary Search, Divide and Conquer, Lint, Segment Tree]|||Java|6| +|N/A|[Count of Smaller Number.java](https://github.com/awangdev/LintCode/blob/master/Java/Count%20of%20Smaller%20Number.java)|Medium|[Binary Search, Lint, Segment Tree]|||Java|7| +|N/A|[Interval Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum.java)|Medium|[Binary Search, Lint, Segment Tree]|||Java|8| +|N/A|[Interval Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Interval%20Sum%20II.java)|Hard|[Binary Search, Lint, Segment Tree]|||Java|9| +|N/A|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|10| +|N/A|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|11| +|[lint]|[[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Query.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|12| +|[lint]|[[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build%20II.java)|Medium|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|||Java|13| +|[lint]|[[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)|Medium|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|||Java|14| +|850|[850. Rectangle Area II.java](https://github.com/awangdev/LintCode/blob/master/Java/850.%20Rectangle%20Area%20II.java)|Hard|[Segment Tree, Sweep Line]|O(n^2)|O(n)|Java|15| +|715|[715. Range Module.java](https://github.com/awangdev/LintCode/blob/master/Java/715.%20Range%20Module.java)|Hard|[Segment Tree, TreeSet]|query O(logn), update O(n)|O(n)|Java|16| + + + + + + +## Coordinate DP (17) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|[Array, Coordinate DP, DFS, DP, Memoization]|||Java|0| +|N/A|[Longest Increasing Continuous subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence.java)|Easy|[Array, Coordinate DP, DP]|||Java|1| +|62|[62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(mn), rolling array O(n)|Java|2| +|64|[64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%20Path%20Sum.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(n) rolling array|Java|3| +|63|[63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%20Paths%20II.java)|Medium|[Array, Coordinate DP, DP]|O(mn)|O(mn)|Java|4| +|N/A|[Jump Game II.java](https://github.com/awangdev/LintCode/blob/master/Java/Jump%20Game%20II.java)|Hard|[Array, Coordinate DP, DP, Greedy]|O(n)|O(1)|Java|5| +|N/A|[Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)|Medium|[Array, Coordinate DP, DP, Memoization]|||Java|6| +|674|[674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)|Easy|[Array, Coordinate DP, DP, Sliding Window]|O(n)|O(1)|Java|7| +|N/A|[Russian Doll Envelopes.java](https://github.com/awangdev/LintCode/blob/master/Java/Russian%20Doll%20Envelopes.java)|Hard|[Binary Search, Coordinate DP, DP]|||Java|8| +|N/A|[Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)|Medium|[Binary Search, Coordinate DP, DP, Memoization]|O(n^2) dp, O(nLogN) binary search|O(n)|Java|9| +|N/A|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|||Java|10| +|N/A|[Number of Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Longest%20Increasing%20Subsequence.java)|Medium|[Coordinate DP, DP]|O(n^2)||Java|11| +|221|[221. Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/221.%20Maximal%20Square.java)|Medium|[Coordinate DP, DP]|O(mn)|O(mn)|Java|12| +|361|[361. Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/361.%20Bomb%20Enemy.java)|Medium|[Coordinate DP, DP]|O(mn)|O(n) by calculating column sum|Java|13| +|523|[523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)|Medium|[Coordinate DP, DP, Math, PreSum, Subarray]|O(n)|O(k)|Java|14| +|N/A|[Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)|Medium|[Coordinate DP, DP, Status DP]|||Java|15| +|N/A|[Longest Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Valid%20Parentheses.java)|Hard|[Coordinate DP, Stack, String]|||Java|16| + + + + + + +## Union Find (16) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Longest Consecutive Sequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Consecutive%20Sequence.java)|Hard|[Array, Hash Table, Union Find]|||Java|0| +|N/A|[Redundant Connection.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection.java)|Medium|[BFS, DFS, Graph, Tree, Union Find]|||Java|1| +|N/A|[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|[BFS, DFS, Graph, Union Find]|||Java|2| +|261|[261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)|Medium|[BFS, DFS, Graph, Union Find]|||Java|3| +|399|[399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%20Division.java)|Medium|[BFS, DFS, Graph, Union Find]|||Java|4| +|N/A|[Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)|Medium|[BFS, DFS, Matrix DFS, Union Find]|||Java|5| +|200|[200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)|Medium|[BFS, DFS, Matrix DFS, Union Find]|O(n)|O(n)|Java|6| +|N/A|[Redundant Connection II.java](https://github.com/awangdev/LintCode/blob/master/Java/Redundant%20Connection%20II.java)|Hard|[DFS, Graph, Tree, Union Find]|||Java|7| +|721|[721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)|Medium|[DFS, Hash Table, Union Find]|||Java|8| +|[tool]|[[tool]. UnionFind.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20UnionFind.java)|Medium|[Lint, Union Find]|O(n), with Path Compression O(mN), with Union by Rank O(logN)|O(n)|Java|9| +|N/A|[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|[Union Find]|||Java|10| +|N/A|[Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)|Medium|[Union Find]|||Java|11| +|N/A|[Connecting Graph III.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20III.java)|Medium|[Union Find]|||Java|12| +|N/A|[Bricks Falling When Hit.java](https://github.com/awangdev/LintCode/blob/master/Java/Bricks%20Falling%20When%20Hit.java)|Hard|[Union Find]|||Java|13| +|N/A|[Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)|Medium|[Union Find]|||Java|14| +|305|[305. Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/305.%20Number%20of%20Islands%20II.java)|Hard|[Union Find]|O(k * log(mn))|O(mn)|Java|15| + + + + + + +## Enumeration (15) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Rotate Image.java](https://github.com/awangdev/LintCode/blob/master/Java/Rotate%20Image.java)|Medium|[Array, Enumeration]|||Java|0| +|N/A|[Spiral Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Spiral%20Matrix.java)|Medium|[Array, Enumeration]|||Java|1| +|621|[621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)|Medium|[Array, Enumeration, Greedy, PriorityQueue, Queue]|O(n)|O(1)|Java|2| +|N/A|[Next Closest Time.java](https://github.com/awangdev/LintCode/blob/master/Java/Next%20Closest%20Time.java)|Medium|[Basic Implementation, Enumeration, String]|||Java|3| +|N/A|[Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)|Medium|[DFS, Enumeration, Math, Sequence DFS]|||Java|4| +|N/A|[Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)|Medium|[DP, Enumeration, Heap, Math, PriorityQueue]|O(n)|O(n)|Java|5| +|639|[639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%20Ways%20II.java)|Hard|[DP, Enumeration, Partition DP]|O(n)|O(n)|Java|6| +|N/A|[Majority Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Majority%20Number%20II.java)|Medium|[Enumeration, Greedy]|||Java|7| +|36|[36. Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/36.%20Valid%20Sudoku.java)|Easy|[Enumeration, Hash Table]|(mn)|(mn)|Java|8| +|246|[246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)|Easy|[Enumeration, Hash Table, Math, Two Pointers]|O(n)|O(1)|Java|9| +|273|[273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%20to%20English%20Words.java)|Hard|[Enumeration, Math, String]|O(n)|O(1)|Java|10| +|65|[65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)|Hard|[Enumeration, Math, String]|O(n)|O(1)|Java|11| +|158|[158. Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/158.%20Read%20N%20Characters%20Given%20Read4%20II%20-%20Call%20multiple%20times.java)|Hard|[Enumeration, String]|O(n)|O(n)|Java|12| +|68|[68. Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/68.%20Text%20Justification.java)|Hard|[Enumeration, String]|O(n) go over words|O(maxLength) buffer list|Java|13| +|157|[157. Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/157.%20Read%20N%20Characters%20Given%20Read4.java)|Easy|[Enumeration, String]|||Java|14| + + + + + + +## Memoization (15) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Triangles.java](https://github.com/awangdev/LintCode/blob/master/Java/Triangles.java)|Medium|[Array, Coordinate DP, DFS, DP, Memoization]|||Java|0| +|N/A|[Longest Increasing Continuous subsequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Continuous%20subsequence%20II.java)|Medium|[Array, Coordinate DP, DP, Memoization]|||Java|1| +|N/A|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|[Array, DP, Game Theory, Interval DP, Memoization]|||Java|2| +|N/A|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|[Array, DP, Game Theory, Memoization, MiniMax]|||Java|3| +|322|[322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)|Medium|[Backpack DP, DFS, DP, Memoization]|O(n * S)|O(S)|Java|4| +|140|[140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)|Hard|[Backtracking, DFS, DP, Hash Table, Memoization]|O(n!)|O(n!)|Java|5| +|N/A|[Longest Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Subsequence.java)|Medium|[Binary Search, Coordinate DP, DP, Memoization]|O(n^2) dp, O(nLogN) binary search|O(n)|Java|6| +|N/A|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|||Java|7| +|1043|[1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)|Medium|[DFS, DP, Graph, Memoization]|O(n), calc memo[n]|O(n)|Java|8| +|516|[516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%20Palindromic%20Subsequence.java)|Medium|[DFS, DP, Interval DP, Memoization]|O(n^2)|O(n^2)|Java|9| +|1216|[1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)|Hard|[DFS, DP, Memoization, String]|O(n^2)|O(n^2)|Java|10| +|N/A|[Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)|Hard|[DP, Divide and Conquer, Interval DP, Memoization]|||Java|11| +|509|[509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.java)|Easy|[DP, Math, Memoization]|||Java|12| +|70|[70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%20Stairs.java)|Easy|[DP, Memoization, Sequence DP]|||Java|13| +|170|[170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)|Easy|[Design, Hash Table, Memoization]|O(n)|O(n)|Java|14| + + + + + + +## Binary Tree (14) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|987|[987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)|Medium|[BFS, Binary Tree, DFS, Hash Table, Tree]|||Java|0| +|N/A|[Search Range in Binary Search Tree .java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Range%20in%20Binary%20Search%20Tree%20.java)|Medium|[BST, Binary Tree]|||Java|1| +|257|[257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%20Tree%20Paths.java)|Easy|[Backtracking, Binary Tree, DFS]|O(n)|O(nlogn)|Java|2| +|114|[114. Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/114.%20Flatten%20Binary%20Tree%20to%20Linked%20List.java)|Medium|[Binary Tree, DFS]|O(n)|O(n), stacks|Java|3| +|N/A|[Segment Tree Modify.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Modify.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|4| +|N/A|[Segment Tree Query II.java](https://github.com/awangdev/LintCode/blob/master/Java/Segment%20Tree%20Query%20II.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|5| +|[lint]|[[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Query.java)|Medium|[Binary Tree, DFS, Divide and Conquer, Lint, Segment Tree]|||Java|6| +|N/A|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|||Java|7| +|N/A|[Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)|Hard|[Binary Tree, DFS, Expression Tree, Stack]|||Java|8| +|N/A|[Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)|Hard|[Binary Tree, DFS, Expression Tree, Stack]|||Java|9| +|[lint]|[[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build%20II.java)|Medium|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|||Java|10| +|[lint]|[[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)|Medium|[Binary Tree, Divide and Conquer, Lint, Segment Tree]|||Java|11| +|N/A|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|||Java|12| +|N/A|[Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)|Hard|[Binary Tree, Expression Tree, Minimum Binary Tree, Stack]|||Java|13| + + + + + + +## PreSum (13) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Maximum Average Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20II.java)|Review|[Array, Binary Search, PreSum]|||Java|0| +|53|[53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)|Easy|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|O(n)|O(n), O(1) rolling array|Java|1| +|N/A|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|||Java|2| +|[lint]|[[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)|Easy|[Array, Hash Table, Lint, PreSum, Subarray]|O(n)|O(n)|Java|3| +|N/A|[Submatrix Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Submatrix%20Sum.java)|Medium|[Array, Hash Table, PreSum]|||Java|4| +|560|[560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)|Medium|[Array, Hash Table, PreSum, Subarray]|O(n)|O(n)|Java|5| +|724|[724. Find Pivot Index.java](https://github.com/awangdev/LintCode/blob/master/Java/724.%20Find%20Pivot%20Index.java)|Easy|[Array, PreSum]|O(n)|O(1)|Java|6| +|327|[327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)|Hard|[BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree]|O(nlogn)|O(n)|Java|7| +|523|[523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)|Medium|[Coordinate DP, DP, Math, PreSum, Subarray]|O(n)|O(k)|Java|8| +|303|[303. Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/303.%20Range%20Sum%20Query%20-%20Immutable.java)|Easy|[DP, PreSum]|O(1) query, O(n) setup|O(n)|Java|9| +|304|[304. Range Sum Query 2D - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/304.%20Range%20Sum%20Query%202D%20-%20Immutable.java)|Medium|[DP, PreSum]|O(mn) build, O(1) query|O(mn)|Java|10| +|N/A|[Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)|Medium|[Hash Table, PreSum, Subarray]|O(n)|O(n)|Java|11| +|N/A|[Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)|Medium|[PreSum, PriorityQueue, Sort, Subarray]|O(nlogn)|O(n)|Java|12| + + + + + + +## Sliding Window (13) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|674|[674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)|Easy|[Array, Coordinate DP, DP, Sliding Window]|O(n)|O(1)|Java|0| +|1040|[1040. Moving Stones Until Consecutive II.java](https://github.com/awangdev/LintCode/blob/master/Java/1040.%20Moving%20Stones%20Until%20Consecutive%20II.java)|Medium|[Array, Sliding Window]|O(nlogn)|O(n)|Java|1| +|727|[727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)|Hard|[DP, Hash Table, Sliding Window, String, Two Pointers]|O(n^2)|O(1)|Java|2| +|239|[239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)|Hard|[Deque, Heap, Sliding Window]|O(n)|O(n)|Java|3| +|N/A|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|||Java|4| +|346|[346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)|Easy|[Design, Queue, Sliding Window]|O(1) for `next()`|O(size) for fixed storage|Java|5| +|340|[340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)|Hard|[Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers]|O(n)|O(k)|Java|6| +|76|[76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)|Hard|[Hash Table, Sliding Window, String, Two Pointers]|O(n)|O(1)|Java|7| +|159|[159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)|Medium|[Hash Table, Sliding Window, String, Two Pointers]|O(n)|O(1)|Java|8| +|438|[438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)|Medium|[Hash Table, Sliding Window, Two Pointers]|O(n)|O(1)|Java|9| +|632|[632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)|Hard|[Hash Table, Sliding Window, Two Pointers]|O(nlogn), n = total elements|O(n) to store sorted list|Java|10| +|567|[567. Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/567.%20Permutation%20in%20String.java)|Medium|[Sliding Window, Two Pointers]|O(m + n)|O(1)|Java|11| +|1004|[1004. Max Consecutive Ones III.java](https://github.com/awangdev/LintCode/blob/master/Java/1004.%20Max%20Consecutive%20Ones%20III.java)|Medium|[Sliding Window, Two Pointers]|O(n)|O(1)|Java|12| + + + + + + +## Trie (11) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Word Search II.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Search%20II.java)|Hard|[Backtracking, DFS, Trie]|||Java|0| +|211|[211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)|Medium|[Backtracking, Design, Trie]|O(n) to search and to add word|< O(mn), depends on the input. m = # of words|Java|1| +|N/A|[Word Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Word%20Squares.java)|Hard|[Backtracking, Trie]|||Java|2| +|N/A|[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|[Bit Manipulation, Trie]|||Java|3| +|N/A|[K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)|Hard|[DP, Double Sequence DP, Sequence DP, Trie]|||Java|4| +|N/A|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words|O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map`|Java|5| +|208|[208. Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/208.%20Implement%20Trie%20(Prefix%20Tree).java)|Medium|[Design, Trie]|||Java|6| +|692|[692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|O(n)|O(n)|Java|7| +|N/A|[Palindrome Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Pairs.java)|Hard|[Hash Table, String, Trie]|||Java|8| +|720|[720. Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/720.%20Longest%20Word%20in%20Dictionary.java)|Easy|[Hash Table, Trie]|O(nlogn)|O(n)|Java|9| +|745|[745. Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/745.%20Prefix%20and%20Suffix%20Search.java)|Hard|[Trie]|O(N + Q)|O(N)|Java|10| + + + + + + +## MinHeap (11) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Trapping Rain Water II.java](https://github.com/awangdev/LintCode/blob/master/Java/Trapping%20Rain%20Water%20II.java)|Hard|[BFS, Heap, MinHeap, PriorityQueue]|||Java|0| +|N/A|[Design Search Autocomplete System.java](https://github.com/awangdev/LintCode/blob/master/Java/Design%20Search%20Autocomplete%20System.java)|Hard|[Design, Hash Table, MinHeap, PriorityQueue, Trie]|input: O(x), where x = possible words, constructor: O(mn) m = max length, n = # of words|O(n^2), n = # of possible words, n = # of trie levels; mainlay saving the `Map`|Java|1| +|295|[295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)|Hard|[Design, Heap, MaxHeap, MinHeap]|O(1) get, O(logn) addNum|O(n)|Java|2| +|N/A|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|||Java|3| +|215|[215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort]|O(nlogk)|O(k)|Java|4| +|N/A|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|||Java|5| +|347|[347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|O(n)|O(n)|Java|6| +|692|[692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|O(n)|O(n)|Java|7| +|[lint]|[[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)|Medium|[HashHeap, Heap, Lint, MinHeap]|||Java|8| +|373|[373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)|Medium|[Heap, MaxHeap, MinHeap]|O(klogk)|O(k)|Java|9| +|[lint]|[[lint]. Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Merge%20k%20Sorted%20Arrays.java)|Medium|[Heap, MinHeap, PriorityQueue]|O(nlogk)|O(k)|Java|10| + + + + + + +## Subarray (11) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Minimum Size Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Size%20Subarray%20Sum.java)|Medium|[Array, Binary Search, Subarray, Two Pointers]|O(n)|O(1)|Java|0| +|53|[53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)|Easy|[Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray]|O(n)|O(n), O(1) rolling array|Java|1| +|N/A|[Maximum Subarray II.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20II.java)|Medium|[Array, DP, Greedy, PreSum, Sequence DP, Subarray]|||Java|2| +|N/A|[Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)|Easy|[Array, DP, Greedy, Sequence DP, Subarray]|O(m)|O(1)|Java|3| +|152|[152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)|Medium|[Array, DP, PreProduct, Subarray]|O(n)|O(1)|Java|4| +|[lint]|[[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)|Easy|[Array, Hash Table, Lint, PreSum, Subarray]|O(n)|O(n)|Java|5| +|560|[560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)|Medium|[Array, Hash Table, PreSum, Subarray]|O(n)|O(n)|Java|6| +|N/A|[Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)|Easy|[Array, Subarray]|O(n)|O(1)|Java|7| +|523|[523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)|Medium|[Coordinate DP, DP, Math, PreSum, Subarray]|O(n)|O(k)|Java|8| +|N/A|[Maximum Size Subarray Sum Equals k.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Size%20Subarray%20Sum%20Equals%20k.java)|Medium|[Hash Table, PreSum, Subarray]|O(n)|O(n)|Java|9| +|N/A|[Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)|Medium|[PreSum, PriorityQueue, Sort, Subarray]|O(nlogn)|O(n)|Java|10| + + + + + + +## Backpack DP (8) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Combination Sum IV.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20IV.java)|Medium|[Array, Backpack DP, DP]|||Java|0| +|322|[322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)|Medium|[Backpack DP, DFS, DP, Memoization]|O(n * S)|O(S)|Java|1| +|N/A|[Backpack VI.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20VI.java)|Medium|[Backpack DP, DP]|||Java|2| +|N/A|[Backpack V.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20V.java)|Medium|[Backpack DP, DP]|||Java|3| +|N/A|[Backpack II.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20II.java)|Medium|[Backpack DP, DP]|||Java|4| +|N/A|[Backpack.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack.java)|Medium|[Backpack DP, DP]|||Java|5| +|N/A|[Backpack III.java](https://github.com/awangdev/LintCode/blob/master/Java/Backpack%20III.java)|Hard|[Backpack DP, DP]|||Java|6| +|518|[518. Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/518.%20Coin%20Change%202.java)|Medium|[Backpack DP, DP]|O(n)|O(n)|Java|7| + + + + + + +## Status DP (8) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[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|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(n), O(1) rolling array|Java|0| +|122|[122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)|Easy|[Array, DP, Greedy, Sequence DP, Status DP]|O(n)|O(1) greedy, O(n) dp|Java|1| +|N/A|[Minimum Swaps To Make Sequences Increasing.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Swaps%20To%20Make%20Sequences%20Increasing.java)|Medium|[Coordinate DP, DP, Status DP]|||Java|2| +|N/A|[House Robber III.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20III.java)|Medium|[DFS, DP, Status DP, Tree]|||Java|3| +|N/A|[House Robber II.java](https://github.com/awangdev/LintCode/blob/master/Java/House%20Robber%20II.java)|Medium|[DP, Sequence DP, Status DP]|||Java|4| +|198|[198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)|Easy|[DP, Sequence DP, Status DP]|O(n)|O(n) or rolling array O(1)|Java|5| +|256|[256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)|Easy|[DP, Sequence DP, Status DP]|O(nm), m = # of colors|O(nm), or O(1) with rolling array|Java|6| +|265|[265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)|Hard|[DP, Sequence DP, Status DP]|O(NK^2):|O(K) with rolling array|Java|7| + + + + + + +## Sweep Line (7) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|[Array, Interval, PriorityQueue, Sort, Sweep Line]|||Java|0| +|56|[56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)|Medium|[Array, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|1| +|57|[57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)|Hard|[Array, PriorityQueue, Sort, Sweep Line]|O(n)|O(n)|Java|2| +|218|[218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)|Hard|[BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line]|O(n^2logn)|O(n)|Java|3| +|253|[253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)|Medium|[Greedy, Heap, PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(n)|Java|4| +|252|[252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)|Easy|[PriorityQueue, Sort, Sweep Line]|O(nlogn)|O(1)|Java|5| +|850|[850. Rectangle Area II.java](https://github.com/awangdev/LintCode/blob/master/Java/850.%20Rectangle%20Area%20II.java)|Hard|[Segment Tree, Sweep Line]|O(n^2)|O(n)|Java|6| + + + + + + +## Quick Sort (7) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)|Easy|[Array, Lint, Quick Select, Quick Sort, Two Pointers]|O(n)|O(logN)|Java|0| +|N/A|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|[Array, Partition, Quick Sort, Sort, Two Pointers]|||Java|1| +|N/A|[Partition Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Partition%20Array.java)|Medium|[Array, Quick Sort, Sort, Two Pointers]|||Java|2| +|215|[215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort]|O(nlogk)|O(k)|Java|3| +|N/A|[Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Sort]|||Java|4| +|N/A|[Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)|Medium|[Partition, Quick Sort, Sort, Two Pointers]|||Java|5| +|N/A|[QuickSort.java](https://github.com/awangdev/LintCode/blob/master/Java/QuickSort.java)|Medium|[Quick Sort, Sort]|||Java|6| + + + + + + +## Double Sequence DP (6) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Wildcard Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/Wildcard%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Greedy, Sequence DP, String]|||Java|0| +|10|[10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)|Hard|[Backtracking, DP, Double Sequence DP, Sequence DP, String]|||Java|1| +|N/A|[Longest Common Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Subsequence.java)|Medium|[DP, Double Sequence DP, Sequence DP]|||Java|2| +|N/A|[Longest Common Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Common%20Substring.java)|Medium|[DP, Double Sequence DP, Sequence DP, String]|||Java|3| +|72|[72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)|Hard|[DP, Double Sequence DP, Sequence DP, String]|O(MN)||Java|4| +|N/A|[K Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Edit%20Distance.java)|Hard|[DP, Double Sequence DP, Sequence DP, Trie]|||Java|5| + + + + + + +## Topological Sort (6) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|269|[269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)|Hard|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n), n = # of graph edges|O(n)|Java|0| +|207|[207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)|Medium|[BFS, Backtracking, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|1| +|1203|[1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)|Hard|[BFS, DFS, Graph, Topological Sort]|O(V + E) to traverse the graph, #nodes + #edges|O(V + E)|Java|2| +|210|[210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)|Medium|[BFS, DFS, Graph, Topological Sort]|O(n)|O(n)|Java|3| +|[tool]|[[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)|Medium|[BFS, DFS, Lint, Topological Sort]|O(V + E)|O(V + E)|Java|4| +|N/A|[Longest Increasing Path in a Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Longest%20Increasing%20Path%20in%20a%20Matrix.java)|Hard|[Coordinate DP, DFS, DP, Memoization, Topological Sort]|||Java|5| + + + + + + +## Merge Sort (5) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|327|[327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)|Hard|[BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree]|O(nlogn)|O(n)|Java|0| +|493|[493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)|Medium|[BST, Binary Indexed Tree, Divide and Conquer, Merge Sort, Segment Tree]|||Java|1| +|23|[23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)|Medium|[Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue]|O(nlogk)|O(logk)|Java|2| +|N/A|[Sort List.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20List.java)|Medium|[Divide and Conquer, Linked List, Merge Sort, Sort]|||Java|3| +|[tool]|[[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)|Medium|[Lint, Merge Sort, Sort]|O(mlogn)|O(n)|Java|4| + + + + + + +## Permutation (5) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|1053|[1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)|Medium|[Array, Greedy, Permutation]|O(n)|O(1)|Java|0| +|31|[31. Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/31.%20Next%20Permutation.java)|Medium|[Array, Permutation]|O(n)|O(1)|Java|1| +|46|[46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)|Medium|[BFS, Backtracking, DFS, Permutation]|O(n!)|O(n!)|Java|2| +|N/A|[Palindrome Permutation II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Permutation%20II.java)|Medium|[Backtracking, Permutation]|||Java|3| +|N/A|[Shuffle an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/Shuffle%20an%20Array.java)|Medium|[Permutation]|||Java|4| + + + + + + +## Partition DP (5) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Perfect Squares.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Squares.java)|Medium|[BFS, DP, Math, Partition DP]|||Java|0| +|N/A|[Copy Books.java](https://github.com/awangdev/LintCode/blob/master/Java/Copy%20Books.java)|Hard|[Binary Search, DP, Partition DP]|||Java|1| +|639|[639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%20Ways%20II.java)|Hard|[DP, Enumeration, Partition DP]|O(n)|O(n)|Java|2| +|N/A|[Palindrome Partitioning II.java](https://github.com/awangdev/LintCode/blob/master/Java/Palindrome%20Partitioning%20II.java)|Hard|[DP, Partition DP]|||Java|3| +|91|[91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)|Medium|[DP, Partition DP, String]|O(n)|O(n)|Java|4| + + + + + + +## MaxHeap (5) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|295|[295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)|Hard|[Design, Heap, MaxHeap, MinHeap]|O(1) get, O(logn) addNum|O(n)|Java|0| +|N/A|[Sliding Window Median.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Window%20Median.java)|Hard|[Design, Heap, MaxHeap, MinHeap, Sliding Window]|||Java|1| +|347|[347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue]|O(n)|O(n)|Java|2| +|692|[692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)|Medium|[Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie]|O(n)|O(n)|Java|3| +|373|[373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)|Medium|[Heap, MaxHeap, MinHeap]|O(klogk)|O(k)|Java|4| + + + + + + +## Expression Tree (5) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|||Java|0| +|N/A|[Convert Expression to Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Polish%20Notation.java)|Hard|[Binary Tree, DFS, Expression Tree, Stack]|||Java|1| +|N/A|[Convert Expression to Reverse Polish Notation.java](https://github.com/awangdev/LintCode/blob/master/Java/Convert%20Expression%20to%20Reverse%20Polish%20Notation.java)|Hard|[Binary Tree, DFS, Expression Tree, Stack]|||Java|2| +|N/A|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|||Java|3| +|N/A|[Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)|Hard|[Binary Tree, Expression Tree, Minimum Binary Tree, Stack]|||Java|4| + + + + + + +## TreeMap (5) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|1146|[1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)|Medium|[Array, Hash Table, TreeMap]|O(1) set, O(logn) get, O(x) snap, x = # of changes|O(n * m), n = array size, m = # of snaps|Java|0| +|N/A|[My Calendar I.java](https://github.com/awangdev/LintCode/blob/master/Java/My%20Calendar%20I.java)|Medium|[Array, TreeMap]|||Java|1| +|981|[981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)|Medium|[Binary Search, Hash Table, TreeMap]|set O(1), get(logn)|O(n)|Java|2| +|716|[716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)|Medium|[Design, Doubly Linked List, Stack, TreeMap]|avg O(1), [O(logN) peekMax(), TreeMap]; [O(n) popMax(), TwoStack]|O(n)|Java|3| +|855|[855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)|Medium|[PriorityQueue, Sort, TreeMap, TreeSet]|O(logn)|O(n)|Java|4| + + + + + + +## Game Theory (4) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|[Array, DP, Game Theory, Interval DP, Memoization]|||Java|0| +|N/A|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|[Array, DP, Game Theory, Memoization, MiniMax]|||Java|1| +|N/A|[Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)|Easy|[Brainteaser, DP, Game Theory]|||Java|2| +|N/A|[Coins in a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line.java)|Medium|[DP, Game Theory, Greedy]|||Java|3| + + + + + + +## Combination (4) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Combination Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Combination%20Sum%20III.java)|Medium|[Array, Backtracking, Combination, DFS]|||Java|0| +|39|[39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)|Medium|[Array, Backtracking, Combination, DFS]|O(k * 2^n), k = avg rst length|O(k) stack depth, if not counting result size|Java|1| +|40|[40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%20Sum%20II.java)|Medium|[Array, Backtracking, Combination, DFS]|O(k * 2^n), k = avg rst length|O(n) stack depth, if not counting result size|Java|2| +|N/A|[Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/Combinations.java)|Medium|[Backtracking, Combination, DFS]|||Java|3| + + + + + + +## TreeSet (4) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[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|[Array, BST, Binary Search, DP, Queue, TreeSet]|||Java|0| +|N/A|[K Empty Slots.java](https://github.com/awangdev/LintCode/blob/master/Java/K%20Empty%20Slots.java)|Hard|[Array, BST, TreeSet]|||Java|1| +|855|[855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)|Medium|[PriorityQueue, Sort, TreeMap, TreeSet]|O(logn)|O(n)|Java|2| +|715|[715. Range Module.java](https://github.com/awangdev/LintCode/blob/master/Java/715.%20Range%20Module.java)|Hard|[Segment Tree, TreeSet]|query O(logn), update O(n)|O(n)|Java|3| + + + + + + +## Interval DP (4) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Coins in a Line III.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20III.java)|Hard|[Array, DP, Game Theory, Interval DP, Memoization]|||Java|0| +|516|[516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%20Palindromic%20Subsequence.java)|Medium|[DFS, DP, Interval DP, Memoization]|O(n^2)|O(n^2)|Java|1| +|N/A|[Burst Balloons.java](https://github.com/awangdev/LintCode/blob/master/Java/Burst%20Balloons.java)|Hard|[DP, Divide and Conquer, Interval DP, Memoization]|||Java|2| +|N/A|[Scramble String.java](https://github.com/awangdev/LintCode/blob/master/Java/Scramble%20String.java)|Hard|[DP, Interval DP, String]|||Java|3| + + + + + + +## Binary Indexed Tree (4) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|315|[315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%20of%20Smaller%20Numbers%20After%20Self.java)|Hard|[BST, Binary Indexed Tree, Binary Search, Divide and Conquer, Segment Tree]|O(nlogn)|O(n)|Java|0| +|493|[493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)|Medium|[BST, Binary Indexed Tree, Divide and Conquer, Merge Sort, Segment Tree]|||Java|1| +|308|[308. Range Sum Query 2D - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/308.%20Range%20Sum%20Query%202D%20-%20Mutable.java)|Hard|[Binary Indexed Tree, Segment Tree]|build(n), update(logn), rangeRuery(logn + k)|O(n)|Java|2| +|307|[307. Range Sum Query - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/307.%20Range%20Sum%20Query%20-%20Mutable.java)|Medium|[Binary Indexed Tree, Segment Tree]|build O(n), query (logn +k), update O(logn)|O(n)|Java|3| + + + + + + +## Bucket Sort (4) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|448|[448. Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/448.%20Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)|Easy|[Array, Bucket Sort]|O(n)|O(1)|Java|0| +|1048|[1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)|Medium|[Bucket Sort, DP, Hash Table, Sort]|O(n)|O(n)|Java|1| +|1057|[1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)|Medium|[Bucket Sort, Greedy, PriorityQueue, Sort]|O(mn)|O(mn)|Java|2| +|274|[274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.H-Index.java)|Medium|[Bucket Sort, Hash Table, Sort]|O(n)|O(n)|Java|3| + + + + + + +## Doubly Linked List (3) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|432|[432. All One Data Structure.java](https://github.com/awangdev/LintCode/blob/master/Java/432.%20All%20One%20Data%20Structure.java)|Hard|[Design, Doubly Linked List]|O(1)|O(n)|Java|0| +|146|[146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)|Medium|[Design, Doubly Linked List, Hash Table, Linked List]|O(1)|O(1)|Java|1| +|716|[716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)|Medium|[Design, Doubly Linked List, Stack, TreeMap]|avg O(1), [O(logN) peekMax(), TreeMap]; [O(n) popMax(), TwoStack]|O(n)|Java|2| + + + + + + +## HashHeap (3) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|218|[218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)|Hard|[BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line]|O(n^2logn)|O(n)|Java|0| +|[lint]|[[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)|Hard|[HashHeap, Heap, Lint]|||Java|1| +|[lint]|[[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)|Medium|[HashHeap, Heap, Lint, MinHeap]|||Java|2| + + + + + + +## Cycle Detection (3) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|287|[287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)|Medium|[Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|0| +|142|[142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)|Medium|[Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|1| +|141|[141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)|Easy|[Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|2| + + + + + + +## Minimum Binary Tree (3) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Expression Evaluation.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Evaluation.java)|Hard|[Binary Tree, DFS, Expression Tree, Minimum Binary Tree, Stack]|||Java|0| +|N/A|[Basic Calculator.java](https://github.com/awangdev/LintCode/blob/master/Java/Basic%20Calculator.java)|Hard|[Binary Tree, Expression Tree, Math, Minimum Binary Tree, Stack]|||Java|1| +|N/A|[Expression Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/Expression%20Tree%20Build.java)|Hard|[Binary Tree, Expression Tree, Minimum Binary Tree, Stack]|||Java|2| + + + + + + +## MiniMax (3) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Coins in a Line II.java](https://github.com/awangdev/LintCode/blob/master/Java/Coins%20in%20a%20Line%20II.java)|Medium|[Array, DP, Game Theory, Memoization, MiniMax]|||Java|0| +|N/A|[Predict the Winner.java](https://github.com/awangdev/LintCode/blob/master/Java/Predict%20the%20Winner.java)|Medium|[DP, MiniMax]|||Java|1| +|843|[843. Guess the Word.java](https://github.com/awangdev/LintCode/blob/master/Java/843.%20Guess%20the%20Word.java)|Hard|[MiniMax]|TODO|TODO|Java|2| + + + + + + +## NestedInteger (3) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|339|[339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)|Easy|[BFS, DFS, NestedInteger]|O(n)|O(h), h = levels|Java|0| +|364|[364. Nested List Weight Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/364.%20Nested%20List%20Weight%20Sum%20II.java)|Medium|[DFS, NestedInteger]|O(n), visit all nodes|O(h), depth|Java|1| +|341|[341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)|Medium|[Design, NestedInteger, Stack]|O(n)|O(n)|Java|2| + + + + + + +## Queue (3) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[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|[Array, BST, Binary Search, DP, Queue, TreeSet]|||Java|0| +|621|[621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)|Medium|[Array, Enumeration, Greedy, PriorityQueue, Queue]|O(n)|O(1)|Java|1| +|346|[346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)|Easy|[Design, Queue, Sliding Window]|O(1) for `next()`|O(size) for fixed storage|Java|2| + + + + + + +## Monotonous Stack (3) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Largest Rectangle in Histogram.java](https://github.com/awangdev/LintCode/blob/master/Java/Largest%20Rectangle%20in%20Histogram.java)|Hard|[Array, Monotonous Stack, Stack]|||Java|0| +|402|[402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)|Medium|[Greedy, Monotonous Stack, Stack]|O(n)|O(n)|Java|1| +|739|[739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)|Medium|[Hash Table, Monotonous Stack, Stack]|O(n)|O(n)|Java|2| + + + + + + +## Partition (3) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Sort Colors.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors.java)|Medium|[Array, Partition, Quick Sort, Sort, Two Pointers]|||Java|0| +|N/A|[Sort Colors II.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Colors%20II.java)|Medium|[Partition, Quick Sort, Sort, Two Pointers]|||Java|1| +|N/A|[Sort Letters by Case.java](https://github.com/awangdev/LintCode/blob/master/Java/Sort%20Letters%20by%20Case.java)|Medium|[Partition, Sort, String, Two Pointers]|||Java|2| + + + + + + +## Slow Fast Pointer (3) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|287|[287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)|Medium|[Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|0| +|142|[142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)|Medium|[Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|1| +|141|[141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)|Easy|[Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|2| + + + + + + +## Sequence DFS (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|22|[22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%20Parentheses.java)|Medium|[Backtracking, DFS, Sequence DFS, String]|O(2^n)|O(2^n)|Java|0| +|N/A|[Strobogrammatic Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Strobogrammatic%20Number%20II.java)|Medium|[DFS, Enumeration, Math, Sequence DFS]|||Java|1| + + + + + + +## Double Recursive (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Binary Tree Longest Consecutive Sequence II.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Tree%20Longest%20Consecutive%20Sequence%20II.java)|Medium|[DFS, Divide and Conquer, Double Recursive, Tree]|||Java|0| +|N/A|[Path Sum III.java](https://github.com/awangdev/LintCode/blob/master/Java/Path%20Sum%20III.java)|Easy|[DFS, Double Recursive, Tree]|||Java|1| + + + + + + +## Moore Voting (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|169|[169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)|Easy|[Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort]|O(n)|O(1)|Java|0| +|229|[229. Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/229.%20Majority%20Element%20II.java)|Medium|[Array, Moore Voting]|O(n)|(1)|Java|1| + + + + + + +## Recursion (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|938|[938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)|Easy|[BST, Recursion, Tree]|||Java|0| +|698|[698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)|Medium|[DFS, DP, Recursion]|O(k^(n-k) * k!)|O(n)|Java|1| + + + + + + +## Brainteaser (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Nim Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Nim%20Game.java)|Easy|[Brainteaser, DP, Game Theory]|||Java|0| +|319|[319. Bulb Switcher.java](https://github.com/awangdev/LintCode/blob/master/Java/319.%20Bulb%20Switcher.java)|Medium|[Brainteaser, Math]|O(1)|O(1)|Java|1| + + + + + + +## Matrix DFS (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Surrounded Regions.java](https://github.com/awangdev/LintCode/blob/master/Java/Surrounded%20Regions.java)|Medium|[BFS, DFS, Matrix DFS, Union Find]|||Java|0| +|200|[200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)|Medium|[BFS, DFS, Matrix DFS, Union Find]|O(n)|O(n)|Java|1| + + + + + + +## Quick Select (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)|Easy|[Array, Lint, Quick Select, Quick Sort, Two Pointers]|O(n)|O(logN)|Java|0| +|215|[215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)|Medium|[Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort]|O(nlogk)|O(k)|Java|1| + + + + + + +## PreProduct (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|152|[152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)|Medium|[Array, DP, PreProduct, Subarray]|O(n)|O(1)|Java|0| +|238|[238. Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/238.%20Product%20of%20Array%20Except%20Self.java)|Medium|[Array, PreProduct]|O(n)|O(1)|Java|1| + + + + + + +## BIT (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|218|[218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)|Hard|[BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line]|O(n^2logn)|O(n)|Java|0| +|327|[327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)|Hard|[BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree]|O(nlogn)|O(n)|Java|1| + + + + + + +## Deque (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|297|[297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%20and%20Deserialize%20Binary%20Tree.java)|Hard|[BFS, DFS, Deque, Design, Divide and Conquer, Tree]|O(n)|O(n)|Java|0| +|239|[239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)|Hard|[Deque, Heap, Sliding Window]|O(n)|O(n)|Java|1| + + + + + + +## Geometry (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|149|[149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%20Points%20on%20a%20Line.java)|Hard|[Array, Geometry, Hash Table, Math]|O(n^2)|O()|Java|0| +|N/A|[Perfect Rectangle.java](https://github.com/awangdev/LintCode/blob/master/Java/Perfect%20Rectangle.java)|Hard|[Design, Geometry, Hash Table]|||Java|1| + + + + + + +## Edge Case (2) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|41|[41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)|Hard|[Analysis, Array, Edge Case]|O(n)|O(1)|Java|0| +|686|[686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)|Easy|[Basic Implementation, Edge Case, String]|||Java|1| + + + + + + +## PQ (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|743|[743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)|Medium|[BFS, DFS, Graph, Heap, PQ]|O(nlogn)|O(n)|Java|0| + + + + + + +## LinkedHashMap (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|340|[340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)|Hard|[Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers]|O(n)|O(k)|Java|0| + + + + + + +## List (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|118|[118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)|Easy|[Array, Basic Implementation, List]|O(n^2) based on pascal triangle size|O(n^2)|Java|0| + + + + + + +## Garph (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|785|[785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)|Medium|[BFS, DFS, Garph]|O(n)|O(n)|Java|0| + + + + + + +## KMP (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Shortest Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/Shortest%20Palindrome.java)|Hard|[KMP, String]|||Java|0| + + + + + + +## Binary Search on Value (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|287|[287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)|Medium|[Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers]|O(n)|O(1)|Java|0| + + + + + + +## Analysis (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|41|[41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)|Hard|[Analysis, Array, Edge Case]|O(n)|O(1)|Java|0| + + + + + + +## Interval (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Number of Airplane in the sky.java](https://github.com/awangdev/LintCode/blob/master/Java/Number%20of%20Airplane%20in%20the%20sky.java)|Medium|[Array, Interval, PriorityQueue, Sort, Sweep Line]|||Java|0| + + + + + + +## Bitwise DP (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|N/A|[Counting Bits.java](https://github.com/awangdev/LintCode/blob/master/Java/Counting%20Bits.java)|Medium|[Bit Manipulation, Bitwise DP, DP]|||Java|0| + + + + + + +## Semaphore (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|1117|[1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)|Medium|[Lock, Semaphore, Thread]|||Java|0| + + + + + + +## Pruning (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|277|[277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)|Medium|[Adjacency Matrix, Array, Graph, Greedy, Pruning]|O(n)|O(1)|Java|0| + + + + + + +## Reservior Sampling (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|398|[398. Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/398.%20Random%20Pick%20Index.java)|Medium|[Reservior Sampling]|O(n)|O(n) for input int[], O(1) extra space used|Java|0| + + + + + + +## Rotation (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|189|[189. Rotate Array.java](https://github.com/awangdev/LintCode/blob/master/Java/189.%20Rotate%20Array.java)|Easy|[Array, Rotation]|||Java|0| + + + + + + +## Adjacency Matrix (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|277|[277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)|Medium|[Adjacency Matrix, Array, Graph, Greedy, Pruning]|O(n)|O(1)|Java|0| + + + + + + +## Lock (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|1117|[1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)|Medium|[Lock, Semaphore, Thread]|||Java|0| + + + + + + +## Thread (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|1117|[1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)|Medium|[Lock, Semaphore, Thread]|||Java|0| + + + + + + +## Backpack (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|416|[416. Partition Equal Subset Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/416.%20Partition%20Equal%20Subset%20Sum.java)|Medium|[Backpack, DP]|||Java|0| + + + + + + +## Two Stacks (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|145|[145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)|Medium|[Stack, Tree, Two Stacks]|O(n)|O(n)|Java|0| + + + + + + +## Tree DP (1) +| Leetcode# | Problem | Level | Tags | Time | Space | Language | Sequence | +|:---------:|:------------|:------:|:----:|-----:|------:|:--------:|---------:| +|124|[124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)|Hard|[DFS, DP, Tree, Tree DP]|O(n)|O(logn)|Java|0| + + + diff --git a/TagReviewPage.md b/TagReviewPage.md new file mode 100644 index 0000000..c2cfa4d --- /dev/null +++ b/TagReviewPage.md @@ -0,0 +1,33781 @@ + + + +## PQ (1) +**0. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + + + + + + + +## Merge Sort (5) +**0. [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/ + + + +--- + +**1. [[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)** Level: Medium Tags: [Lint, 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) + + + +--- + +**2. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**3. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**4. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + + + + + + + +## Sequence DFS (2) +**0. [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) + + + +--- + +**1. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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 + + + +--- + + + + + + + +## Doubly Linked List (3) +**0. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**1. [432. All One Data Structure.java](https://github.com/awangdev/LintCode/blob/master/Java/432.%20All%20One%20Data%20Structure.java)** Level: Hard Tags: [Design, Doubly Linked List] + + +#### Doubly Linked List +- IMPORTANT: the problem aims to put keys of same frequency in same node! This affects the design of node +- Main a class `Node {keySet, count, last/next pointers}` +- Each operation: + - 1) finds target node and extract the key + - 2) calculate: count +/- 1 + - 3) find new spot to store the key (prior positions or later positions) +- Be careful when handling the cases in inc() and dec() + + + +--- + +**2. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + + + + + + + +## Lint (27) +**0. [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 + + + +--- + +**1. [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 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**2. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**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 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. + + + +--- + +**6. [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. + + + +--- + +**7. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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, 自底向上建立起的。 + + + +--- + +**8. [[lint]. Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Nth%20to%20Last%20Node%20in%20List.java)** Level: Easy Tags: [Linked List, Lint] + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth + + + +--- + +**9. [[lint]. Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array, Lint] + + + + +--- + +**10. [[lint]. Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Compare%20Strings.java)** Level: Easy Tags: [Lint, String] + +看StringA是不是包括所有 StringB的字符. Anagram + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + + + +--- + +**11. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**12. [[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)** Level: Hard Tags: [HashHeap, Heap, Lint] + +非题.是从九章找来的HashHeap implementation. + +#### HashHeap +- An efficient implementation of a priority queue. +- The linear hash function monotonically maps keys to buckets, and each bucket is a heap +- https://xlinux.nist.gov/dads/HTML/hashheap.html + + + +--- + +**13. [[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)** Level: Easy Tags: [Hash Table, Lint, String] + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**14. [[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)** Level: Medium Tags: [Array, Hash Table, Lint] + + +把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. + + + + +--- + +**15. [[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)** Level: Medium Tags: [Array, Lint, Two Pointers] + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**16. [[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)** Level: Easy Tags: [Array, Lint, 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) + + + + +--- + +**17. [[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Lint, 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! + + + + +--- + +**18. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**19. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**20. [[lint]. Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array, Lint] + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**21. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**22. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**23. [[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)** Level: Medium Tags: [Lint, 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) + + + +--- + +**24. [[tool]. Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Hash%20Function.java)** Level: Easy Tags: [Hash Table, Lint] + + +In general, there is no universal recipe to stick to when it comes to implementing hashCode(). +https://www.baeldung.com/java-hashcode + +#### 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会变得太大,所以不能算完和 再 %... + + + +--- + +**25. [[tool]. UnionFind.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20UnionFind.java)** Level: Medium Tags: [Lint, Union Find] + + +#### Method1: Union Find with Array +- union(), find() +- Path Compresion: store skip father after found, which makes find O(1) + +#### Method2: Union Find with HashMap + + + + + +--- + +**26. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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 + + + +--- + + + + + + + +## String (78) +**0. [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了 + + + +--- + +**1. [Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)** Level: Easy Tags: [String, Two Pointers] + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + + + +--- + +**2. [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的要求! + + + +--- + +**3. [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 + + + +--- + +**4. [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 + + + +--- + +**5. [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 + + + +--- + +**6. [Orderly Queue.java](https://github.com/awangdev/LintCode/blob/master/Java/Orderly%20Queue.java)** Level: Hard Tags: [Math, String] + + + + +--- + +**7. [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, 看解答 + + + +--- + +**8. [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 + + + +--- + +**9. [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 + + + +--- + +**10. [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 + + + +--- + +**11. [Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)** Level: Medium Tags: [String] + + + +--- + +**12. [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 +还要做一下那. + + + +--- + +**13. [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' + + + + +--- + +**14. [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一下最后一个词 + + + + +--- + +**15. [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) + + + +--- + +**16. [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 + + + +--- + +**17. [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. + + + + +--- + +**18. [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] + + + + +--- + +**19. [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 一样 + + + +--- + +**20. [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) + + + + +--- + +**21. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**22. [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看起来更容易理解. + + + +--- + +**23. [[HackerRank]. Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/[HackerRank].%20Change%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. + + + +--- + +**24. [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 + + + +--- + +**25. [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 + + + +--- + +**26. [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一下。 + + + +--- + +**27. [[lint]. Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Compare%20Strings.java)** Level: Easy Tags: [Lint, String] + +看StringA是不是包括所有 StringB的字符. Anagram + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + + + +--- + +**28. [[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)** Level: Easy Tags: [Hash Table, Lint, String] + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**29. [[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)** Level: Easy Tags: [Array, Lint, 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) + + + + +--- + +**30. [788. Rotated Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/788.%20Rotated%20Digits.java)** Level: Easy Tags: [Basic Implementation, String] + + +#### Basic Implementation of the rules +- [3,4,7] -> cannot rotate, failures. Must NOT have. set1 +- 2,5,6,9 -> good candidates. Must have 1. set2 +- [0,1,8] -> goes back to itself. can have +- loop over [1, N], count=int[10] appearance. + - set1 meet 0 + - set2 meet at least 1 + + + +--- + +**31. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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 + + + +--- + +**32. [408. Valid Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/408.%20Valid%20Word%20Abbreviation.java)** Level: Easy Tags: [Basic Implementation, String] + +tricky: find integer within a string +edge case: leading '0' should not be allow in such abbr. + + + +--- + +**33. [415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)** Level: Easy Tags: [Basic Implementation, Math, String] + + +#### Two Pointer +- Use i, j to process from end of 2 strings +- handle edge case for i, j + - if i < 0, its num = 0 (since we are doing sum, blindly setting 0 is okay) +- Note: `sb.insert(0, x)` is much slower than doing a final `sb.reverse()` + +#### If manually convertin to int[] +1. when converting to int[], remember to reverse string. +1. when converting to int[], remember to reserve extra space for carry + + + +--- + +**34. [1108. Defanging an IP Address.java](https://github.com/awangdev/LintCode/blob/master/Java/1108.%20Defanging%20an%20IP%20Address.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**35. [383. Ransom Note.java](https://github.com/awangdev/LintCode/blob/master/Java/383.%20Ransom%20Note.java)** Level: Easy Tags: [Basic Implementation, String] + +count chars in int[256] + + + +--- + +**36. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**37. [293. Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/293.%20Flip%20Game.java)** Level: Easy Tags: [String] + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + + + +--- + +**38. [686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)** Level: Easy Tags: [Basic Implementation, Edge Case, String] + +Track: 纸上分析edge case. +Validation helps speed it up. + + + +--- + +**39. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**40. [5. Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/5.%20Longest%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)? + +#### Method1: DP of interval +- Very similar to `216. Longest Palindromic Subsequence`, but this problem requires solid substring(i+1, j-1) to be palindromic +- Similarly: process i = n-1, from end so [i + 1, j] is always ready to consume +- boolean dp[i][j] to mark range (i, j) as palindrome or not. +- 在计算 dp[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- time: O(n^2) dp +- space: O(n^2) + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + + + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**41. [58. Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/58.%20Length%20of%20Last%20Word.java)** Level: Easy Tags: [String] + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + + + +--- + +**42. [824. Goat Latin.java](https://github.com/awangdev/LintCode/blob/master/Java/824.%20Goat%20Latin.java)** Level: Easy Tags: [Basic Implementation, String] + + + + +--- + +**43. [151. Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/151.%20Reverse%20Words%20in%20a%20String.java)** Level: Medium Tags: [String] + + +#### Method1: Split string by space, then flip +- Option1: With `s.split(" ")`: No brain, and super fast +- Option2: With `s.split("\\s+")`, it skips space, but slow. Use sb.insert(0, xxx) +- trim() output +- Time, Space: O(n) + +#### Method2: Flip entire, then individual, two pointer +- flip entire string, then flip each individual string +- Time, Space: O(n) + + + +--- + +**44. [67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%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 + +#### reverse +- Reverse string -> Convert to Integer List, add up -> Convert back to string +- pointer 从前向后, 所以只需要 1个pointer. +- 操作复杂, 如上, 证明可以解决. 没必要reverse. + +#### Incorrect: convert to Integer +把binary换成数字作加法. 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**45. [557. Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/557.%20Reverse%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就好了 + + + +--- + +**46. [1249. Minimum Remove to Make Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1249.%20Minimum%20Remove%20to%20Make%20Valid%20Parentheses.java)** Level: Medium Tags: [Stack, String] + + +#### Stack +- Goal: remove extra '(' or ')' so it is valid. +- Forward thinking: use stack to track '(' and ')', then keep appending partial string to output +- Backward thinking: use stack to filter out false indexes, and remove them in the end + + + + +--- + +**47. [443. String Compression.java](https://github.com/awangdev/LintCode/blob/master/Java/443.%20String%20Compression.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**48. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**49. [158. Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/158.%20Read%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就好了. +- alaternatively: use queue to hold so we do not need to worry about size + + + +--- + +**50. [43. Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/43.%20Multiply%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]. + - Loop over num1, each row num1[x] * num2, save to correct index (i + j + 1) + - Note: skip leading '0' during output, but do not delete string "0" + - time,space O(mn) +- Option1: Calculate carry on the fly + - index `curr = i + j + 1`, left index `left = curr - 1`, since we start calculation from end of the array. + - **we only touch right side of the array once**, so we can move the carry off from it, and carry to left index + - code is concise +- Option2: save product first without calculating carry + - save product in each int index + - calculate carry on rst[] and `sb.insert(0, c)` (since we start from end of rst) + - this is actaully faster than Option1, somehow. + +#### Reverse, calculate from index 0, and reverse back +- 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'; + + + +--- + +**51. [680. Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/680.%20Valid%20Palindrome%20II.java)** Level: Easy Tags: [String] + +#### Palindrome String +- delete an index: 有两种情况 +- 用一个boolean parameter来表现state. 如果有其他status, state可以变成 String/enum + + + +--- + +**52. [387. First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/387.%20First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +#### Count appearance with int[256] +- 按照题意, 找到第一个 first index == last index的字母. + +#### Count appearance with hashmap (more scalable) +- 用hashmap存字母的index, 有些重复字母的index就会是个list. +- 找到单一index, 结合成list, sort, return list.get(0) +- slow due + + + +--- + +**53. [345. Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/345.%20Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i && 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 cannot be any match. + + + + +--- + +**55. [28. Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/28.%20Implement%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 + + + +--- + +**56. [1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)** Level: Hard Tags: [DFS, Stack, String] + +#### Parse exp as sub problem +- Analyze the pattern: 1) single char, 2) with !, 3) with &, | +- Identify sub problem + - Use stack to parse the data in "()", which is a sub problem to solve with recursive call + - Handle &, | case: need to parse multiple +- Be comfortable with string parsing +- Slight improve: + - If see obvious result, directly return evaluation w/o further parsing + - use memo to store evaluated exp + +#### Evaluate inner exp and save back to Stack +- Use '(' and ')' to mark inner exp +- Evaluate the inner exp and save result back to Stack: the result will be 'f' or 't' +- This is slightly slow because: + - It requires all stack items on top to be processed before reaching the operator + - There is no room to optimize even there is simplification for specific operator + + + +--- + +**57. [12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)** Level: Medium Tags: [Basic Implementation, Math, String] + + +#### String, Basic implementation +- Parse each digit based on rules +- 1) parse: analyze the situations + + +--- + +**58. [14. Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/14.%20Longest%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) + + + +--- + +**59. [20. Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/20.%20Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +#### Stack +- 剥皮过程。解铃还须系铃人 +- 左边的外皮'{['在stack底部 +- 右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**60. [893. Groups of Special-Equivalent Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/893.%20Groups%20of%20Special-Equivalent%20Strings.java)** Level: Easy Tags: [Basic Implementation, String] + +Mark # of characters can be useful to print string signature + + + +--- + +**61. [91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Method1: DP, Bottom-Up by calculating base case first +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- there can be 2 final states at dp[i]: single digit or double digit. + - validate if `single digit`, dp[i] += dp[i - 1]. Last 1 digit can be decoded. + - validate if `double digit`, dp[i] += dp[i - 2]. Last 2 digits can be decoded. +- note: + - get number from char: `c - '0'` + - validatae single digit != '0', 因为'0' 不在条件之中(A-Z) +- Option1: dp[i] as # ways to decode at index i, including letter i + - The validation is done on: 1) single digit i, or 2) double digit [i-1, i] +- Option2: Partition DP, dp[i] as # ways to decode for first i letters (excluding letter i) + - 定义`dp[i] = 前i个digits最多有多少种decode的方法`: new dp[n + 1]. + - dp[i] += dp[i - x], where x = 1, 2 + - The validation is done on: 1) single digit [i-1], or 2) double digit [i-2, i-1] + - Option2 is better in terms of clean coding. We assume `dp[0]=1` as 1 way to decode 0 digits. + - No need to specially handle length == 1, because it is covered later + - No need to manualy init the first 2-digit case as in Option1 + - Return of `dp[n]` is clean +- 引申: 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +#### Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 + - especially when i = n - 2, handle 2-digt edge case carefully: + - 1) check if two-digit range [i, i+1] is valid + - 2) check if single-digit [i] is valid; if so, += dfs(s, i + 1) +- memo[i]: # ways to decode from [i, n). init with `memo[i]=-1` + + + +--- + +**62. [68. Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/68.%20Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- greedy approach: line up as many words as possible; once exceed the MaxLength, justify the list of words +- Steps + - 1) Split & group + - 2) Juststify a row of words + - 3) clean up last row +- Calcualte bounded row length = `width + (list.size() - 1)`. `list.size()-1` = Minimum amount of slot/space used. +- Calculate max ave spaces ot insert into each slot = `totalExtraSpace/slot` +- `Juststify a row of words`: + - 1) take a list of words and assume minimum 1 space in-between words + - 2) distribute the remaining spaces evenly to each slot +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**63. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**64. [796. Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/796.%20Rotate%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 不变 + + + +--- + +**65. [1041. Robot Bounded In Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/1041.%20Robot%20Bounded%20In%20Circle.java)** Level: Easy Tags: [String] + +简单的character checking. 各个方向, 加加减减. + + + +--- + +**66. [157. Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/157.%20Read%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)` +- Edge Case: + - When there is not enough space to the result buffer, `i + 3 > n`, then only copy what we can: `Math.min(n - i, count)` + - `count < 4` meaning there is not enough content to read, break + + + +--- + +**67. [273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%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来找到结果, 每段的处理方法都是一样的 +- Note: + - StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 + - 注意加 " " 的时候, 如果多余, 要`trim()` + - 注意, `小于20的数字, 有自己的特殊写法, 需要额外handle` + - 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 +- Thinking process: + - `1 ~ 19`: [one, two ... nine, ten, eleven, ...., ninteen] + - `20 ~ x0`: [twenty, thirty, fourty, ... ninety] + - `x00`: hundred: 100 + - thousand: 10^3 + - million: 10^6 + - billion: 10^9 + - trillian: 10^12 way over 2^31, not needed +- plan: + - parse 3 digits at a time + - convert the 3 digit to [xx hundred xx-ty x] + - come up with a string[] + - insert the thousands/million/billion to the string[] + + + +--- + +**68. [125. Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/125.%20Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Two Pointers +- Time O(n), Space O(1). +- 普通方法, 两边check, 速度相比较regular expression更快. leetcode 4ms. +- Use helper functions. + +#### 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/ + + + +--- + +**69. [65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**70. [49. Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/49.%20Group%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 (too slow) +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**71. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + +**72. [1170. Compare Strings by Frequency of the Smallest Character.java](https://github.com/awangdev/LintCode/blob/master/Java/1170.%20Compare%20Strings%20by%20Frequency%20of%20the%20Smallest%20Character.java)** Level: Easy Tags: [Array, String] + + +#### Method1: letter frequency map, kinda bucket sort +- Goal: find word count that fits into `f(queries[i]) < f(W)` +- What if: we can store the f(W) as preSum, then goal: `rst[i] = preSum[end] - preSum[queryWordCount]` + - count(W) and store in count[i] + - calc preSum + - processs queries array +- kinda bucket sort: + - 1) we know the boundary of the word length, so we can create `bucket` + - 2) `function count(w)` can produce a value that sort a word into a specific bucket slot + - extend: the bucket can store keys that links back to the word (if there are follow up questions) +- time: O(m + n) +- space: O(m + n) + +#### Method2: No brain solution, basic impl based on the desc, w/o search. +- time: + - O(nm) to count all words, O(nlogn) to sort the wordCount + - O(nm) to to count all queries + - O(n^2) to perform the match +- space: O(n) + + + +--- + +**73. [8. String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/8.%20String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- Handling use cases +- Parse steps: + - 0. trim space + - 1 parse operator + - 2 trim leading zero + - 3. get number string +- Validation: + - 1. max length over max integer length + - 2. exceed min/max value +- Alternatively, regular expression, but not applicable in interview: if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**74. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**75. [71. Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/71.%20Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path: + - 1. `.` 代表current directory, 可以忽略. + - 2. `../` 表示previous level. + - 3. double slash 可以忽略. + - 4. empty string 要output `/` +- parse by '/', and go over using stack + - put [folder] in stack + - ".." pop() 1 element of the stack, if anything + - "." stays the same +- output stack reversely: connect with '/', skip tail + + + +--- + +**76. [13. Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/13.%20Roman%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 + +#### use map to store combinations + + + + +--- + +**77. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + + + + + + + +## DP (94) +**0. [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 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 + + + +--- + +**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. [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. [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) + + + +--- + +**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. [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);` + + + +--- + +**6. [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 + + + +--- + +**7. [Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)** Level: Medium Tags: [DP] + +这个DP有点诡异. 需要斟酌。 +NOT DONE YET + + +--- + +**8. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**9. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + + +#### 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题,想不到,就是搞不出。 + + + + +--- + +**10. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**11. [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) + + + +--- + +**12. [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 + + + +--- + +**13. [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了。 + + + +--- + +**14. [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 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**15. [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) + + + +--- + +**16. [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) + + + + +--- + +**17. [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. + + + +--- + +**18. [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? + + + +--- + +**19. [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, 看解答 + + + +--- + +**20. [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作比较;求的所有情况的最大值嘛。 + + + +--- + +**21. [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), 太慢. + + + +--- + +**22. [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. + + + + +--- + +**23. [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 + + + +--- + +**24. [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) + + + +--- + +**25. [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 + + + +--- + +**26. [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] + + +给一串 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) + + + +--- + +**27. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**28. [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) + + + +--- + +**29. [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 + + + +--- + +**30. [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[] + + + +--- + +**31. [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? + + + +--- + +**32. [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, 节约时间复杂度. + + + +--- + +**33. [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] + + + + +--- + +**34. [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]。 + + + + + +--- + +**35. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**36. [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. + + + + +--- + +**37. [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 +还没有做 + + + +--- + +**38. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**39. [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看出有直接关系. + + + +--- + +**40. [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) + + + +--- + +**41. [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; + + + + +--- + +**42. [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 + + + + +--- + +**43. [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的状态, 然后看最后一步. + + + +--- + +**44. [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的理所应当。 + + + + +--- + +**45. [Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)** Level: Hard Tags: [DP] + + + +--- + +**46. [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] + + +跟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] + + + +--- + +**47. [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]) + + + +--- + +**48. [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` + + + +--- + +**49. [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 + + + +--- + +**50. [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 + + + +--- + +**51. [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; + + + +--- + +**52. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + +// 如何想到从中间initialize + + + +--- + +**53. [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. + + + +--- + +**54. [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的做法写出结果. + + + +--- + +**55. [198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +搜刮房子, 相邻的不能碰. 每个房子里有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虽然抽象, 但是更加实用. + +#### Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- calculation + - dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]). The prior house can be either state. + - dp[i][1] = dp[i - 1][0] + nums[i]. The prior house must be `NOT ROBBED`. +- return `Math.max(dp[n - 1][0], dp[n - 1][1])` + + + +--- + +**56. [301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)** Level: Hard Tags: [BFS, DFS, DP] + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- Goal: identify invalid parentheses and remove (minimum removals) +- Step: + - Detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` + - When invalid occurs: + - chance for correction. Remove the incorrect parentheses, one at a time + - dfs on the rest of the s that has not been tested yet: start index from index i + - Core edge cases: + - Do not correct twice of the same parenthesis by checking [j-1] pos + - Make sure to attempt correction of all possible parenthesis within tested range: because it outputs all results at the same level + - return/finish once correction done +- Success case: + - a string s passed test: make sure it passes REVERSED string test! + - Core Concept: `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**. +- How does it guarantee minimum removals? + - When seeing a chance to correct, it jumps into a for loop of DFS. It `return` after the for loop. This stops additional testing + - When invalid occurs, correct it right away: minimum correction +- Complexity: + - O(nk), k being the # of recursive calls. It takes n calls to finish a full string case. + +#### BFS +- Similar to DFS, we wnat to test: 1) test input s valid, 2) remove 1 invalid parenthesis at a time, 3) process substring +- instead of testing all substrings (timeout), we want to establish rules to improve reprocess: + - Test1: skip regular char. No need to test it. + - Test2: if redundant paren, do 1 is enough. skip adjacent ones. + - Test3: if last removed extra paren is '(', the next ')' must be a valid pair. LastRemoved char: pecial handling by using a struct: `class Node {String s, int index, char lastRemoved}` +- How to end tests? When there is data in rst, stop adding to queue. + + + +--- + +**57. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**58. [5. Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/5.%20Longest%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)? + +#### Method1: DP of interval +- Very similar to `216. Longest Palindromic Subsequence`, but this problem requires solid substring(i+1, j-1) to be palindromic +- Similarly: process i = n-1, from end so [i + 1, j] is always ready to consume +- boolean dp[i][j] to mark range (i, j) as palindrome or not. +- 在计算 dp[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- time: O(n^2) dp +- space: O(n^2) + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + + + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**59. [303. Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/303.%20Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**60. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**61. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**62. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**63. [518. Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/518.%20Coin%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 + + + +--- + +**64. [509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.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. + + + + +--- + +**65. [221. Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/221.%20Maximal%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; +- init: 每个点都可能是边长1, 如果 matrix[i][j] == '1' +- Space, time O(mn) +- Rolling array: [i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + + +--- + +**66. [1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)** Level: Medium Tags: [Bucket Sort, DP, Hash Table, Sort] + + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**67. [62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +#### DP, 加法原理 +- 计数DP: 2 ways to reach (i,j): dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + - non-overlapping: `dp[i - 1][j]`, `dp[i][j - 1]` + - covers the only 2 possible way to reach (i,j) +- initialization: dp[i][0] = 1, dp[0][i] = 1 + - Of course, row i = 0, or col j = 0, there is only 1 way to reach +- time O(mn), space O(mn) + +##### 滚动数组 Rolling Array +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + +#### DFS + Memoization +- move from (0,0) towards (m, n) +- use Map as memoization technique + + + +--- + +**68. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + +**69. [55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%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 +- 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) +- space: O(1) + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (j + A[j] >= i), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- time: O(n^2) +- space: O(n) + + + + +--- + +**70. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**71. [741. Cherry Pickup.java](https://github.com/awangdev/LintCode/blob/master/Java/741.%20Cherry%20Pickup.java)** Level: Hard Tags: [DFS, DP] + + +special hint: `r1 + c1 = constant t = r2 + c2`, if the two points are moving at same time. + +#### DFS + Memo: TOP-DOWN +- Similar concept to Minimum Path Sum +- https://leetcode.com/problems/cherry-pickup/solution/ +- realize r1 + c1 = r2 + c2. Knowing 3 parameters can uniquely identify the 4th. +- assume there are 2 people starting from origin, and the 2 people can go total 4 directions + - perform DFS based on the 4 directions + - concern: do they visit the same spot? possible. when that happens, make sure we do not double count the grid[i][j] +- when is the end state? + - then anyone, for example, (r1,c1) reaches end (n-1, n-1). + - it means the other person also reaches end +- use memo: memo[r1][c1][r2], it records any given (r1, c1, r2, c2) state + + + + +--- + +**72. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**73. [70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%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]往往是有特殊状态的. 这里, dp[0]: 1种方式可以原地不动 + - dp[1]=1, 1种方式到达index=1, + - 之后的`dp[2] = dp[0]+dp[1]`: 就是dp[0]的走法 or dp[1]的走法 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**74. [10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + + + +--- + +**75. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**76. [91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Method1: DP, Bottom-Up by calculating base case first +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- there can be 2 final states at dp[i]: single digit or double digit. + - validate if `single digit`, dp[i] += dp[i - 1]. Last 1 digit can be decoded. + - validate if `double digit`, dp[i] += dp[i - 2]. Last 2 digits can be decoded. +- note: + - get number from char: `c - '0'` + - validatae single digit != '0', 因为'0' 不在条件之中(A-Z) +- Option1: dp[i] as # ways to decode at index i, including letter i + - The validation is done on: 1) single digit i, or 2) double digit [i-1, i] +- Option2: Partition DP, dp[i] as # ways to decode for first i letters (excluding letter i) + - 定义`dp[i] = 前i个digits最多有多少种decode的方法`: new dp[n + 1]. + - dp[i] += dp[i - x], where x = 1, 2 + - The validation is done on: 1) single digit [i-1], or 2) double digit [i-2, i-1] + - Option2 is better in terms of clean coding. We assume `dp[0]=1` as 1 way to decode 0 digits. + - No need to specially handle length == 1, because it is covered later + - No need to manualy init the first 2-digit case as in Option1 + - Return of `dp[n]` is clean +- 引申: 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +#### Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 + - especially when i = n - 2, handle 2-digt edge case carefully: + - 1) check if two-digit range [i, i+1] is valid + - 2) check if single-digit [i] is valid; if so, += dfs(s, i + 1) +- memo[i]: # ways to decode from [i, n). init with `memo[i]=-1` + + + +--- + +**77. [639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%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]) +- Enumeration: + - 具体分析 '*' 出现的位置, 枚举出数字, 基本功. + - 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) + - 枚举好以后, 其实这个题目的写法和思考过程都不难 +- Mode: + 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. + + +#### DFS + memoization +- DFS top-down approach is used to analyze the problem. The logic flow: +- 1) consider the case of 1 letter or 2 letters. +- 2) one letter: + - [*]: + 9 * dfs(s, i + 1) + - [0~9]: + dfs(s, i + 1) +- 3) two letters: + - [_, *]: depends + - [*, _]: depends + - [*, *]: + 15 * dfs(s, i + 2) +- memo[i] records # of ways to decode from [i ~ n] +- space: O(n), Size of recursion tree can go upto n +- time: O(n), `memo array is filled exactly once`!!! + + + +--- + +**78. [64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%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(N) + - 1) 需要在同一个for loop里面完成initialization, 2) reuse dp[i][j] + - Reason: dp[i % 2][j] 在被计算出来的时候, 马上在下一轮会被用. 被覆盖前不用,就白算 + - Option3 below initialize dp outside of loop: 看起来固然简单, 但是不方便空间优化 + +#### DFS (top-down) Thinking process +- Enumerate the possible 2 paths: go right, go down +- sub problem: dfs(i+1,j), dfs(i,j+1); pick the min of the two +- memoization: after the path from a point (i,j) to end is computed, record memo[i][j] to avoid repatative calculation +- time: O(mn), only visite and record memo[i][j] once. +- space: O(mn) extrem case of m=100000, n = 2; the stack height is O(mn) + + +--- + +**79. [121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### min[n] +- 每天都就交易价格,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 dont need to buyin at 5, 3, 6, 4 later on; + - only need to sell on higher prices. + + + +--- + +**80. [304. Range Sum Query 2D - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/304.%20Range%20Sum%20Query%202D%20-%20Immutable.java)** Level: Medium Tags: [DP, PreSum] + + +#### Method1: rangeSum/caching +- build rangeSum[i][j]: square range sum from (0,0) to (i,j), O(mn) to init +- query: time O(1) + +#### Method2: presum/caching +- build rowPreSum[i][j]: row i sum from [0 ~ j], O(mn) to init +- callign takes O(m); space O(mn) + + + + +--- + +**81. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + + +--- + +**82. [63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%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] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + + + +--- + +**83. [139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- Bottom-up, test simply case. Sequence DP. +- true/false problem, think about dp + - 子问题: 前i个字母, 是否可以有valid break + - check: 1) dp[j] && 2) `if substring(j, i) valid`, for all j = [0 ~ i] + - dp = new boolean[n + 1]; dp[0] = true; + - test: `dp[i] |= dp[j] == true && word[j, n] in dict`. + - Need iterate over i = [0 ~ n], also j = [0, i] + - When there is a way to make dp[i] == true, then break the [j ~ i] loop, move on to test dp[i++] +- Use set dict: `dict.contains()` +- Improvement: O(n) to figure out max length, so we can skip some substring[j~i] dict.contains() +- overall O(n^2) time since the double for loop + +#### DFS +- Top-Down, break into small problems: Check front subString, and put the rest substring into dfs to test +- Memoization: for tested failed substring, record and do NOT test them again. +- Same Improvement as in DP: use max/min length of dict words as boundary + + + +--- + +**84. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + +**85. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + +**86. [361. Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/361.%20Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Method1: 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 的变换. + +#### Method2: Analyze, col count array: +- Inspired by: https://leetcode.com/problems/bomb-enemy/discuss/83387/Short-O(mn)-time-O(n)-space-solution +- Analyize the problem: need to add up 2 directions at any given point. + - Notice 1: if I traverse row by row, each colSum at a specific col j is likely to be the same + - Unless there is a Wall in last row, so we have to calclate the col sum starting from current row, below the Wall + - Notice 2: for row it is the same: + - If I in a new spot row[i][j], where (i-1) is Wall, i need to sum row from 0 +- we will process each point: + - process row by row and add up row sum + - buffer col[j] in an array vertically so we can resue + - make sure to recalculate row sum or col sum if last row index or last col index is Wall +- time: O(mn) traversal +- space: O(n) only use a column array + + + +--- + +**87. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + +**88. [689. Maximum Sum of 3 Non-Overlapping Subarrays.java](https://github.com/awangdev/LintCode/blob/master/Java/689.%20Maximum%20Sum%20of%203%20Non-Overlapping%20Subarrays.java)** Level: Hard Tags: [Array, DP] + + +#### DP, Divide and conquer +- split into 3 parts [0, i -1], [i, i + k -1]. [i + k, n - 1] +- NOTE: be very careful about index handling: + - `presum[i + 1] - presum[0]` gives inclusive range of `[0, i]` +- Use DP to record the starting position of max sum, +- inspired by: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C%2B%2BJava-DP-with-explanation-O(n) + - 1) calculate preSum with range [0, n] + - 2) calculate leftMaxIndex[], rightMaxIndex[] + - 3) test middle range to find max solution +- Note: the test range for 1, 2, 3 always start with assumption that k has been consumed from one side +- Note: When need to record at max/min value change, we can check/assign it manually (rather than use a object to carry & sort) + + + + +--- + +**89. [698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)** Level: Medium Tags: [DFS, DP, Recursion] + + +#### Method1: Brutle DFS to find subsets and return results +- Target = total / k, fixed. +- DFS: Pick one num and dfs with the remaining nums for subproblem + - subproblem1: accumulate local sum to target + - EndState: accumulatedSum == target, continue with below + - subproblem2: after accumulatedSum == target, continue dfs with k-1 + - EndState: k == 0, return overall true +- Option1: DFS with nums, and boolean[] visited. Fast +- Option2: DFS with queue. + - Specially handling: dfs(size+1) to prevent `while(size-- >0)` from skipping last item at index 0 + + +#### Method2: DP +- the problem aims to find true/false capability +- bit-masking. TODO. The DP approach is kinda hard-level +- https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/335668/DP-with-Bit-Masking-Solution-%3A-Best-for-Interviews + + + +--- + +**90. [416. Partition Equal Subset Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/416.%20Partition%20Equal%20Subset%20Sum.java)** Level: Medium Tags: [Backpack, DP] + +#### Backpack DP +- the problem turns into: can we find a subset of items that sum up to target sum? +- create `boolean dp[j]` to represent if we can sum up to j, where j = sum value + - want to try out all items in num, + +#### DFS +- use dfs to find a subset of items that sum up to target sum? + + + +--- + +**91. [256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, depends on the color of dp[n-1] + - 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- Need to have status with dp array: int[index][color status] + - dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. + - dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- time: O(nm), m = # of colors +- space: O(nm) + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 +- space:O(1) + + + +--- + +**92. [265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + + +一排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) + + + +--- + +**93. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + + + + + + + +## Math (45) +**0. [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) 太慢, 不合题意 + + + +--- + +**1. [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 + + + + +--- + +**2. [Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)** Level: Easy Tags: [Math] + + + +--- + +**3. [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. + + +--- + +**4. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + + +给一串点, 找是否有一个所有点中间的, 跟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) + + + +--- + +**5. [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了。 + + + +--- + +**6. [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许多。 + + + + +--- + +**7. [Orderly Queue.java](https://github.com/awangdev/LintCode/blob/master/Java/Orderly%20Queue.java)** Level: Hard Tags: [Math, String] + + + + +--- + +**8. [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. + + + +--- + +**9. [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. + + + + +--- + +**10. [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的益处。 + + +--- + +**11. [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. + + + +--- + +**12. [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找合适的数字. + + + +--- + +**13. [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 + + + +--- + +**14. [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 + + + +--- + +**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] + + +#### 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. [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整除. 一步到位. + + + +--- + +**17. [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` + + + +--- + +**18. [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) + + + +--- + +**19. [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) + + + +--- + +**20. [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 的情况. + + +--- + +**21. [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 + + + +--- + +**22. [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, 数位号) + + + + +--- + +**23. [360. Sort Transformed Array.java](https://github.com/awangdev/LintCode/blob/master/Java/360.%20Sort%20Transformed%20Array.java)** Level: Medium Tags: [Math, Two Pointers] + + +#### Two Pointers, Math +- Being able to analys the a*x^2 + b*x graph and find the `peak` or `valley` +- Math basics: x^2 dominates the overall curve so it is up to a to determine: + - `valley`: if a < 0, both sides will be small and center will be large. Prioritize larger value. + - `peak`: if a > 0, center will be small and both sides will be large. Prioritize smaller value. + - starting index being 0 or n-1, is driven by `a` + + + +--- + +**24. [415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)** Level: Easy Tags: [Basic Implementation, Math, String] + + +#### Two Pointer +- Use i, j to process from end of 2 strings +- handle edge case for i, j + - if i < 0, its num = 0 (since we are doing sum, blindly setting 0 is okay) +- Note: `sb.insert(0, x)` is much slower than doing a final `sb.reverse()` + +#### If manually convertin to int[] +1. when converting to int[], remember to reverse string. +1. when converting to int[], remember to reserve extra space for carry + + + +--- + +**25. [7. Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/7.%20Reverse%20Integer.java)** Level: Easy Tags: [Math] + + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + + + +--- + +**26. [204. Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/204.%20Count%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个数就好了 + + + +--- + +**27. [509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.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. + + + + +--- + +**28. [67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%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 + +#### reverse +- Reverse string -> Convert to Integer List, add up -> Convert back to string +- pointer 从前向后, 所以只需要 1个pointer. +- 操作复杂, 如上, 证明可以解决. 没必要reverse. + +#### Incorrect: convert to Integer +把binary换成数字作加法. 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**29. [168. Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/168.%20Excel%20Sheet%20Column%20Title.java)** Level: Easy Tags: [Math] + + +#### 基本换算 +- 26位, 像10位一样去思考 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + + + +--- + +**30. [9. Palindrome Number.java](https://github.com/awangdev/LintCode/blob/master/Java/9.%20Palindrome%20Number.java)** Level: Easy Tags: [Math] + +#### Reverse half of the number +- build reversed integer 直到midpoint, where x <= reverse +- 如果input双数: x == reverse +- 如果input单数 (而且x>reverse): x == reverse/10 + +#### Consider palindrome +- optionA: compare digit by digit +- optionB: reverse half of the string/int, and compare with other half. + + + + + + +--- + +**31. [43. Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/43.%20Multiply%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]. + - Loop over num1, each row num1[x] * num2, save to correct index (i + j + 1) + - Note: skip leading '0' during output, but do not delete string "0" + - time,space O(mn) +- Option1: Calculate carry on the fly + - index `curr = i + j + 1`, left index `left = curr - 1`, since we start calculation from end of the array. + - **we only touch right side of the array once**, so we can move the carry off from it, and carry to left index + - code is concise +- Option2: save product first without calculating carry + - save product in each int index + - calculate carry on rst[] and `sb.insert(0, c)` (since we start from end of rst) + - this is actaully faster than Option1, somehow. + +#### Reverse, calculate from index 0, and reverse back +- 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'; + + + +--- + +**32. [367. Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/367.%20Valid%20Perfect%20Square.java)** Level: Easy Tags: [Binary Search, Math] + + +#### Binary找sqrt +- binary search template: mid+1, mid-1, `start <= end` +- define index as long. + + + +--- + +**33. [246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math, Two Pointers] + + +根据题意枚举, 再根据规则basic implementation + +#### HashTable + Two Pointer +- compare left/right + +#### Alter input +- flip number (6 and 9), and then reverse the string, see if the string is the same. +- takes more + + + + +--- + +**34. [319. Bulb Switcher.java](https://github.com/awangdev/LintCode/blob/master/Java/319.%20Bulb%20Switcher.java)** Level: Medium Tags: [Brainteaser, Math] + + +#### Brainteaser +- https://leetcode.com/problems/bulb-switcher/discuss/77104/Math-solution.. + +#### Brutle: +- if just impl, it take O(n^2): +- repating: some pos are toggled mutiple times: if we know total times, easy to determin each pos. +- loop over [2, n], count times on each index + + + +--- + +**35. [12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)** Level: Medium Tags: [Basic Implementation, Math, String] + + +#### String, Basic implementation +- Parse each digit based on rules +- 1) parse: analyze the situations + + +--- + +**36. [202. Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/202.%20Happy%20Number.java)** Level: Easy Tags: [Hash Table, Math] + + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + + + +--- + +**37. [69. Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/69.%20Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + +#### sqrt(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 + + + +--- + +**38. [2. Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/2.%20Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. 跟Add Binary的理解方式一模一样. + +#### Math, Linked List +- reverse order helps calculation + - add additional carry to end + - not same length: align on left +- traverse till both ends +- 遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + + + + +--- + +**39. [273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%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来找到结果, 每段的处理方法都是一样的 +- Note: + - StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 + - 注意加 " " 的时候, 如果多余, 要`trim()` + - 注意, `小于20的数字, 有自己的特殊写法, 需要额外handle` + - 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 +- Thinking process: + - `1 ~ 19`: [one, two ... nine, ten, eleven, ...., ninteen] + - `20 ~ x0`: [twenty, thirty, fourty, ... ninety] + - `x00`: hundred: 100 + - thousand: 10^3 + - million: 10^6 + - billion: 10^9 + - trillian: 10^12 way over 2^31, not needed +- plan: + - parse 3 digits at a time + - convert the 3 digit to [xx hundred xx-ty x] + - come up with a string[] + - insert the thousands/million/billion to the string[] + + + +--- + +**40. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + +**41. [65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**42. [8. String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/8.%20String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- Handling use cases +- Parse steps: + - 0. trim space + - 1 parse operator + - 2 trim leading zero + - 3. get number string +- Validation: + - 1. max length over max integer length + - 2. exceed min/max value +- Alternatively, regular expression, but not applicable in interview: if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**43. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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" + + + +--- + +**44. [13. Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/13.%20Roman%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 + +#### use map to store combinations + + + + +--- + + + + + + + +## 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. [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), 太慢. + + + +--- + +**2. [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 + + + +--- + +**3. [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] + + + + +--- + +**4. [10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + + + +--- + +**5. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + + + + + + + +## BFS (54) +**0. [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开头 + + + +--- + +**1. [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()` + + + +--- + +**2. [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. + + + +--- + +**3. [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了。 + + + +--- + +**4. [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. + + + +--- + +**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. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**7. [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 + + + +--- + +**8. [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完 + + + +--- + +**9. [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 + + + +--- + +**10. [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. + + + +--- + +**11. [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 + + + +--- + +**12. [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. +- 分析过, 还没有写. + + + +--- + +**13. [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 + + + +--- + +**14. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**15. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + +--- + +**16. [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; + + + + +--- + +**17. [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 + + + +--- + +**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. + + + +--- + +**19. [Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)** Level: Hard Tags: [BFS] + + + +--- + +**20. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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. [102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### Method1: BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### Method2: DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**22. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**23. [301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)** Level: Hard Tags: [BFS, DFS, DP] + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- Goal: identify invalid parentheses and remove (minimum removals) +- Step: + - Detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` + - When invalid occurs: + - chance for correction. Remove the incorrect parentheses, one at a time + - dfs on the rest of the s that has not been tested yet: start index from index i + - Core edge cases: + - Do not correct twice of the same parenthesis by checking [j-1] pos + - Make sure to attempt correction of all possible parenthesis within tested range: because it outputs all results at the same level + - return/finish once correction done +- Success case: + - a string s passed test: make sure it passes REVERSED string test! + - Core Concept: `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**. +- How does it guarantee minimum removals? + - When seeing a chance to correct, it jumps into a for loop of DFS. It `return` after the for loop. This stops additional testing + - When invalid occurs, correct it right away: minimum correction +- Complexity: + - O(nk), k being the # of recursive calls. It takes n calls to finish a full string case. + +#### BFS +- Similar to DFS, we wnat to test: 1) test input s valid, 2) remove 1 invalid parenthesis at a time, 3) process substring +- instead of testing all substrings (timeout), we want to establish rules to improve reprocess: + - Test1: skip regular char. No need to test it. + - Test2: if redundant paren, do 1 is enough. skip adjacent ones. + - Test3: if last removed extra paren is '(', the next ')' must be a valid pair. LastRemoved char: pecial handling by using a struct: `class Node {String s, int index, char lastRemoved}` +- How to end tests? When there is data in rst, stop adding to queue. + + + +--- + +**24. [111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%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 Conquer to find min depth. + - if one of child is null, return the other child depth + 1 + - Pick the min of the two child depth + 1 +- need to visit all nodes + + + + +--- + +**25. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**26. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**27. [429. N-ary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/429.%20N-ary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Tree] + + +#### BFS +- use queue to hold each level. O(n) + + + +--- + +**28. [199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%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 +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n nodes in final results + + +#### DFS +- Use Map to override the result at each level +- dfs: + - dfs(node.left) and then dfs(node.right) because we want to log right side last +- record global max depth for iteration purpose +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n stacks (and n nodes in final results) + + + + +--- + +**29. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**30. [515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS +- faster than BFS, using less space if not couting final rst: stack size, O(logn) +- time: O(n), visit all + +#### Method2: BFS with queue +- loop over queue level and record max + + + + +--- + +**31. [1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph] + + + +#### BFS +- simply calc each level sum with BFS +- top-level is processed first, since we go from top level -> deeper level + - only update result if sum is truly > global MAX. + + + +--- + +**32. [1091. Shortest Path in Binary Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/1091.%20Shortest%20Path%20in%20Binary%20Matrix.java)** Level: Medium Tags: [BFS] + + + +#### BFS +- find shortest path using queue +- time/space: O(n^2), n = grid length +- why SKIP `boolean visited[i][j]`? after a position grid[i][j] is used: + - 1) the curr path will not return to (i, j) + - 2) other route that may eventually reach (i, j) need not to be recorded, + - because the other route is already longer than the curr path + - therefore, we just simply block the visited node by `grid[x][y] = 1` + - note: block it right after it is added to the queue, so other nodes at same level will not attempt this visited node. + + + +--- + +**33. [339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS, NestedInteger] + + +给一串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. + - 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) + +#### BFS +- bfs, queue, 处理queue.size() for a level +- use a level variable to track levels +- slower since it uses extra space, worst case O(n) of all items + + + +--- + +**34. [1197. Minimum Knight Moves.java](https://github.com/awangdev/LintCode/blob/master/Java/1197.%20Minimum%20Knight%20Moves.java)** Level: Medium Tags: [BFS] + + +#### BFS +- `from starting point, find min steps to reach certain point`: think of BFS + - similar: shortest path, shortest distance +- bfs: minimum steps, enumerate the possible moves + - move closer to x or y (test 8 possible directions) + - add possible moves in queue +- use visited to cache visited coordinates +- time: O(8^n), # of BFS branches +- space: O(8^n), # of BFS branche nodes + + + +--- + +**35. [1306. Jump Game III.java](https://github.com/awangdev/LintCode/blob/master/Java/1306.%20Jump%20Game%20III.java)** Level: Medium Tags: [BFS, Graph] + + +### Method1: BFS +- Find possibility to reach certain point, we can BFS: faster to find shortest candidate +- use queue to hold left, right candidates +- use set to record visited + +### Method2: DFS +- attemp all nodes, use set to record visited. +- time: O(n) +- space: O(n) + + + +--- + +**36. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**37. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + +**38. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + +**39. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**40. [100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### Method1: DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### Method2: BFS with 2 queues +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**41. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**42. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + +**43. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**44. [103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + + +#### 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 + + + +--- + +**45. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**46. [133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%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: `map` + - 1) make new curr node; + - 2) clone all neibhors and add them +- Use the map to avoid visited nodes +- time: O(n). visit all nodes +- space: O(n). Technically only travels n levels/stacks to circle all nodes (undirected & connected) + +#### 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. + + + +--- + +**47. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**48. [1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS, top-down +- key concetpL the `common ancester of deppest leaves` must have its `two branch being same depth`. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +- time: O(n) traversal 1 pass +- space: O(n) dfs worst case depth + +#### Method2: bottom-up, find leaves first and traverse backwards +- 1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +- 2) use leaf nodes to find way backwards till common node is found; return +- time: O(n) but two passes +- space: O(n) dsf + map storage +- this approach is more brutle and uses exrtra spaces + + + +--- + +**49. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + +**50. [785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)** Level: Medium Tags: [BFS, DFS, Garph] + + +#### DFS marking each node with a state +- `bipartite` require each node to be in exact 1 party, which means it only has 1 state +- DFS to mark node with one state; and mark its edges as reversed state + - If any node state has been assigned by different from desired one, return false. + +#### BFS, Queue +- Use `Boolean states[i]` to represent visted & state +- Try all nodes with for loop, and skip visited nodes (similar validation rules as in dfs) +- In `int next : graph[curr]`, test next level first before adding. + + + +--- + +**51. [101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### Method1: DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Method2: interative with queue +- put left or right children in pair + +#### Method3: iterative with Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**52. [671. Second Minimum Node In a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/671.%20Second%20Minimum%20Node%20In%20a%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +#### BFS +- min tree: parent node is the min of left/right child +- BFS to traverse the tree and find 1st non-root smallest val +- Improvement area: when `node.val >= nextMin`, no need to dive into node children since it is a min Tree. + +#### DFS +- Find left and right val: + - if left/right equals root.val, that means the left or right sub children could have larger number + - Therefore DFS into left or right +- compare and return min(left, right) + + + +--- + +**53. [254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)** Level: Medium Tags: [BFS, Backtracking, DFS] + + + +#### Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs + + +#### Method2: BFS +- Check if the number can be devided by [2, sqrt(n)], return a list of possible factors. Only check till `Math.sqrt(n)` + - build suffixes: use the factor to devide last element of list and replace last element + - build candidate: replace last element of the queue item with new list of suffixes; add to rst + - add success item back to queue: in case last element can be simplified +- **remove dupilcates**: since we start factor from [2, sqrt(n)], the final factor list should be **ascending**!! +- time: O(x), x is the # of results +- space: O(y), y is all ongoing candidates in queue + + + +--- + + + + + + + +## Segment Tree (17) +**0. [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 + + + +--- + +**1. [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 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**2. [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了 + + + +--- + +**3. [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. + + + + +--- + +**4. [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. + + + +--- + +**5. [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. + + + +--- + +**6. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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, 自底向上建立起的。 + + + +--- + +**7. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**8. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**9. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**10. [308. Range Sum Query 2D - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/308.%20Range%20Sum%20Query%202D%20-%20Mutable.java)** Level: Hard Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree +- Same concept as turning an array into a binary segment tree, + - HOWEVER, this is a 4-nary segmenet tree +- Reference. 307 Range Sum Query +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. +- Handling end stage, there are two approaches: + - ApproachA: check at beginning of recursive call (i.e in `build()`, `updateNode()`, `rangeQuery()`). + - pro: calling recursive function blindly; code is easy. + - con: be really clear about termination state, and catch it. + - ApproachB: check & come up with correct query condition before recursive call + - pro: input to recursive function is assumed to be correct + - con: sometimes really hard to write the conditions before recursive call; code is hard. + + + +--- + +**11. [850. Rectangle Area II.java](https://github.com/awangdev/LintCode/blob/master/Java/850.%20Rectangle%20Area%20II.java)** Level: Hard Tags: [Segment Tree, Sweep Line] + + +#### Sweep Line + Merge Interval concept +- Inspired by: https://leetcode.com/problems/rectangle-area-ii/discuss/137941/Java-TreeMap-solution-inspired-by-Skyline-and-Meeting-Room +- First consider regular sweep line and realize problem: each vertical line has multiple block segments + - Easy: take a list of vertical dots, and calculate the height diff + - We can use a TreeMap with y-coordinate as key, so to `natural sort by y-coordinate` +- Trick: can NOT remove used y coordinate from map, because the rectangle may continue to expand to right side. +- apply simple equation to calc area: `(long)preY * (p.x - preX)` +- time: + - sort initial queue: O(nlogn) + - process queue: O(n) + - TreeMap insertion: O(logn) + - TreeMap traversal: O(n) + - overall, process queue can be O(n^2) +- space: O(n) + +#### Sweep Line concept, bottom->top sweep +- https://leetcode.com/problems/rectangle-area-ii/discuss/137914/C%2B%2BPython-Discretization-and-O(NlogN) + +#### Segment Tree +- TODO lol + + + +--- + +**12. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**13. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**14. [307. Range Sum Query - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/307.%20Range%20Sum%20Query%20-%20Mutable.java)** Level: Medium Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree, devide and conquer +- sample problem for segment tree +- build(), update(), rangeQuery() + - build and update are standard + - rangeQuery: handle the range split check +- Null leaf node handling: NO, ideally it will not encounter null leaf. + - in update/rangeQuery: when final state (`start==end`) is reached, the recursive call ends + - there is no way for any node to dive futher into null child. +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. + + + +--- + +**15. [715. Range Module.java](https://github.com/awangdev/LintCode/blob/master/Java/715.%20Range%20Module.java)** Level: Hard Tags: [Segment Tree, TreeSet] + + +#### TreeSet +- start with considering array structure but operation are all O(n) + - what if we can easily find range, and update +- TreeSet: + - build a class `Interval {int start, end;}` + - build a customized `compareTo` that sorts the interval by start at default, but sort by end if a.start==b.start + - Query: TreeSet allow us to find element in O(logn) + - Add Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + - Remove Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + + + +--- + +**16. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + + + + + + + +## LinkedHashMap (1) +**0. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + + + + + + + +## DFS (121) +**0. [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. + + + +--- + +**1. [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 + + + +--- + +**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. [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了 + + + +--- + +**4. [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);` + + + +--- + +**5. [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,要乘一下。 + + + + +--- + +**6. [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 + + + +--- + +**7. [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) + + + +--- + +**8. [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 + + + +--- + +**9. [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()` + + + +--- + +**10. [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 + + + + +--- + +**11. [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. + + + +--- + +**12. [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)) + + + + +--- + +**13. [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 + + + + +--- + +**14. [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 + + + +--- + +**15. [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 + + + +--- + +**16. [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. + + + +--- + +**17. [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. + + + +--- + +**18. [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. + + + +--- + +**19. [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开始选。 + + + +--- + +**20. [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, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**21. [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一下。 + + + + +--- + +**22. [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. + + + +--- + +**23. [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加上去就好. + + + +--- + +**24. [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) + + + +--- + +**25. [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 + + + +--- + +**26. [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的基础上, 比对左左,左右,右左,右右 + + + +--- + +**27. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**28. [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了 + + + +--- + +**29. [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 + + + +--- + +**30. [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};` + + + +--- + +**31. [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完 + + + +--- + +**32. [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 + + + +--- + +**33. [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. + + + +--- + +**34. [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 + + + +--- + +**35. [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 一样 + + + +--- + +**36. [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 + + + +--- + +**37. [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的判断 + + + +--- + +**38. [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的情况。要把遍历的例子写写 + + + +--- + +**39. [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. + + + + +--- + +**40. [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 +还没有做 + + + +--- + +**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. [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) + + + +--- + +**43. [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 + + + +--- + +**44. [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一下。 + + + +--- + +**45. [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] + + +给一个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 + + + +--- + +**46. [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)))` + + + +--- + +**47. [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` + + + +--- + +**48. [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就可以了. + + + +--- + +**49. [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 + + + +--- + +**50. [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. + + + +--- + +**51. [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 + + + +--- + +**52. [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) + + + +--- + +**53. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + +// 如何想到从中间initialize + + + +--- + +**54. [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] + + +给一个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才可以施行. + + + +--- + +**55. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**56. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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 + + + +--- + +**57. [102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### Method1: BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### Method2: DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**58. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**59. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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 + + + +--- + +**60. [236. Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/236.%20Lowest%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 + - Base Case: 当root == null, p or q is found (`root == p || root == q`),那么就return the root as LCA + - 三种情况: + - 1. leftLCA and rightLCA all found: `each path has found one of p and q node as LCA`. Therefore, curr root is the lowest ancestor + - 2. One of leftLCA and rightLCA is found: return whichever one found + - 3. both LCAs are null, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time O(n), stack space O(n) + + + +--- + +**61. [301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)** Level: Hard Tags: [BFS, DFS, DP] + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- Goal: identify invalid parentheses and remove (minimum removals) +- Step: + - Detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` + - When invalid occurs: + - chance for correction. Remove the incorrect parentheses, one at a time + - dfs on the rest of the s that has not been tested yet: start index from index i + - Core edge cases: + - Do not correct twice of the same parenthesis by checking [j-1] pos + - Make sure to attempt correction of all possible parenthesis within tested range: because it outputs all results at the same level + - return/finish once correction done +- Success case: + - a string s passed test: make sure it passes REVERSED string test! + - Core Concept: `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**. +- How does it guarantee minimum removals? + - When seeing a chance to correct, it jumps into a for loop of DFS. It `return` after the for loop. This stops additional testing + - When invalid occurs, correct it right away: minimum correction +- Complexity: + - O(nk), k being the # of recursive calls. It takes n calls to finish a full string case. + +#### BFS +- Similar to DFS, we wnat to test: 1) test input s valid, 2) remove 1 invalid parenthesis at a time, 3) process substring +- instead of testing all substrings (timeout), we want to establish rules to improve reprocess: + - Test1: skip regular char. No need to test it. + - Test2: if redundant paren, do 1 is enough. skip adjacent ones. + - Test3: if last removed extra paren is '(', the next ')' must be a valid pair. LastRemoved char: pecial handling by using a struct: `class Node {String s, int index, char lastRemoved}` +- How to end tests? When there is data in rst, stop adding to queue. + + + +--- + +**62. [111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%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 Conquer to find min depth. + - if one of child is null, return the other child depth + 1 + - Pick the min of the two child depth + 1 +- need to visit all nodes + + + + +--- + +**63. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**64. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**65. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**66. [694. Number of Distinct Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/694.%20Number%20of%20Distinct%20Islands.java)** Level: Medium Tags: [DFS, Hash Table] + + +#### DFS + HashSet +- DFS can find # of island, just like `200.Number of Islands`, aim to count total +- We need to map same-shap land + - One approach: print the **footprint** starting from coordinate (0, 0) + - Another approach: print the actual island in its boundary, like a QR code. (too hard to code, skip) +- Footprint approach: + - 1. always assume a newly found islands starts from (0, 0) + - 2. take 4 direction from init pos and keep printing the footprint + - 3. Since we always visit nodes from top->right->nextrow, we always visit top-left cornor of a new island, and the footprint will be identical + - Otherwises, if needed, we can sort the footprint and output the hash +- time: O(n), visit all +- space: O(n), store footprint, and dfs stacks worst case visit all nodes + + + +--- + +**67. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**68. [199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%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 +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n nodes in final results + + +#### DFS +- Use Map to override the result at each level +- dfs: + - dfs(node.left) and then dfs(node.right) because we want to log right side last +- record global max depth for iteration purpose +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n stacks (and n nodes in final results) + + + + +--- + +**69. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**70. [1008. Construct Binary Search Tree from Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/1008.%20Construct%20Binary%20Search%20Tree%20from%20Preorder%20Traversal.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top Down DFS +- This approach highly relies on the preorder rules + - we can use validation rules to navigate throug hteh preorder array + - use a global index +- time: O(n) + + + + +--- + +**71. [515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS +- faster than BFS, using less space if not couting final rst: stack size, O(logn) +- time: O(n), visit all + +#### Method2: BFS with queue +- loop over queue level and record max + + + + +--- + +**72. [1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph] + + + +#### BFS +- simply calc each level sum with BFS +- top-level is processed first, since we go from top level -> deeper level + - only update result if sum is truly > global MAX. + + + +--- + +**73. [131. Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/131.%20Palindrome%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) + + + +--- + +**74. [222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, DFS, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### Method1: DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样 + - 如果一样, 直接 2 ^ h - 1就好 + - 不一样的话, 再DFS +- calculate `2^(h)`: 位运算, Math.pow(2, h) = 2 << (h - 1). 神奇! + - 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- time: O(n) visit all nodes on 1 side +- space: O(h) visit all nodes on 1 side + + +#### Method2: Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +#### Method3: Binary Search +- NOT DONE, TODO: https://leetcode.com/problems/count-complete-tree-nodes/solution/ + + + +--- + +**75. [257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%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 +- time: visit all n nodes +- space: to hold all paths, O(nlogn) + - O((n-1)/2) = O(n) nodes at leaf + - O(logn) depth + +#### 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 + + + + +--- + +**76. [1219. Path with Maximum Gold.java](https://github.com/awangdev/LintCode/blob/master/Java/1219.%20Path%20with%20Maximum%20Gold.java)** Level: Medium Tags: [Backtracking, DFS] + + + +### DFS, Backtracking +- typical recursive visit all situation + + + + +--- + +**77. [1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +- time: O(n), visit all nodes +- space: O(logn), height of the tree + +#### Method2: HashMap, DFS. +- traverse tree and create `map ` to fast O(1) removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- This approach requires a dfs build of parentMap + - it is same amount of efforts to do the regular dfs removal. + - not a good solution +- time: O(n) +- space: O(n) + + + +--- + +**78. [339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS, NestedInteger] + + +给一串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. + - 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) + +#### BFS +- bfs, queue, 处理queue.size() for a level +- use a level variable to track levels +- slower since it uses extra space, worst case O(n) of all items + + + +--- + +**79. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + +**80. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**81. [741. Cherry Pickup.java](https://github.com/awangdev/LintCode/blob/master/Java/741.%20Cherry%20Pickup.java)** Level: Hard Tags: [DFS, DP] + + +special hint: `r1 + c1 = constant t = r2 + c2`, if the two points are moving at same time. + +#### DFS + Memo: TOP-DOWN +- Similar concept to Minimum Path Sum +- https://leetcode.com/problems/cherry-pickup/solution/ +- realize r1 + c1 = r2 + c2. Knowing 3 parameters can uniquely identify the 4th. +- assume there are 2 people starting from origin, and the 2 people can go total 4 directions + - perform DFS based on the 4 directions + - concern: do they visit the same spot? possible. when that happens, make sure we do not double count the grid[i][j] +- when is the end state? + - then anyone, for example, (r1,c1) reaches end (n-1, n-1). + - it means the other person also reaches end +- use memo: memo[r1][c1][r2], it records any given (r1, c1, r2, c2) state + + + + +--- + +**82. [104. Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/104.%20Maximum%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 + + + +--- + +**83. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**84. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + +**85. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + +**86. [47. Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/47.%20Permutations%20II.java)** Level: Medium Tags: [Backtracking, DFS] + +space: O(n!) + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +Similar to 46. Permutations, but with dedup + +#### Backtracking +- Sort the list, and on same level, if last element is the same as curr, skip this recursive call +- time O(n!) + +#### 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]在重复时候, 不用重新记录. + + + +--- + +**87. [332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)** Level: Medium Tags: [Backtracking, DFS, Graph] + + +#### DFS with backtcking +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +- time: O(n^n). n = # of cities. worst case, each city has (n-1) edges and need to try all combinations +- space: O(n^2), there can at most be n * (n - 1) edges + + + +--- + +**88. [39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + + + +--- + +**89. [1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)** Level: Hard Tags: [DFS, Stack, String] + +#### Parse exp as sub problem +- Analyze the pattern: 1) single char, 2) with !, 3) with &, | +- Identify sub problem + - Use stack to parse the data in "()", which is a sub problem to solve with recursive call + - Handle &, | case: need to parse multiple +- Be comfortable with string parsing +- Slight improve: + - If see obvious result, directly return evaluation w/o further parsing + - use memo to store evaluated exp + +#### Evaluate inner exp and save back to Stack +- Use '(' and ')' to mark inner exp +- Evaluate the inner exp and save result back to Stack: the result will be 'f' or 't' +- This is slightly slow because: + - It requires all stack items on top to be processed before reaching the operator + - There is no room to optimize even there is simplification for specific operator + + + +--- + +**90. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**91. [110. Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/110.%20Balanced%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + +给一个binary tree, 看是否是height-balanced + +#### DFS with end state +- 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 above solution, but cost more traversal efforts +- 试图计算所有情况 + + + +--- + +**92. [100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### Method1: DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### Method2: BFS with 2 queues +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**93. [112. Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/112.%20Path%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. + + + + +--- + +**94. [1026. Maximum Difference Between Node and Ancestor.java](https://github.com/awangdev/LintCode/blob/master/Java/1026.%20Maximum%20Difference%20Between%20Node%20and%20Ancestor.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top-Down DFS +- cache parent max and min => produce current max and min +- pass the max and min to dfs +- compare and return the max of dfs(left), dfs(right) +- time: O(n) +- space: O(logn) +- easy to write, a bit hard to think of + +#### Method2: Bottom-up DFS +- pass up the local (min, max) as object `Val{max, min}` +- easy to think of, but more code to write + + + +--- + +**95. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**96. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + +**97. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**98. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**99. [114. Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/114.%20Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### Method1: DFS return tail node +- DFS to return the tail of the flattened list +- Careful handling the null child. Choose one and both works: + - Option1: put non-null as right child and continue dfs + - Option2: put non-null as left child and continue dfs + +#### Method2: void DFS +- latten the tree, no extra space. + - 1. Set right node in buffer, and connect: `root.right = root.left`, DFS flatten(root.right) + - 3. 移花接木: traverse to tail of the current root.right and attach the buffer node. + - 3. flatten the remaining of buffer + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**100. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + + +--- + +**101. [430. Flatten a Multilevel Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/430.%20Flatten%20a%20Multilevel%20Doubly%20Linked%20List.java)** Level: Medium Tags: [DFS, Linked List] + + +#### DFS +- Depth-first: + - 1) process curr.child, return tailChild + - 2) connect tailChild.next = curr.next +- function: link(Node a, Node b); + + + +--- + +**102. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**103. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + +**104. [364. Nested List Weight Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/364.%20Nested%20List%20Weight%20Sum%20II.java)** Level: Medium Tags: [DFS, NestedInteger] + + +#### Method1: DFS +- Build a list of NestedInt +- DFS: + - sum up integers in the list are integers + - dfs on nested list + - overallSum = sum * (depth+1) + - End state: if no nested list (no more child dfs), return depth 1 +- Parent level: sum up all ints and times the (depth+1) + + +#### Method2: BFS +- Using stack to flatten all nestedList, and process in the end +- Can actually use list, does not need to be stack. +- uses more memory + + + +--- + +**105. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + +**106. [133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%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: `map` + - 1) make new curr node; + - 2) clone all neibhors and add them +- Use the map to avoid visited nodes +- time: O(n). visit all nodes +- space: O(n). Technically only travels n levels/stacks to circle all nodes (undirected & connected) + +#### 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. + + + +--- + +**107. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**108. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**109. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**110. [1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS, top-down +- key concetpL the `common ancester of deppest leaves` must have its `two branch being same depth`. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +- time: O(n) traversal 1 pass +- space: O(n) dfs worst case depth + +#### Method2: bottom-up, find leaves first and traverse backwards +- 1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +- 2) use leaf nodes to find way backwards till common node is found; return +- time: O(n) but two passes +- space: O(n) dsf + map storage +- this approach is more brutle and uses exrtra spaces + + + +--- + +**111. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + +**112. [785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)** Level: Medium Tags: [BFS, DFS, Garph] + + +#### DFS marking each node with a state +- `bipartite` require each node to be in exact 1 party, which means it only has 1 state +- DFS to mark node with one state; and mark its edges as reversed state + - If any node state has been assigned by different from desired one, return false. + +#### BFS, Queue +- Use `Boolean states[i]` to represent visted & state +- Try all nodes with for loop, and skip visited nodes (similar validation rules as in dfs) +- In `int next : graph[curr]`, test next level first before adding. + + + +--- + +**113. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + +**114. [721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Union Find] + +- time O(mn) + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Method1: 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, 最终用来输出. + +#### Method2: 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) + +### DFS +- TODO + + + +--- + +**115. [101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### Method1: DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Method2: interative with queue +- put left or right children in pair + +#### Method3: iterative with Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**116. [698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)** Level: Medium Tags: [DFS, DP, Recursion] + + +#### Method1: Brutle DFS to find subsets and return results +- Target = total / k, fixed. +- DFS: Pick one num and dfs with the remaining nums for subproblem + - subproblem1: accumulate local sum to target + - EndState: accumulatedSum == target, continue with below + - subproblem2: after accumulatedSum == target, continue dfs with k-1 + - EndState: k == 0, return overall true +- Option1: DFS with nums, and boolean[] visited. Fast +- Option2: DFS with queue. + - Specially handling: dfs(size+1) to prevent `while(size-- >0)` from skipping last item at index 0 + + +#### Method2: DP +- the problem aims to find true/false capability +- bit-masking. TODO. The DP approach is kinda hard-level +- https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/335668/DP-with-Bit-Masking-Solution-%3A-Best-for-Interviews + + + +--- + +**117. [366. Find Leaves of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/366.%20Find%20Leaves%20of%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS: store nodes at the same depth +- the leaves are at depth 0 and the root is at highest depth +- dfs: the depth = index of the rst, start from depth = 0 at leaf +- end state: leaf node, add to rst, and return depth + + + + +--- + +**118. [235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的 path +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Beofre lasts common ancestor is found: p and q should follow same search pattern: + - compare with root, then dfs(left) or dfs(right) + - util p and q fall on two sides of root, then return root + - 非常巧妙, 但是也比较局限; 稍微变条件, 就很难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(logn) extra space: recursive stack space +- O(logn) time + + + +--- + +**119. [156. Binary Tree Upside Down.java](https://github.com/awangdev/LintCode/blob/master/Java/156.%20Binary%20Tree%20Upside%20Down.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS +- Given that left & right nodes are always available in pair, at each level: + - perform dfs to find new root: return deepest left node as root + - pick out curr left node and fix current level: + - add right node as left node + - add root as right node + + + +--- + +**120. [254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)** Level: Medium Tags: [BFS, Backtracking, DFS] + + + +#### Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs + + +#### Method2: BFS +- Check if the number can be devided by [2, sqrt(n)], return a list of possible factors. Only check till `Math.sqrt(n)` + - build suffixes: use the factor to devide last element of list and replace last element + - build candidate: replace last element of the queue item with new list of suffixes; add to rst + - add success item back to queue: in case last element can be simplified +- **remove dupilcates**: since we start factor from [2, sqrt(n)], the final factor list should be **ascending**!! +- time: O(x), x is the # of results +- space: O(y), y is all ongoing candidates in queue + + + +--- + + + + + + + +## Design (27) +**0. [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个数字 + + + +--- + +**1. [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 + + + +--- + +**2. [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 一样) + + + +--- + +**3. [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()里, 每次换水,查看末尾项. + + + + +--- + +**4. [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). + + + + +--- + +**5. [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,可惜错了。 + + + + +--- + +**6. [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 + + + +--- + +**7. [Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)** Level: Easy Tags: [Design] + +让一个class 是 singleton + + + +--- + +**8. [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,也是类似做法 + + + +--- + +**9. [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] + + +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 +- + + + +--- + +**10. [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本身. + + + +--- + +**11. [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了. + + + + +--- + +**12. [359. Logger Rate Limiter.java](https://github.com/awangdev/LintCode/blob/master/Java/359.%20Logger%20Rate%20Limiter.java)** Level: Easy Tags: [Design, Hash Table] + + +#### HashMap +- map: avoid duplicate message, records timestamp for validation +- time: O(1) +- space: O(n) + +#### Queue + Set +- 1) keep a trimmed queue and set (all tasks to be within 10 secs); +- 2) use set to O(1) check if incoming message exists. +- time: O(x), trimQueue() +- space: O(n) + + + +--- + +**13. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**14. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**15. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**16. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**17. [211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +#### Trie, prefix tree. +- Trie Structure: `boolean isEnd`, `HashMap children` + - trie.addWord: 没node就加,有node就移动 + - trie.search: 没node就return false,有node就移动 +- Alternatively, the hash can be `TrieNode[26]` a fixed size array when applicable + - I like map better for the simplicity to write (w/o converting char -> index) + + + + +--- + +**18. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**19. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**20. [170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)** Level: Easy Tags: [Design, Hash Table, Memoization] + + +#### Hash Table, Memo +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +- time: O(n), loop over all elements in map +- space: O(n), store all elements in map & memoization set + + + +--- + +**21. [432. All One Data Structure.java](https://github.com/awangdev/LintCode/blob/master/Java/432.%20All%20One%20Data%20Structure.java)** Level: Hard Tags: [Design, Doubly Linked List] + + +#### Doubly Linked List +- IMPORTANT: the problem aims to put keys of same frequency in same node! This affects the design of node +- Main a class `Node {keySet, count, last/next pointers}` +- Each operation: + - 1) finds target node and extract the key + - 2) calculate: count +/- 1 + - 3) find new spot to store the key (prior positions or later positions) +- Be careful when handling the cases in inc() and dec() + + + +--- + +**22. [380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + + +#### Hash Table, Swap when removing +- Map: `map, Lis: tracks `index->value` +- list maintain 用来 insert/remove/random operations. +- Remove: swap input valueIndex & tialIndex = list.size() -1. + - list.remove(object) 应该是要O(logn) 做一个search的. + - list.remove(list.size() - 1) is cheapter + + + +--- + +**23. [346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison +- Note: if we it is calculate moving-window-product, better to use deque :) +- Sliding window? + - It has the spirit of slinding window: 1) maintain a range; 2) check range size `if (queue.size() > size)` + - Though, the solution must use a data structure to store data; it is not the traditional sliding window type of `left/right` pointer problem + + + +--- + +**24. [208. Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/208.%20Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- Trie Structure: + - trace the char to children node: Map + - boolean isEnd to indicate if there is string end with this node + - `TrieNode {boolean isEnd; Map children}`. +- No need to store letter c in TrieNode +- 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。 + + + + + +--- + +**25. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + +**26. [341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, NestedInteger, Stack] + + +#### Method1: Stack holds items from back of the list +- Option1: always set integer on top of the stack everywhere + - if not, poping stack until the top is integer + - code is easy +- Option2: in hasNext(), faltten the list in stack + +#### Method2: DFS preprocessing. +- 用queue to store all items. Kinda hack. Defeat the purpose of the problem. +- Super fast to query next(), however, needs to holds everything in memory +- O(n) + + + +--- + + + + + + + +## Game Theory (4) +**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. [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. [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 + + + +--- + +**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 (3) +**0. [[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)** Level: Hard Tags: [HashHeap, Heap, Lint] + +非题.是从九章找来的HashHeap implementation. + +#### HashHeap +- An efficient implementation of a priority queue. +- The linear hash function monotonically maps keys to buckets, and each bucket is a heap +- https://xlinux.nist.gov/dads/HTML/hashheap.html + + + +--- + +**1. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**2. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + + + + + + + +## Backtracking (35) +**0. [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了 + + + +--- + +**1. [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) + + + +--- + +**2. [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 + + + + +--- + +**3. [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许多。 + + + + +--- + +**4. [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 + + + +--- + +**5. [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开始选。 + + + +--- + +**6. [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一下。 + + + + +--- + +**7. [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, 碰壁的时候就回头走。 + + + +--- + +**8. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**9. [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};` + + + +--- + +**10. [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] + + + + +--- + +**11. [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 一样 + + + +--- + +**12. [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的情况。要把遍历的例子写写 + + + +--- + +**13. [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里面, 这个想法非常值得思考. + + + +--- + +**14. [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)))` + + + +--- + +**15. [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 + + + +--- + +**16. [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。 + + + +--- + +**17. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**18. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**20. [131. Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/131.%20Palindrome%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. [257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%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 +- time: visit all n nodes +- space: to hold all paths, O(nlogn) + - O((n-1)/2) = O(n) nodes at leaf + - O(logn) depth + +#### 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 + + + + +--- + +**22. [1219. Path with Maximum Gold.java](https://github.com/awangdev/LintCode/blob/master/Java/1219.%20Path%20with%20Maximum%20Gold.java)** Level: Medium Tags: [Backtracking, DFS] + + + +### DFS, Backtracking +- typical recursive visit all situation + + + + +--- + +**23. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**24. [51. N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/51.%20N-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 +- Backtracking by replacement: each row has 1 queen, so just store it in int[] columns (CC book solution) + +#### 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 + + + + +--- + +**25. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + +**26. [211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +#### Trie, prefix tree. +- Trie Structure: `boolean isEnd`, `HashMap children` + - trie.addWord: 没node就加,有node就移动 + - trie.search: 没node就return false,有node就移动 +- Alternatively, the hash can be `TrieNode[26]` a fixed size array when applicable + - I like map better for the simplicity to write (w/o converting char -> index) + + + + +--- + +**27. [47. Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/47.%20Permutations%20II.java)** Level: Medium Tags: [Backtracking, DFS] + +space: O(n!) + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +Similar to 46. Permutations, but with dedup + +#### Backtracking +- Sort the list, and on same level, if last element is the same as curr, skip this recursive call +- time O(n!) + +#### 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]在重复时候, 不用重新记录. + + + +--- + +**28. [332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)** Level: Medium Tags: [Backtracking, DFS, Graph] + + +#### DFS with backtcking +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +- time: O(n^n). n = # of cities. worst case, each city has (n-1) edges and need to try all combinations +- space: O(n^2), there can at most be n * (n - 1) edges + + + +--- + +**29. [39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + + + +--- + +**30. [10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + + + +--- + +**31. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**32. [52. N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/52.%20N-Queens%20II.java)** Level: Hard Tags: [Backtracking] + + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking (with replacement) +- Each row has just 1 Queen value +- As CC book suggests, use `int[] columns` of length n to store all queen col positions for n rows + - `int[] columns` is slightly easier to backtrack by updating certain index i with new col + - list will usualy has the add/remove pattern for backtracking + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + + + + +--- + +**33. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + +**34. [254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)** Level: Medium Tags: [BFS, Backtracking, DFS] + + + +#### Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs + + +#### Method2: BFS +- Check if the number can be devided by [2, sqrt(n)], return a list of possible factors. Only check till `Math.sqrt(n)` + - build suffixes: use the factor to devide last element of list and replace last element + - build candidate: replace last element of the queue item with new list of suffixes; add to rst + - add success item back to queue: in case last element can be simplified +- **remove dupilcates**: since we start factor from [2, sqrt(n)], the final factor list should be **ascending**!! +- time: O(x), x is the # of results +- space: O(y), y is all ongoing candidates in queue + + + +--- + + + + + + + +## Hash Table (86) +**0. [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 + + + +--- + +**1. [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 + + + +--- + +**2. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + + +给一串点, 找是否有一个所有点中间的, 跟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) + + + +--- + +**3. [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)}` + + + +--- + +**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. [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. + + + +--- + +**6. [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). + + + + +--- + +**7. [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? + + + +--- + +**8. [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 + + + +--- + +**9. [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. + + + +--- + +**10. [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 +- 都是基本操作, 概念实现 + + + +--- + +**11. [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) + + + +--- + +**12. [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? + + + +--- + +**13. [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 +还要做一下那. + + + +--- + +**14. [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' + + + + +--- + +**15. [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的益处。 + + +--- + +**16. [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) + + + +--- + +**17. [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] + + +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 +- + + + +--- + +**18. [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] + + +#### 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. + + + +--- + +**19. [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` + + + +--- + +**20. [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本身. + + + +--- + +**21. [Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)** Level: Medium Tags: [Hash Table] + + +给一面墙, 每一行是一行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 + + + +--- + +**22. [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. + + + +--- + +**23. [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 + + + +--- + +**24. [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一下。 + + + +--- + +**25. [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) + + + +--- + +**26. [[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)** Level: Easy Tags: [Hash Table, Lint, String] + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**27. [[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)** Level: Medium Tags: [Array, Hash Table, Lint] + + +把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. + + + + +--- + +**28. [[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Lint, 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! + + + + +--- + +**29. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**30. [[tool]. Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Hash%20Function.java)** Level: Easy Tags: [Hash Table, Lint] + + +In general, there is no universal recipe to stick to when it comes to implementing hashCode(). +https://www.baeldung.com/java-hashcode + +#### 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会变得太大,所以不能算完和 再 %... + + + +--- + +**31. [36. Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/36.%20Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited row/col/block. + - 在nest for loop里面validate row,col,and block. + - Special: validate block要利用i 和 j 增长的规律 +- i, j are [0~n) can build block boundary in a 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 +- 可能代码稍微复杂一点 + + + +--- + +**32. [359. Logger Rate Limiter.java](https://github.com/awangdev/LintCode/blob/master/Java/359.%20Logger%20Rate%20Limiter.java)** Level: Easy Tags: [Design, Hash Table] + + +#### HashMap +- map: avoid duplicate message, records timestamp for validation +- time: O(1) +- space: O(n) + +#### Queue + Set +- 1) keep a trimmed queue and set (all tasks to be within 10 secs); +- 2) use set to O(1) check if incoming message exists. +- time: O(x), trimQueue() +- space: O(n) + + + +--- + +**33. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**34. [953. Verifying an Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/953.%20Verifying%20an%20Alien%20Dictionary.java)** Level: Easy Tags: [Hash Table] + + +#### Hash Table +- mark the char position +- check adjacent words +- Optimization + - a) If s1 equals s2, just return true, no need to continue. + - b) if s2 (app) is a substring of s1(apple), just return false. + + + + +--- + +**35. [1213. Intersection of Three Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/1213.%20Intersection%20of%20Three%20Sorted%20Arrays.java)** Level: Easy Tags: [Hash Table, Two Pointers] + + +Very similar to 349.Intersection of Two Arrays. + +#### Hash Table +- Use set to check +- Verify duplicates at end rst + +#### Two Pointers +- similar to Intersection of Two Sorted Arrays +- Start from front/back, process 1 item at a time + - if match, move all pointers +- Optoin1: check from back +- Optoin2: check from frotn + + + +--- + +**36. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**37. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**38. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**39. [204. Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/204.%20Count%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个数就好了 + + + +--- + +**40. [496. Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/496.%20Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack +- use stack to hold all elements + - keep poping if `stack.peek() < num` + - use map to record (top, num) +- time O(n), run through base once and sub-sequence once +- space O(n), stack, map + +#### HashMap +- O(n) space, O(n^2) time worst case + + + +--- + +**41. [694. Number of Distinct Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/694.%20Number%20of%20Distinct%20Islands.java)** Level: Medium Tags: [DFS, Hash Table] + + +#### DFS + HashSet +- DFS can find # of island, just like `200.Number of Islands`, aim to count total +- We need to map same-shap land + - One approach: print the **footprint** starting from coordinate (0, 0) + - Another approach: print the actual island in its boundary, like a QR code. (too hard to code, skip) +- Footprint approach: + - 1. always assume a newly found islands starts from (0, 0) + - 2. take 4 direction from init pos and keep printing the footprint + - 3. Since we always visit nodes from top->right->nextrow, we always visit top-left cornor of a new island, and the footprint will be identical + - Otherwises, if needed, we can sort the footprint and output the hash +- time: O(n), visit all +- space: O(n), store footprint, and dfs stacks worst case visit all nodes + + + +--- + +**42. [136. Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/136.%20Single%20Number.java)** Level: Easy Tags: [Bit Manipulation, Hash Table] + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + + + +--- + +**43. [1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)** Level: Medium Tags: [Bucket Sort, DP, Hash Table, Sort] + + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**44. [299. Bulls and Cows.java](https://github.com/awangdev/LintCode/blob/master/Java/299.%20Bulls%20and%20Cows.java)** Level: Medium Tags: [Hash Table] + + +#### Solution1: use int[10] to count frequency +1. check match chars +1. check unmatched chars by counting and offset their frequency + - count++ on secret chars: if secretCount is ever < 0 => `char g` has match, then cows++ + - count-- on guess chars: if guessCount is ever >0 => `char s` has match, then cows++ + +#### Solution2: Use hashmap to count +- Improvement: since all digit, use int[10] to count + + + +--- + +**45. [266. Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/266.%20Palindrome%20Permutation.java)** Level: Easy Tags: [Hash Table] + + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count char occurrance + - 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assume lower case letter. 应该至少是所有ASCII code + + + +--- + +**46. [311. Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/311.%20Sparse%20Matrix%20Multiplication.java)** Level: Medium Tags: [Hash Table] + + +给两个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 + + + +--- + +**47. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**48. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**49. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**50. [771. Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/771.%20Jewels%20and%20Stones.java)** Level: Easy Tags: [Hash Table] + + +- 给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 +- Basic HashSet + + + +--- + +**51. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**52. [387. First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/387.%20First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +#### Count appearance with int[256] +- 按照题意, 找到第一个 first index == last index的字母. + +#### Count appearance with hashmap (more scalable) +- 用hashmap存字母的index, 有些重复字母的index就会是个list. +- 找到单一index, 结合成list, sort, return list.get(0) +- slow due + + + +--- + +**53. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**54. [246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math, Two Pointers] + + +根据题意枚举, 再根据规则basic implementation + +#### HashTable + Two Pointer +- compare left/right + +#### Alter input +- flip number (6 and 9), and then reverse the string, see if the string is the same. +- takes more + + + + +--- + +**55. [463. Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/463.%20Island%20Perimeter.java)** Level: Easy Tags: [Hash Table] + +#### Brutle, Count Blocks and Walls +- 每个格子 +4 个墙; +- 每个shared的墙要减去: 从每个island走去另外一个, 都-1 (最终没面墙, -2) + +#### Hash Table +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么就可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不大,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + + + +--- + +**56. [170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)** Level: Easy Tags: [Design, Hash Table, Memoization] + + +#### Hash Table, Memo +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +- time: O(n), loop over all elements in map +- space: O(n), store all elements in map & memoization set + + + +--- + +**57. [981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)** Level: Medium Tags: [Binary Search, Hash Table, TreeMap] + + +#### Method1: Binary Search +- use hash to store +- binary serach on list of values + +#### Method2: TreeMap +- use hash to store > +- treemap.floorKey(timestamp) finds the top item below certain timestamp + + + +--- + +**58. [202. Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/202.%20Happy%20Number.java)** Level: Easy Tags: [Hash Table, Math] + + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + + + +--- + +**59. [380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + + +#### Hash Table, Swap when removing +- Map: `map, Lis: tracks `index->value` +- list maintain 用来 insert/remove/random operations. +- Remove: swap input valueIndex & tialIndex = list.size() -1. + - list.remove(object) 应该是要O(logn) 做一个search的. + - list.remove(list.size() - 1) is cheapter + + + +--- + +**60. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**61. [219. Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/219.%20Contains%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 +- Time O(nm), m = # of duplicates. 太慢 +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k + + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**62. [205. Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/205.%20Isomorphic%20Strings.java)** Level: Easy Tags: [Hash Table] + + +#### HashMap +- check 2 failture cases: + - same key, value not matching + - two key maps to same value + + + +--- + +**63. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**64. [242. Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/242.%20Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +#### int[26] + +#### HashMap + + + +--- + +**65. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**66. [217. Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/217.%20Contains%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 + + + +--- + +**67. [1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)** Level: Medium Tags: [Array, Hash Table, TreeMap] + + + +#### Hash Table, TreeMap; atomic save +- store efficiently: use List>. only preserve changed itemd +- if no match, find last modifed item based on snapId, use TreeMap.floorEntry + - map.floorEntry(id) return the item.key lower or equal to id +- Utilize a `buffer: Map` and perform atomic save + + + +--- + +**68. [1. Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1.%20Two%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. + + + + +--- + +**69. [139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- Bottom-up, test simply case. Sequence DP. +- true/false problem, think about dp + - 子问题: 前i个字母, 是否可以有valid break + - check: 1) dp[j] && 2) `if substring(j, i) valid`, for all j = [0 ~ i] + - dp = new boolean[n + 1]; dp[0] = true; + - test: `dp[i] |= dp[j] == true && word[j, n] in dict`. + - Need iterate over i = [0 ~ n], also j = [0, i] + - When there is a way to make dp[i] == true, then break the [j ~ i] loop, move on to test dp[i++] +- Use set dict: `dict.contains()` +- Improvement: O(n) to figure out max length, so we can skip some substring[j~i] dict.contains() +- overall O(n^2) time since the double for loop + +#### DFS +- Top-Down, break into small problems: Check front subString, and put the rest substring into dfs to test +- Memoization: for tested failed substring, record and do NOT test them again. +- Same Improvement as in DP: use max/min length of dict words as boundary + + + +--- + +**70. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**71. [274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.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. + +#### Couting Sort / 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 + +#### More about Counting Sort +1. Application: for number/character range +1. Steps: + - Find range, define countArray + - Count element and record in the array + - PreSum the countArray + - Start from beginning of the array, `print & decrese count` to produce the sorted elements + + + + +--- + +**72. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**73. [49. Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/49.%20Group%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 (too slow) +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**74. [720. Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/720.%20Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序(lexicographically), 排序以后才能逐个看是否partial string已经存在 + - 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 + - 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: + - 长 string 排在前; + - 相等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) + +#### 从最长的word开始做 +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**75. [438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)** Level: Medium Tags: [Hash Table, Sliding Window, Two Pointers] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### Method1: Sliding Window, Hash Table. +- A creative way of using anagram char count `hash[c] >= 0` to determine if the curr c is a target char of the deesired anagram. + - because we always reduce hash[c]-- for all characters + - so only the anagram chars would be `hash[c] >= 0` after reducing. +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +- Slinding window always has left/right pointer: + - 1) at any given time move 1 index at a time: expand right window, process rsult, shrink left window + - 2) one of the basic goal is to maintain fixed window size +- algo: + - calc char freq of the target p, and store in a hash[256]; it will be used to distinguish anagram chars: `hash[c] >= 0` indicates a anagram char + - expand right window: move right to expand the window; ONLY when meeting a anagram char, count-- + - process result: if count reduces to 0, one anagram is found + - shrink left window: if (right - left) == p.length(), drop curr left char, and move forward +- how could we rely on only just `count == 0`? + - the hidden pre-condition is `right - left must already be p.length()`, which is validaterd in prev iteration +- time: O(n) +- space: O(1) + +#### Method2: HashTable +- count character apperance -> hash table, here just a int[26] + - use a window to record count++ and count--, in order to compare with countP +- prep the countP takes O(m) time +- time: O(n) + O(m) +- space: O(n) + + + + + +--- + +**76. [632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)** Level: Hard Tags: [Hash Table, Sliding Window, Two Pointers] + + +#### Method1: Sliding Window +- First sort all of the items together by actual val using `Node {int val, int row}` +- Slinding window goal: + - 1) use right to find range that touches all rows, + - 2) use left to shrink the range +- Sliding Window Template + - move right pointer + - Counts[i] = # of elements used in left/right range + - when counts[i] == 0, countUnique++; the number of row/list being included + - when count == row size: + - processing & save shorter range by using left/right Pointers + - move left pointer; when counts[i] == 0, countUnique-- +- time: O(nlogn) for initial sort and then O(n) to process +- space: O(n) +- What is hard here? To think of the idea of counting one usage of each row: + - when each all rows are used at least 1 time + - calculate the min dist + +#### Method2 PQ?, Similar to merging k array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104893/Java-Code-using-PriorityQueue.-similar-to-merge-k-array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/solution/ + + + +--- + +**77. [138. Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/138.%20Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List, time, space: O(n) +- Basic Implementation of copy linked list: + - use a iterator node to iterate over the list: 遍历head.next .... null. + - use a dummy node to hold reference to the iterator node. +- Map: 1. avoid creating same node; 2. return the new node if existing + - 每一步都check map里面有没有head. 没有? 加上 + - 每一步都check map里面有没有head.random. 没有? 加上 +- Note, there is a way to skip the extra map O(n): https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43491/A-solution-with-constant-space-complexity-O(1)-and-linear-time-complexity-O(N) + - However, creating a deep clone of the list is already O(n) extra space, so it is NOT effectively O(1) w/o map + - It may be beneficial, if we can not hold all nodes in memory, then the approach w/o map is more applicable. + + + +--- + +**78. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + +**79. [760. Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/760.%20Find%20Anagram%20Mappings.java)** Level: Easy Tags: [Hash Table] + + +- HashMap 存index list +- 遍历一遍数组A, 列举出所有元素 + + + +--- + +**80. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**81. [94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Method1: DFS +- option1: dfs + rst list to carry results +- option2: Divide and Conquer, 在自己的基础上recursive, 不用helper function +- O(n) time + +#### Method2: Iterative, Stack inorder traversal +- 1) Add root.leftPath all the way to leaf, 2) process curr 3) Move to right if applicable 4) add all right.leftPath +- O(n) time, O(h) space + + + + +--- + +**82. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**83. [721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Union Find] + +- time O(mn) + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Method1: 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, 最终用来输出. + +#### Method2: 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) + +### DFS +- TODO + + + +--- + +**84. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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" + + + +--- + +**85. [739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)** Level: Medium Tags: [Hash Table, Monotonous Stack, Stack] + + +#### Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) + +#### Method2: `Map `, kinda of like bucket sort +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: + - 1) record tempIndex[currTemp] = i; + - 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result + + +--- + + + + + + + +## 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 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)))` + + + +--- + +**2. [39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + + + +--- + +**3. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + + + + + + + +## TreeSet (4) +**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. + + + +--- + +**2. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**3. [715. Range Module.java](https://github.com/awangdev/LintCode/blob/master/Java/715.%20Range%20Module.java)** Level: Hard Tags: [Segment Tree, TreeSet] + + +#### TreeSet +- start with considering array structure but operation are all O(n) + - what if we can easily find range, and update +- TreeSet: + - build a class `Interval {int start, end;}` + - build a customized `compareTo` that sorts the interval by start at default, but sort by end if a.start==b.start + - Query: TreeSet allow us to find element in O(logn) + - Add Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + - Remove Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + + + +--- + + + + + + + +## Tree (69) +**0. [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. + + + +--- + +**1. [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. + + + +--- + +**2. [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 + + + +--- + +**3. [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的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**4. [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. + + + +--- + +**5. [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 + + + +--- + +**6. [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 + + + +--- + +**7. [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开头 + + + +--- + +**8. [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的头。 + + + + + +--- + +**9. [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 + + + +--- + +**10. [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. + + + +--- + +**11. [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. + + + +--- + +**12. [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. + + + +--- + +**13. [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加上去就好. + + + +--- + +**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 + + + +--- + +**15. [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的基础上, 比对左左,左右,右左,右右 + + + +--- + +**16. [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 + + + +--- + +**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. [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? + + + +--- + +**19. [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的判断 + + + +--- + +**20. [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的情况。要把遍历的例子写写 + + + +--- + +**21. [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 + + + + +--- + +**22. [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] + + +给一个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, 永远在左边上下上下。 + + + +--- + +**23. [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] + + +给一个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 + + + +--- + +**24. [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` + + + +--- + +**25. [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; + + + + +--- + +**26. [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 + + + +--- + +**27. [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.valleaf 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) + + + +--- + +**29. [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] + + +给一个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才可以施行. + + + +--- + +**30. [[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Lint, 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! + + + + +--- + +**31. [102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### Method1: BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### Method2: DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**32. [236. Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/236.%20Lowest%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 + - Base Case: 当root == null, p or q is found (`root == p || root == q`),那么就return the root as LCA + - 三种情况: + - 1. leftLCA and rightLCA all found: `each path has found one of p and q node as LCA`. Therefore, curr root is the lowest ancestor + - 2. One of leftLCA and rightLCA is found: return whichever one found + - 3. both LCAs are null, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time O(n), stack space O(n) + + + +--- + +**33. [111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%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 Conquer to find min depth. + - if one of child is null, return the other child depth + 1 + - Pick the min of the two child depth + 1 +- need to visit all nodes + + + + +--- + +**34. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**35. [429. N-ary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/429.%20N-ary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Tree] + + +#### BFS +- use queue to hold each level. O(n) + + + +--- + +**36. [199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%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 +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n nodes in final results + + +#### DFS +- Use Map to override the result at each level +- dfs: + - dfs(node.left) and then dfs(node.right) because we want to log right side last +- record global max depth for iteration purpose +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n stacks (and n nodes in final results) + + + + +--- + +**37. [1008. Construct Binary Search Tree from Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/1008.%20Construct%20Binary%20Search%20Tree%20from%20Preorder%20Traversal.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top Down DFS +- This approach highly relies on the preorder rules + - we can use validation rules to navigate throug hteh preorder array + - use a global index +- time: O(n) + + + + +--- + +**38. [515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS +- faster than BFS, using less space if not couting final rst: stack size, O(logn) +- time: O(n), visit all + +#### Method2: BFS with queue +- loop over queue level and record max + + + + +--- + +**39. [222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, DFS, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### Method1: DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样 + - 如果一样, 直接 2 ^ h - 1就好 + - 不一样的话, 再DFS +- calculate `2^(h)`: 位运算, Math.pow(2, h) = 2 << (h - 1). 神奇! + - 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- time: O(n) visit all nodes on 1 side +- space: O(h) visit all nodes on 1 side + + +#### Method2: Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +#### Method3: Binary Search +- NOT DONE, TODO: https://leetcode.com/problems/count-complete-tree-nodes/solution/ + + + +--- + +**40. [543. Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/543.%20Diameter%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 and CombinedPath +- Option1: Use local single path max & global combined max + - Since the local combined diameter is used for comparision, but not used for specific calculation + - calculate path length (diameter), understand: + - for single path: child single path value + 1 (curr node) + - for combined path including curr node: left child single + right child path +- Option2: record local combined and single path for each iteration + - `int[]{combinedPath, singlePath}`; + - single path: pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; + - combined path `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`, find max from: + - 1) complete left child combined path + - 2) complete right child combined path + - 3) combined path with curr root + - Note: we treat a single node itself with diameter of 1, so we want to -1 in final result + - problem statement wants the path length (not # of nodes or depth) + + + +--- + +**41. [1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +- time: O(n), visit all nodes +- space: O(logn), height of the tree + +#### Method2: HashMap, DFS. +- traverse tree and create `map ` to fast O(1) removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- This approach requires a dfs build of parentMap + - it is same amount of efforts to do the regular dfs removal. + - not a good solution +- time: O(n) +- space: O(n) + + + +--- + +**42. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**43. [104. Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/104.%20Maximum%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 + + + +--- + +**44. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**45. [270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +Concept: Iterate over all logN nodes in the BST and record the closest. Rather than finding the value at +/- 0.5 precision + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, until null leaf +- time: O(logn), space O(1) since no extra space used + +#### DFS, Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return +- time: O(logn), space: O(logn) + + + + +--- + +**46. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**47. [110. Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/110.%20Balanced%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + +给一个binary tree, 看是否是height-balanced + +#### DFS with end state +- 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 above solution, but cost more traversal efforts +- 试图计算所有情况 + + + +--- + +**48. [100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### Method1: DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### Method2: BFS with 2 queues +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**49. [112. Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/112.%20Path%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. + + + + +--- + +**50. [427. Construct Quad Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/427.%20Construct%20Quad%20Tree.java)** Level: Medium Tags: [Tree] + + +#### Basic Impl +- build tree recursively by definition +- O(n^2) time and space due to single visit to all nodes + + +--- + +**51. [1026. Maximum Difference Between Node and Ancestor.java](https://github.com/awangdev/LintCode/blob/master/Java/1026.%20Maximum%20Difference%20Between%20Node%20and%20Ancestor.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top-Down DFS +- cache parent max and min => produce current max and min +- pass the max and min to dfs +- compare and return the max of dfs(left), dfs(right) +- time: O(n) +- space: O(logn) +- easy to write, a bit hard to think of + +#### Method2: Bottom-up DFS +- pass up the local (min, max) as object `Val{max, min}` +- easy to think of, but more code to write + + + +--- + +**52. [145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Method1: DFS, Recursive +- trivial, 先加left recursively, 再加right recursively, 然后组成头部. +- Option1 w/o helper; option2 with dfs helper. + +#### Method2, Iterative, Stack +- Option1: reversely add to list +- 双stack的思想, 需要在图纸上画一画 + - 原本需要的顺序是: 先leftChild, rightChild, currNode. + - 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild + - 这样出来的结果是reverse的, 那么翻转一下就可以了. +- reverse add: `list.add(0, x)`; +- 利用stack的特点 + - 每次加element进stack的时候, 想要在 bottom/后process的, 先加 + - 想要下一轮立刻process的, 最后push进stack. +- Option2: regular sequence add to stack: add curr, right, left + - Use set to contain the processed children + - only process curr if its children is processed + + + + +--- + +**53. [938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)** Level: Easy Tags: [BST, Recursion, Tree] + +#### DFS based on BST rules +- sum up the matching L & R + - Find (L,R) on left child + - Find (L,R) on right child + - Find (L,R) covering the root node +- space O(n), worst case O(logn), height of dfs. +- time O(n) to find all nodes between (L, R) + +#### Iterative +- Using stack, or queue, list: any data structure (we are not doing ordered search) +- space O(n) +- time O(n) + + + +--- + +**54. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**55. [103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + + +#### 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 + + + +--- + +**56. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**57. [449. Serialize and Deserialize BST.java](https://github.com/awangdev/LintCode/blob/master/Java/449.%20Serialize%20and%20Deserialize%20BST.java)** Level: Medium Tags: [Tree] + + +#### DFS, Divide and Conquer, Preorder (utilizing BST) +- with BST, we can: + - skip adding the null nodes into the serialized string: `String NULL = "#"` + - In deserialization: use min/max boundary to check if queue.peek() can be added: + - if not meeting BST condition, skip this dfs and let other call to consume the queue +- Faster because it shortens the serialized string + + +#### DFS, Divide and Conquer, Preorder (w/o using BST) +- Take reference in Serialize and Deserialize Binary Tree +- The approach works but does not utilize Binary Search Tree properties + + + +--- + +**58. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**59. [94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Method1: DFS +- option1: dfs + rst list to carry results +- option2: Divide and Conquer, 在自己的基础上recursive, 不用helper function +- O(n) time + +#### Method2: Iterative, Stack inorder traversal +- 1) Add root.leftPath all the way to leaf, 2) process curr 3) Move to right if applicable 4) add all right.leftPath +- O(n) time, O(h) space + + + + +--- + +**60. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**61. [1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS, top-down +- key concetpL the `common ancester of deppest leaves` must have its `two branch being same depth`. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +- time: O(n) traversal 1 pass +- space: O(n) dfs worst case depth + +#### Method2: bottom-up, find leaves first and traverse backwards +- 1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +- 2) use leaf nodes to find way backwards till common node is found; return +- time: O(n) but two passes +- space: O(n) dsf + map storage +- this approach is more brutle and uses exrtra spaces + + + +--- + +**62. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + +**63. [101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### Method1: DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Method2: interative with queue +- put left or right children in pair + +#### Method3: iterative with Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**64. [671. Second Minimum Node In a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/671.%20Second%20Minimum%20Node%20In%20a%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +#### BFS +- min tree: parent node is the min of left/right child +- BFS to traverse the tree and find 1st non-root smallest val +- Improvement area: when `node.val >= nextMin`, no need to dive into node children since it is a min Tree. + +#### DFS +- Find left and right val: + - if left/right equals root.val, that means the left or right sub children could have larger number + - Therefore DFS into left or right +- compare and return min(left, right) + + + +--- + +**65. [366. Find Leaves of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/366.%20Find%20Leaves%20of%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS: store nodes at the same depth +- the leaves are at depth 0 and the root is at highest depth +- dfs: the depth = index of the rst, start from depth = 0 at leaf +- end state: leaf node, add to rst, and return depth + + + + +--- + +**66. [235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的 path +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Beofre lasts common ancestor is found: p and q should follow same search pattern: + - compare with root, then dfs(left) or dfs(right) + - util p and q fall on two sides of root, then return root + - 非常巧妙, 但是也比较局限; 稍微变条件, 就很难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(logn) extra space: recursive stack space +- O(logn) time + + + +--- + +**67. [156. Binary Tree Upside Down.java](https://github.com/awangdev/LintCode/blob/master/Java/156.%20Binary%20Tree%20Upside%20Down.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS +- Given that left & right nodes are always available in pair, at each level: + - perform dfs to find new root: return deepest left node as root + - pick out curr left node and fix current level: + - add right node as left node + - add root as right node + + + +--- + +**68. [272. Closest Binary Search Tree Value II.java](https://github.com/awangdev/LintCode/blob/master/Java/272.%20Closest%20Binary%20Search%20Tree%20Value%20II.java)** Level: Hard Tags: [Stack, Tree] + + +#### Method1: Stack, DFS, Inorder Traversal +- find successors and predecessors using BST (both list will be sorted); in the end, we can easily get top k from the two sorted list + - with BST: **inorder traversal gives us sorted predecessors + - with BST: **reversed-inorder traversal gives us sorted successors + - smallest on top of the stack +- time: O(n) visit all nodes, O(k) to output +- space overall: O(n) to store all nodes + +#### Method2: BFS, brutle force +- Itereate over all nodes and maintain pq (improvemenet point: how to avoid traversing entire tree?) +- prioritize nodes that are closer to target, so we may stop early when result reaches k candidates +- time: O(n*logn) +- kinds slow and not utilizing BST + + + +--- + + + + + + + +## Sort (31) +**0. [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), 排序 + + + +--- + +**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. [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/ + + + +--- + +**3. [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来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**4. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + +--- + +**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. [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) + + + +--- + +**7. [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看起来更容易理解. + + + +--- + +**8. [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在某个点小了,加进去当下这个空隙。 + + + +--- + +**9. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**10. [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 + + + +--- + +**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. [[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)** Level: Medium Tags: [Lint, 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) + + + +--- + +**13. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**14. [252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### Method1: sort input and compare if curr.end & next.start overlaps +- sort: `Arrays.sort(intervals, Comparator.comparing(i -> i[0]))` +- time: O(nlogn), space: O(1) + +#### Method2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 +- time: O(nlogn), space O(n) +- Not necessary for this problem, since it requires extra space with pq. + + + +--- + +**15. [259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +1. Similar to 15. 3Sum, but simpler. +1. 只需要count triplet, 但是不需要save triplet, 而且还不需要handle duplicated triplets +1. 发现start, end满足条件时候,(end - start)就是所有 sum target, 那么就end-- +1. 两层循环, O(n2) + + + +--- + +**16. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**17. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**18. [1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)** Level: Medium Tags: [Bucket Sort, DP, Hash Table, Sort] + + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**19. [15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +#### sort array, for loop + two pointer +- 处理duplicate wthin triplets: + - 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. + - 如果是triplet内有重复, 用完start point, 移动到结尾. +- Note: + - 1. 找 value triplets, 多个结果。注意,并非找index。 + - 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + - 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 +- 时间 O(n^2), 两个nested loop + +#### For loop + 2Sum +- HashMap 2Sum. Remember to handle duplicates + - 1. For loop 挑个数字A + - 2. 2Sum 出一堆2个数字的结果 + - 3. Cross match 步骤1里面的A + + + +--- + +**20. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**21. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**22. [973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)** Level: Medium Tags: [Divide and Conquer, Heap, Sort] + + +#### PriorityQueue +- Create customized Point{} class +- Sort by distance +- Maintain queue size <= K + +#### Divide and Conquer +- ?, select sort? + + + +--- + +**23. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**24. [242. Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/242.%20Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +#### int[26] + +#### HashMap + + + +--- + +**25. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**26. [1033. Moving Stones Until Consecutive.java](https://github.com/awangdev/LintCode/blob/master/Java/1033.%20Moving%20Stones%20Until%20Consecutive.java)** Level: Easy Tags: [Basic Implementation, Sort] + + +#### Analyze to understand +- put 3 elements into array, sort and follow below rules: +- min: + - if 3 elements consecutive, 0 move. + - if only 1 pair of the two elemnets consecutive or if they have 1 slot in between, it needs exactly 1 move + - otherwise, at most 2 moves +- max: # of open slots between them (high - low + 1) - n, where n = 3 +- Follow up: `1040. Moving Stones Until Consecutive` is more interesting with special rulese (cannot move to `ending spot`), and it uses sliding window concept + + + +--- + +**27. [274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.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. + +#### Couting Sort / 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 + +#### More about Counting Sort +1. Application: for number/character range +1. Steps: + - Find range, define countArray + - Count element and record in the array + - PreSum the countArray + - Start from beginning of the array, `print & decrese count` to produce the sorted elements + + + + +--- + +**28. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**29. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**30. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + + + + + + + +## List (1) +**0. [118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)** Level: Easy Tags: [Array, Basic Implementation, List] + + + + +--- + + + + + + + +## Moore Voting (2) +**0. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**1. [229. Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/229.%20Majority%20Element%20II.java)** Level: Medium Tags: [Array, Moore Voting] + + +#### Two counters, Moore Voting +1. Moore voting: vote可以加减, 一旦为零, 换下一个candidate, 之前抵消掉的算作清零. +1. 一个array里面, 最多也只有2个数字 出现次数大于2次, 所以用A/B表示. +1. count overall apperance at the end for the two items: countA, countB +1. save to result as valA, valB +1. 有点 moore voting的意思: + - 当count == 0的时候, reset + - 两个candidate A/B都不等, 那么countA--, countB-- +1. 最终重新计数, 然后比较出结局. +1. 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +#### Sort + count +- O(nlogN) + + + +--- + + + + + + + +## Trie (11) +**0. [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), 太慢. + + + +--- + +**1. [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一下。 + + + + +--- + +**2. [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 +还要做一下那. + + + +--- + +**3. [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] + + +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 +- + + + +--- + +**4. [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里面, 这个想法非常值得思考. + + + +--- + +**5. [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 + + + +--- + +**6. [211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +#### Trie, prefix tree. +- Trie Structure: `boolean isEnd`, `HashMap children` + - trie.addWord: 没node就加,有node就移动 + - trie.search: 没node就return false,有node就移动 +- Alternatively, the hash can be `TrieNode[26]` a fixed size array when applicable + - I like map better for the simplicity to write (w/o converting char -> index) + + + + +--- + +**7. [208. Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/208.%20Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- Trie Structure: + - trace the char to children node: Map + - boolean isEnd to indicate if there is string end with this node + - `TrieNode {boolean isEnd; Map children}`. +- No need to store letter c in TrieNode +- 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。 + + + + + +--- + +**8. [720. Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/720.%20Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序(lexicographically), 排序以后才能逐个看是否partial string已经存在 + - 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 + - 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: + - 长 string 排在前; + - 相等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) + +#### 从最长的word开始做 +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**9. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**10. [745. Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/745.%20Prefix%20and%20Suffix%20Search.java)** Level: Hard Tags: [Trie] + + +#### Chain `suffix # prefix` +- Build Trie for all combinations of `suffix#prefix`; all assigned with weight +- how does it make sure to return largest weight/index? + - when we build trie, always update weight for the path nodes it goes through + - yes, it overrides, but this problem does not care if some words are not found +- Time: + - build: go through all words O(n) * word length * 2 => O(n) + - query: O(1) tree height is just at most 20. +- Space: O(N) store all words + + + +--- + + + + + + + +## BST (23) +**0. [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. + + + +--- + +**1. [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的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**2. [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 + + + +--- + +**3. [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 + + + +--- + +**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. [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的边框限制好,中间就全部遍历了。 + + + +--- + +**6. [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规律。 + + + +--- + +**7. [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 + + + +--- + +**8. [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? + + + +--- + +**9. [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维护清楚就行。 + + +--- + +**10. [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 + + + + +--- + +**11. [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一下。 + + + +--- + +**12. [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. 这个用法要记住吧, 没别的捷径. + + + +--- + +**13. [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= 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. + + + +--- + +**15. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**16. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**17. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**18. [270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +Concept: Iterate over all logN nodes in the BST and record the closest. Rather than finding the value at +/- 0.5 precision + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, until null leaf +- time: O(logn), space O(1) since no extra space used + +#### DFS, Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return +- time: O(logn), space: O(logn) + + + + +--- + +**19. [938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)** Level: Easy Tags: [BST, Recursion, Tree] + +#### DFS based on BST rules +- sum up the matching L & R + - Find (L,R) on left child + - Find (L,R) on right child + - Find (L,R) covering the root node +- space O(n), worst case O(logn), height of dfs. +- time O(n) to find all nodes between (L, R) + +#### Iterative +- Using stack, or queue, list: any data structure (we are not doing ordered search) +- space O(n) +- time O(n) + + + +--- + +**20. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**21. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**22. [235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的 path +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Beofre lasts common ancestor is found: p and q should follow same search pattern: + - compare with root, then dfs(left) or dfs(right) + - util p and q fall on two sides of root, then return root + - 非常巧妙, 但是也比较局限; 稍微变条件, 就很难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(logn) extra space: recursive stack space +- O(logn) time + + + +--- + + + + + + + +## MinHeap (11) +**0. [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个数字 + + + +--- + +**1. [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] + + +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 +- + + + +--- + +**2. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**3. [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 + + + +--- + +**5. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**6. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**7. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**8. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**9. [373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)** Level: Medium Tags: [Heap, MaxHeap, MinHeap] + + +#### Method1: MinHeap wiht k size +- This approach follows the pattern of finding min pair: + - 1) only need to store k pairs + - 2) always start from min of A list and min of B list + - 3) pre-build k pairs honoring A list, and then pick the min pair, and start swapping with min of list B +- First attemp all first k pairs from nums1[i] against nums2[0] <=k : O(k) +- Use queue to pull min node and save results +- Use the nums1 val from the min node, pair up with nums2[j], add back to queue to sort +- overall runtime: O(klogk) +- space: O(k) + +#### Method2: MaxHeap with k size +- Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +- overall time: O(mnLogK) +- space: O(k) + + + +--- + +**10. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high nums[low]` from right + - 3) swap(low, high). + - By now, [low, n-1] forms a greater permutation + - but it is not the smallest, because right side [low + 1, n - 1] is descending + - 4) reverse(low + 1, n-1) to create ascending slopt on right of low (smallest next lexicographically permutation) +- Corner case: if input array is decending (1st low not found), reverse it all together O(n) +- time: O(n) visit all indexes +- space: O(1) not using additional +- Similar question: `1053. Previous Permutation With One Swap` + + + + +--- + +**4. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + + + + + + + +## Garph (1) +**0. [785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)** Level: Medium Tags: [BFS, DFS, Garph] + + +#### DFS marking each node with a state +- `bipartite` require each node to be in exact 1 party, which means it only has 1 state +- DFS to mark node with one state; and mark its edges as reversed state + - If any node state has been assigned by different from desired one, return false. + +#### BFS, Queue +- Use `Boolean states[i]` to represent visted & state +- Try all nodes with for loop, and skip visited nodes (similar validation rules as in dfs) +- In `int next : graph[curr]`, test next level first before adding. + + + +--- + + + + + + + +## 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. [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的理所应当。 + + + + +--- + +**3. [91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Method1: DP, Bottom-Up by calculating base case first +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- there can be 2 final states at dp[i]: single digit or double digit. + - validate if `single digit`, dp[i] += dp[i - 1]. Last 1 digit can be decoded. + - validate if `double digit`, dp[i] += dp[i - 2]. Last 2 digits can be decoded. +- note: + - get number from char: `c - '0'` + - validatae single digit != '0', 因为'0' 不在条件之中(A-Z) +- Option1: dp[i] as # ways to decode at index i, including letter i + - The validation is done on: 1) single digit i, or 2) double digit [i-1, i] +- Option2: Partition DP, dp[i] as # ways to decode for first i letters (excluding letter i) + - 定义`dp[i] = 前i个digits最多有多少种decode的方法`: new dp[n + 1]. + - dp[i] += dp[i - x], where x = 1, 2 + - The validation is done on: 1) single digit [i-1], or 2) double digit [i-2, i-1] + - Option2 is better in terms of clean coding. We assume `dp[0]=1` as 1 way to decode 0 digits. + - No need to specially handle length == 1, because it is covered later + - No need to manualy init the first 2-digit case as in Option1 + - Return of `dp[n]` is clean +- 引申: 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +#### Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 + - especially when i = n - 2, handle 2-digt edge case carefully: + - 1) check if two-digit range [i, i+1] is valid + - 2) check if single-digit [i] is valid; if so, += dfs(s, i + 1) +- memo[i]: # ways to decode from [i, n). init with `memo[i]=-1` + + + +--- + +**4. [639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%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]) +- Enumeration: + - 具体分析 '*' 出现的位置, 枚举出数字, 基本功. + - 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) + - 枚举好以后, 其实这个题目的写法和思考过程都不难 +- Mode: + 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. + + +#### DFS + memoization +- DFS top-down approach is used to analyze the problem. The logic flow: +- 1) consider the case of 1 letter or 2 letters. +- 2) one letter: + - [*]: + 9 * dfs(s, i + 1) + - [0~9]: + dfs(s, i + 1) +- 3) two letters: + - [_, *]: depends + - [*, _]: depends + - [*, *]: + 15 * dfs(s, i + 2) +- memo[i] records # of ways to decode from [i ~ n] +- space: O(n), Size of recursion tree can go upto n +- time: O(n), `memo array is filled exactly once`!!! + + + +--- + + + + + + + +## Cycle Detection (3) +**0. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**1. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**2. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + + + + + + + +## PriorityQueue (23) +**0. [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] + + +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 +- + + + +--- + +**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. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**3. [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 + + + +--- + +**4. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**5. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**6. [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 + + + +--- + +**9. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**10. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**11. [252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### Method1: sort input and compare if curr.end & next.start overlaps +- sort: `Arrays.sort(intervals, Comparator.comparing(i -> i[0]))` +- time: O(nlogn), space: O(1) + +#### Method2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 +- time: O(nlogn), space O(n) +- Not necessary for this problem, since it requires extra space with pq. + + + +--- + +**12. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**13. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**14. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**15. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**16. [414. Third Maximum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/414.%20Third%20Maximum%20Number.java)** Level: Easy Tags: [Array, PriorityQueue] + +#### pq +- 注意special case: `when less than 3 items, return maximum` +- PQ是natural order: remain peek() will be the 3rd maximum + + + + +--- + +**17. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**18. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**19. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**20. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**21. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**22. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high0)` from skipping last item at index 0 + + +#### Method2: DP +- the problem aims to find true/false capability +- bit-masking. TODO. The DP approach is kinda hard-level +- https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/335668/DP-with-Bit-Masking-Solution-%3A-Best-for-Interviews + + + +--- + + + + + + + +## Heap (22) +**0. [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个数字 + + + +--- + +**1. [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 + + + +--- + +**2. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**3. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**4. [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 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) + + + + +--- + +**6. [[lint]. Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Merge%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 + + + +--- + +**7. [[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)** Level: Hard Tags: [HashHeap, Heap, Lint] + +非题.是从九章找来的HashHeap implementation. + +#### HashHeap +- An efficient implementation of a priority queue. +- The linear hash function monotonically maps keys to buckets, and each bucket is a heap +- https://xlinux.nist.gov/dads/HTML/hashheap.html + + + +--- + +**8. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**9. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**10. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**11. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**12. [973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)** Level: Medium Tags: [Divide and Conquer, Heap, Sort] + + +#### PriorityQueue +- Create customized Point{} class +- Sort by distance +- Maintain queue size <= K + +#### Divide and Conquer +- ?, select sort? + + + +--- + +**13. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**14. [239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Method1: Deque, Monotonous queue +- 维持monotonuous queue: `front is always at max` and the `tail end is min`. Always need to return the max end of queue. +- when adding new elements x: + - 1) start from small-end of the queue + - 2) drop all smaller elements + - 3) append to the ending element that is larger than x. + - This is to maintain a front->tail decreasing queue +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`: better than using arraylist, since DeQueue(linked list) removes at O(1) cost +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +- Option1: sliding window template using right/left + while loop + - 1) tailing the new number to max queue, if applicable + - 2) process: record max + - 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] +- Option2: same concept, but use index `i` to mark right, and `i - k + 1` to mark left. +- time: O(n), one pass +- space: O(k), store the deque + + +#### Method2: Heap +- can always build a `class Node{index, val}`; and sort them with PQ of size k +- time: O(nlogK) +- space: O(k) +- this is not linear time, not as good as method1 + + + +--- + +**15. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**16. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**17. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**18. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**19. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**20. [373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)** Level: Medium Tags: [Heap, MaxHeap, MinHeap] + + +#### Method1: MinHeap wiht k size +- This approach follows the pattern of finding min pair: + - 1) only need to store k pairs + - 2) always start from min of A list and min of B list + - 3) pre-build k pairs honoring A list, and then pick the min pair, and start swapping with min of list B +- First attemp all first k pairs from nums1[i] against nums2[0] <=k : O(k) +- Use queue to pull min node and save results +- Use the nums1 val from the min node, pair up with nums2[j], add back to queue to sort +- overall runtime: O(klogk) +- space: O(k) + +#### Method2: MaxHeap with k size +- Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +- overall time: O(mnLogK) +- space: O(k) + + + +--- + +**21. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high= 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. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + + +--- + + + + + + + +## Stack (38) +**0. [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, 但是数字可为多位 + + + +--- + +**1. [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 + + + + +--- + +**2. [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. + + + +--- + +**3. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + +--- + +**4. [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()里, 每次换水,查看末尾项. + + + + +--- + +**5. [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的头。 + + + + + +--- + +**6. [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? + + + +--- + +**7. [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. + + + +--- + +**8. [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,也是类似做法 + + + +--- + +**9. [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 + + + +--- + +**10. [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 + + + +--- + +**11. [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) + + + + +--- + +**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. [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) + + + + +--- + +**14. [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了. + + + + +--- + +**15. [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` + + + +--- + +**16. [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] + + +给一个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, 永远在左边上下上下。 + + + +--- + +**17. [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就可以了. + + + +--- + +**18. [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)的巧妙用法. + + + + +--- + +**19. [42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. + +#### Method1: Max wall from both sides +- Array: Left Max Wall vs Right Max Wall. +- 对于每个index而言, vertically 能存放的最大水柱, 就是靠 左 右 最高墙决定的: + - min(leftHighestWall, rightHighestWall) - currHeight. +- time: O(n) +- space: O(n) + +#### Method2: Two Pointers +- Optimization from Method1: two pointer, 还是找左边最高和右边最高. O(1) space. +- 利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +- always limited by the shorter wall: 左边按照maxLeft来计算, 右边按照maxRight来计算. +- time: O(n) +- space: O(1) + +#### Method3: 2 Pointers, start from 2 sides +- 1. 找中间最高bar的index +- 2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条 +- 3. 每次还要减去block自身的height +- time: O(n) +- space: O(1) + +#### Method4: Stack +- 主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +- 最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +- 用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. +- time: O(n) +- space: O(n) + + + + +--- + +**20. [1021. Remove Outermost Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1021.%20Remove%20Outermost%20Parentheses.java)** Level: Easy Tags: [Stack] + +#### Stack +- use stack to hold potential pair +- when stack is empty: detect outtermost element, dont add to final result +- time: O(n), space O(n) + +#### Count occurance +- solution from discussion, time O(n), space O(1) +- save space, but less scalable: think about if there are 100 different pairs, then the couting will be a bit complex to handle. + + + +--- + +**21. [496. Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/496.%20Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack +- use stack to hold all elements + - keep poping if `stack.peek() < num` + - use map to record (top, num) +- time O(n), run through base once and sub-sequence once +- space O(n), stack, map + +#### HashMap +- O(n) space, O(n^2) time worst case + + + +--- + +**22. [1249. Minimum Remove to Make Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1249.%20Minimum%20Remove%20to%20Make%20Valid%20Parentheses.java)** Level: Medium Tags: [Stack, String] + + +#### Stack +- Goal: remove extra '(' or ')' so it is valid. +- Forward thinking: use stack to track '(' and ')', then keep appending partial string to output +- Backward thinking: use stack to filter out false indexes, and remove them in the end + + + + +--- + +**23. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**24. [844. Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/844.%20Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + +#### Method1: Two pointers to backtack from end of string +- time: O(n) +- space: O(1) + +#### Method2: Stack +- need to remove entity just added +- use stack to hold array content; pop if # is found + + + +--- + +**25. [1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)** Level: Hard Tags: [DFS, Stack, String] + +#### Parse exp as sub problem +- Analyze the pattern: 1) single char, 2) with !, 3) with &, | +- Identify sub problem + - Use stack to parse the data in "()", which is a sub problem to solve with recursive call + - Handle &, | case: need to parse multiple +- Be comfortable with string parsing +- Slight improve: + - If see obvious result, directly return evaluation w/o further parsing + - use memo to store evaluated exp + +#### Evaluate inner exp and save back to Stack +- Use '(' and ')' to mark inner exp +- Evaluate the inner exp and save result back to Stack: the result will be 'f' or 't' +- This is slightly slow because: + - It requires all stack items on top to be processed before reaching the operator + - There is no room to optimize even there is simplification for specific operator + + + +--- + +**26. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**27. [20. Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/20.%20Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +#### Stack +- 剥皮过程。解铃还须系铃人 +- 左边的外皮'{['在stack底部 +- 右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**28. [145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Method1: DFS, Recursive +- trivial, 先加left recursively, 再加right recursively, 然后组成头部. +- Option1 w/o helper; option2 with dfs helper. + +#### Method2, Iterative, Stack +- Option1: reversely add to list +- 双stack的思想, 需要在图纸上画一画 + - 原本需要的顺序是: 先leftChild, rightChild, currNode. + - 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild + - 这样出来的结果是reverse的, 那么翻转一下就可以了. +- reverse add: `list.add(0, x)`; +- 利用stack的特点 + - 每次加element进stack的时候, 想要在 bottom/后process的, 先加 + - 想要下一轮立刻process的, 最后push进stack. +- Option2: regular sequence add to stack: add curr, right, left + - Use set to contain the processed children + - only process curr if its children is processed + + + + +--- + +**29. [103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + + +#### 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 + + + +--- + +**30. [636. Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/636.%20Exclusive%20Time%20of%20Functions.java)** Level: Medium Tags: [Stack] + + +#### Stack +- Task time range: + - start range = next task timestamp - start.timestamp + - end range = curr task timestamp - last task timestamp + 1; because end node is inclusive. +- How to think of using stack: a task cannot finish until end is met; a early task cannot stop until a later task ends + - Alternatively, we can use a hashmap to track as well +- Keep track of the timestamp +- make sure to +1 when end node is met because end task is inclusive to this finishing task + + + + +--- + +**31. [94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Method1: DFS +- option1: dfs + rst list to carry results +- option2: Divide and Conquer, 在自己的基础上recursive, 不用helper function +- O(n) time + +#### Method2: Iterative, Stack inorder traversal +- 1) Add root.leftPath all the way to leaf, 2) process curr 3) Move to right if applicable 4) add all right.leftPath +- O(n) time, O(h) space + + + + +--- + +**32. [402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)** Level: Medium Tags: [Greedy, Monotonous Stack, Stack] + + +#### Monotonous Stack (Increasing) +- Greedy: Remove 1) earlier digits(数位靠前权值大), 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. + + + + +--- + +**33. [71. Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/71.%20Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path: + - 1. `.` 代表current directory, 可以忽略. + - 2. `../` 表示previous level. + - 3. double slash 可以忽略. + - 4. empty string 要output `/` +- parse by '/', and go over using stack + - put [folder] in stack + - ".." pop() 1 element of the stack, if anything + - "." stays the same +- output stack reversely: connect with '/', skip tail + + + +--- + +**34. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + +**35. [341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, NestedInteger, Stack] + + +#### Method1: Stack holds items from back of the list +- Option1: always set integer on top of the stack everywhere + - if not, poping stack until the top is integer + - code is easy +- Option2: in hasNext(), faltten the list in stack + +#### Method2: DFS preprocessing. +- 用queue to store all items. Kinda hack. Defeat the purpose of the problem. +- Super fast to query next(), however, needs to holds everything in memory +- O(n) + + + +--- + +**36. [739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)** Level: Medium Tags: [Hash Table, Monotonous Stack, Stack] + + +#### Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) + +#### Method2: `Map `, kinda of like bucket sort +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: + - 1) record tempIndex[currTemp] = i; + - 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result + + +--- + +**37. [272. Closest Binary Search Tree Value II.java](https://github.com/awangdev/LintCode/blob/master/Java/272.%20Closest%20Binary%20Search%20Tree%20Value%20II.java)** Level: Hard Tags: [Stack, Tree] + + +#### Method1: Stack, DFS, Inorder Traversal +- find successors and predecessors using BST (both list will be sorted); in the end, we can easily get top k from the two sorted list + - with BST: **inorder traversal gives us sorted predecessors + - with BST: **reversed-inorder traversal gives us sorted successors + - smallest on top of the stack +- time: O(n) visit all nodes, O(k) to output +- space overall: O(n) to store all nodes + +#### Method2: BFS, brutle force +- Itereate over all nodes and maintain pq (improvemenet point: how to avoid traversing entire tree?) +- prioritize nodes that are closer to target, so we may stop early when result reaches k candidates +- time: O(n*logn) +- kinds slow and not utilizing BST + + + +--- + + + + + + + +## Linked List (34) +**0. [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 + + + + +--- + +**1. [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 + + + +--- + +**2. [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, 换位子 + + + +--- + +**3. [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. + + + +--- + +**4. [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/ + + + +--- + +**5. [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]。 然后把三段链接在一起。 + + + + +--- + +**6. [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会出问题 + + + +--- + +**7. [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) + + + +--- + +**8. [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链接起来。 + + + +--- + +**9. [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) + + +--- + +**10. [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 + + + + +--- + +**11. [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; + + + +--- + +**12. [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在某个点小了,加进去当下这个空隙。 + + + +--- + +**13. [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一下。 + + + +--- + +**14. [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] + + +给一个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, 永远在左边上下上下。 + + + +--- + +**15. [[lint]. Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Nth%20to%20Last%20Node%20in%20List.java)** Level: Easy Tags: [Linked List, Lint] + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth + + + +--- + +**16. [21. Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/21.%20Merge%20Two%20Sorted%20Lists.java)** Level: Easy Tags: [Linked List] + + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + + + +--- + +**17. [237. Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/237.%20Delete%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 + + + +--- + +**18. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**19. [83. Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/83.%20Remove%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会非常清晰 + + + +--- + +**20. [203. Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/203.%20Remove%20Linked%20List%20Elements.java)** Level: Easy Tags: [Linked List] + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + + + +--- + +**21. [19. Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/19.%20Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Two Pointer +- 1 end pointer to define the window based n steps +- 1 pre pointer to track the node before the targeting node +- when end reaches null, remove nth node: link pre and head.next + + + +--- + +**22. [206. Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/206.%20Reverse%20Linked%20List.java)** Level: Easy Tags: [Linked List] + +#### Iterative +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + +#### Recursive with a helper function +- source node: head +- target node: new head + + + +--- + +**23. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**24. [369. Plus One Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/369.%20Plus%20One%20Linked%20List.java)** Level: Medium Tags: [Linked List] + + +#### Reverse to make significant digit at tail +- Need add from the back and calculate carry +- Reverse list, so insignificant digit at head; calculate carry +- Reverse back when output + + + +--- + +**25. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**26. [234. Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/234.%20Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单: 两边回溯相等. However: + - 1) cannot random access index on linkded list + - 2) cannot reverse iterating linked list +- solution: reverse linked list: 遍历接开头 + - 1) 用快慢指正找到mid point + - 2) reverse 2nd half + - 3) compare leftList and rightList +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + + + +--- + +**27. [876. Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/876.%20Middle%20of%20Linked%20List.java)** Level: Easy Tags: [Linked List] + +找Linked List的中间node + +#### 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + + + +--- + +**28. [2. Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/2.%20Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. 跟Add Binary的理解方式一模一样. + +#### Math, Linked List +- reverse order helps calculation + - add additional carry to end + - not same length: align on left +- traverse till both ends +- 遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + + + + +--- + +**29. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**30. [430. Flatten a Multilevel Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/430.%20Flatten%20a%20Multilevel%20Doubly%20Linked%20List.java)** Level: Medium Tags: [DFS, Linked List] + + +#### DFS +- Depth-first: + - 1) process curr.child, return tailChild + - 2) connect tailChild.next = curr.next +- function: link(Node a, Node b); + + + +--- + +**31. [160. Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/160.%20Intersection%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 + + + +--- + +**32. [138. Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/138.%20Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List, time, space: O(n) +- Basic Implementation of copy linked list: + - use a iterator node to iterate over the list: 遍历head.next .... null. + - use a dummy node to hold reference to the iterator node. +- Map: 1. avoid creating same node; 2. return the new node if existing + - 每一步都check map里面有没有head. 没有? 加上 + - 每一步都check map里面有没有head.random. 没有? 加上 +- Note, there is a way to skip the extra map O(n): https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43491/A-solution-with-constant-space-complexity-O(1)-and-linear-time-complexity-O(N) + - However, creating a deep clone of the list is already O(n) extra space, so it is NOT effectively O(1) w/o map + - It may be beneficial, if we can not hold all nodes in memory, then the approach w/o map is more applicable. + + + +--- + +**33. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + + + + + + + +## 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 (13) +**0. [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] + + +#### 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. + + + +--- + +**1. [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` + + + +--- + +**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. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**4. [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]) + + + +--- + +**5. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**6. [303. Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/303.%20Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**7. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**8. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**9. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**10. [304. Range Sum Query 2D - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/304.%20Range%20Sum%20Query%202D%20-%20Immutable.java)** Level: Medium Tags: [DP, PreSum] + + +#### Method1: rangeSum/caching +- build rangeSum[i][j]: square range sum from (0,0) to (i,j), O(mn) to init +- query: time O(1) + +#### Method2: presum/caching +- build rowPreSum[i][j]: row i sum from [0 ~ j], O(mn) to init +- callign takes O(m); space O(mn) + + + + +--- + +**11. [724. Find Pivot Index.java](https://github.com/awangdev/LintCode/blob/master/Java/724.%20Find%20Pivot%20Index.java)** Level: Easy Tags: [Array, PreSum] + + +#### PreSum +- want to find `nums[i - 1] == nums[n - 1] - nums[i]`, given: + - preSum[i], sum from [0, i] inclusive + - preSum[j] - preSum[i] = [i+1, j] inclusive +- O(n) to build preSum +- O(n) to find pivot + + + +--- + +**12. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + + + + + + + +## Binary Indexed Tree (4) +**0. [308. Range Sum Query 2D - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/308.%20Range%20Sum%20Query%202D%20-%20Mutable.java)** Level: Hard Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree +- Same concept as turning an array into a binary segment tree, + - HOWEVER, this is a 4-nary segmenet tree +- Reference. 307 Range Sum Query +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. +- Handling end stage, there are two approaches: + - ApproachA: check at beginning of recursive call (i.e in `build()`, `updateNode()`, `rangeQuery()`). + - pro: calling recursive function blindly; code is easy. + - con: be really clear about termination state, and catch it. + - ApproachB: check & come up with correct query condition before recursive call + - pro: input to recursive function is assumed to be correct + - con: sometimes really hard to write the conditions before recursive call; code is hard. + + + +--- + +**1. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**2. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**3. [307. Range Sum Query - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/307.%20Range%20Sum%20Query%20-%20Mutable.java)** Level: Medium Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree, devide and conquer +- sample problem for segment tree +- build(), update(), rangeQuery() + - build and update are standard + - rangeQuery: handle the range split check +- Null leaf node handling: NO, ideally it will not encounter null leaf. + - in update/rangeQuery: when final state (`start==end`) is reached, the recursive call ends + - there is no way for any node to dive futher into null child. +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. + + + +--- + + + + + + + +## Binary Search on Value (1) +**0. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + + + + + + + +## Graph (20) +**0. [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. + + + +--- + +**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. [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 + + + +--- + +**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)** 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 + + + +--- + +**4. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + +--- + +**5. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**6. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**7. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**8. [1153. String Transforms Into Another String.java](https://github.com/awangdev/LintCode/blob/master/Java/1153.%20String%20Transforms%20Into%20Another%20String.java)** Level: Hard Tags: [Graph] + + +#### Graph +- analysis: + - 1) should not have mult-origin cases: 1 char maps to 1 char at maximum + - 2) need a buffer char NOT exist in target to hold inter-media transformation + - check open char (out of 26 lower letter) that is NOT in target chars +- impl the validation rules +- more to read in https://leetcode.com/problems/string-transforms-into-another-string/discuss?currentPage=1&orderBy=most_votes&query= + + + +--- + +**9. [1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph] + + + +#### BFS +- simply calc each level sum with BFS +- top-level is processed first, since we go from top level -> deeper level + - only update result if sum is truly > global MAX. + + + +--- + +**10. [1306. Jump Game III.java](https://github.com/awangdev/LintCode/blob/master/Java/1306.%20Jump%20Game%20III.java)** Level: Medium Tags: [BFS, Graph] + + +### Method1: BFS +- Find possibility to reach certain point, we can BFS: faster to find shortest candidate +- use queue to hold left, right candidates +- use set to record visited + +### Method2: DFS +- attemp all nodes, use set to record visited. +- time: O(n) +- space: O(n) + + + +--- + +**11. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + +**12. [332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)** Level: Medium Tags: [Backtracking, DFS, Graph] + + +#### DFS with backtcking +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +- time: O(n^n). n = # of cities. worst case, each city has (n-1) edges and need to try all combinations +- space: O(n^2), there can at most be n * (n - 1) edges + + + +--- + +**13. [1267. Count Servers that Communicate.java](https://github.com/awangdev/LintCode/blob/master/Java/1267.%20Count%20Servers%20that%20Communicate.java)** Level: Medium Tags: [Array, Graph] + + +#### two axis array, cross-reference +- analyze problem, and realize we want to eliminate `isolated servers` +- count row[], count col[] +- cross-reference row[] and col[]: `row[i]==1 & col[j]==1` indicates a isolated server + +### DFS brutle +- Unfortunately, this problems checks unconnected items, so dfs needs to brutlely check entire row or column +- Only add if `vertical + horizontal count` > 1 +- time: O(mn) * O(m + n) + + + +--- + +**14. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + +**15. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**16. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + +**17. [133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%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: `map` + - 1) make new curr node; + - 2) clone all neibhors and add them +- Use the map to avoid visited nodes +- time: O(n). visit all nodes +- space: O(n). Technically only travels n levels/stacks to circle all nodes (undirected & connected) + +#### 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. + + + +--- + +**18. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**19. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + + + + + + + +## Brainteaser (2) +**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的做法写出结果. + + + +--- + +**1. [319. Bulb Switcher.java](https://github.com/awangdev/LintCode/blob/master/Java/319.%20Bulb%20Switcher.java)** Level: Medium Tags: [Brainteaser, Math] + + +#### Brainteaser +- https://leetcode.com/problems/bulb-switcher/discuss/77104/Math-solution.. + +#### Brutle: +- if just impl, it take O(n^2): +- repating: some pos are toggled mutiple times: if we know total times, easy to determin each pos. +- loop over [2, n], count times on each index + + + +--- + + + + + + + +## Analysis (1) +**0. [41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)** Level: Hard Tags: [Analysis, Array, Edge Case] + + +给一串无序数字, 有负数: 找这个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 +1. 如果nums==null, 其实missing positive integer 自然而然是 1 +1. 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) + - 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +1. 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + + + + + + + +## Union Find (16) +**0. [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拿出来就好了。 + + + +--- + +**1. [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)}` + + + +--- + +**2. [Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)** Level: Medium Tags: [Union Find] + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + + + +--- + +**3. [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. + + + +--- + +**4. [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 + + + +--- + +**5. [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--. + + + +--- + +**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)** 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 + + + +--- + +**7. [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 + + + +--- + +**8. [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? + + + + +--- + +**9. [Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)** Level: Medium Tags: [Union Find] + +Lint还不能跑, 全部按照题意和答案document的. + + + +--- + +**10. [[tool]. UnionFind.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20UnionFind.java)** Level: Medium Tags: [Lint, Union Find] + + +#### Method1: Union Find with Array +- union(), find() +- Path Compresion: store skip father after found, which makes find O(1) + +#### Method2: Union Find with HashMap + + + + + +--- + +**11. [305. Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/305.%20Number%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 + + + +--- + +**12. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + +**13. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**14. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + +**15. [721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Union Find] + +- time O(mn) + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Method1: 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, 最终用来输出. + +#### Method2: 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) + +### DFS +- TODO + + + +--- + + + + + + + +## Sweep Line (7) +**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 + + + +--- + +**1. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**2. [252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### Method1: sort input and compare if curr.end & next.start overlaps +- sort: `Arrays.sort(intervals, Comparator.comparing(i -> i[0]))` +- time: O(nlogn), space: O(1) + +#### Method2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 +- time: O(nlogn), space O(n) +- Not necessary for this problem, since it requires extra space with pq. + + + +--- + +**3. [850. Rectangle Area II.java](https://github.com/awangdev/LintCode/blob/master/Java/850.%20Rectangle%20Area%20II.java)** Level: Hard Tags: [Segment Tree, Sweep Line] + + +#### Sweep Line + Merge Interval concept +- Inspired by: https://leetcode.com/problems/rectangle-area-ii/discuss/137941/Java-TreeMap-solution-inspired-by-Skyline-and-Meeting-Room +- First consider regular sweep line and realize problem: each vertical line has multiple block segments + - Easy: take a list of vertical dots, and calculate the height diff + - We can use a TreeMap with y-coordinate as key, so to `natural sort by y-coordinate` +- Trick: can NOT remove used y coordinate from map, because the rectangle may continue to expand to right side. +- apply simple equation to calc area: `(long)preY * (p.x - preX)` +- time: + - sort initial queue: O(nlogn) + - process queue: O(n) + - TreeMap insertion: O(logn) + - TreeMap traversal: O(n) + - overall, process queue can be O(n^2) +- space: O(n) + +#### Sweep Line concept, bottom->top sweep +- https://leetcode.com/problems/rectangle-area-ii/discuss/137914/C%2B%2BPython-Discretization-and-O(NlogN) + +#### Segment Tree +- TODO lol + + + +--- + +**4. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**5. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**6. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + + + + + + + +## Matrix DFS (2) +**0. [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 + + + +--- + +**1. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + + + + + + + +## Subarray (11) +**0. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**1. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + + +简单的求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] + + +给一串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. [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] + + +#### 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. + + + +--- + +**4. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**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. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**8. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**9. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**10. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + + + + + + + +## 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. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + + +#### 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题,想不到,就是搞不出。 + + + + +--- + +**1. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**2. [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) + + + +--- + +**3. [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. + + + +--- + +**4. [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), 太慢. + + + +--- + +**5. [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 + + + +--- + +**6. [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) + + + +--- + +**7. [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] + + + + +--- + +**8. [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]。 + + + + + +--- + +**9. [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] + + +跟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] + + + +--- + +**10. [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]) + + + +--- + +**11. [198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +搜刮房子, 相邻的不能碰. 每个房子里有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虽然抽象, 但是更加实用. + +#### Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- calculation + - dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]). The prior house can be either state. + - dp[i][1] = dp[i - 1][0] + nums[i]. The prior house must be `NOT ROBBED`. +- return `Math.max(dp[n - 1][0], dp[n - 1][1])` + + + +--- + +**12. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**13. [70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%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]往往是有特殊状态的. 这里, dp[0]: 1种方式可以原地不动 + - dp[1]=1, 1种方式到达index=1, + - 之后的`dp[2] = dp[0]+dp[1]`: 就是dp[0]的走法 or dp[1]的走法 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**14. [10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + + + +--- + +**15. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**16. [121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### min[n] +- 每天都就交易价格,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 dont need to buyin at 5, 3, 6, 4 later on; + - only need to sell on higher prices. + + + +--- + +**17. [139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- Bottom-up, test simply case. Sequence DP. +- true/false problem, think about dp + - 子问题: 前i个字母, 是否可以有valid break + - check: 1) dp[j] && 2) `if substring(j, i) valid`, for all j = [0 ~ i] + - dp = new boolean[n + 1]; dp[0] = true; + - test: `dp[i] |= dp[j] == true && word[j, n] in dict`. + - Need iterate over i = [0 ~ n], also j = [0, i] + - When there is a way to make dp[i] == true, then break the [j ~ i] loop, move on to test dp[i++] +- Use set dict: `dict.contains()` +- Improvement: O(n) to figure out max length, so we can skip some substring[j~i] dict.contains() +- overall O(n^2) time since the double for loop + +#### DFS +- Top-Down, break into small problems: Check front subString, and put the rest substring into dfs to test +- Memoization: for tested failed substring, record and do NOT test them again. +- Same Improvement as in DP: use max/min length of dict words as boundary + + + +--- + +**18. [256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, depends on the color of dp[n-1] + - 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- Need to have status with dp array: int[index][color status] + - dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. + - dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- time: O(nm), m = # of colors +- space: O(nm) + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 +- space:O(1) + + + +--- + +**19. [265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + + +一排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) + + + +--- + +**20. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + + + + + + + +## Minimum Binary Tree (3) +**0. [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. + + + +--- + +**1. [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 + + + + +--- + +**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 (3) +**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` + + + +--- + +**2. [843. Guess the Word.java](https://github.com/awangdev/LintCode/blob/master/Java/843.%20Guess%20the%20Word.java)** Level: Hard Tags: [MiniMax] + + +TODO: revist time/space complexity + +#### Minimax, find target, and use it to eliminate +- `擒贼先擒王`: find the candidate that has largest set of correlations with the rest candidates, and eliminate based on this candidate. + - `approach A`: count the candidate that has 0 overlaps, find min of this poll + - `approach B`: count the candidate that has largest # of connections +- cross-compare, count `match==0` : find candidates that has 0 overlap with others + - pick `min-count candidate A`: it is a candidate that has overlaps with most strings (since 0-match-count is lowest) + - the above candidate will help to **eliminate** a largerset of overlapped candidates + - guess A, return matchCount. +- filter set with matchCount: eliminateCandidate + + + +--- + + + + + + + +## Two Pointers (57) +**0. [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 + + + + +--- + +**1. [Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)** Level: Easy Tags: [String, Two Pointers] + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + + + +--- + +**2. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + +--- + +**3. [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] + + +给一串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 + + + +--- + +**4. [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, 换位子 + + + +--- + +**5. [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' + + + + +--- + +**6. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**7. [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的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**8. [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链接起来。 + + + +--- + +**9. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + +--- + +**10. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + +--- + +**11. [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) + + + +--- + +**12. [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看起来更容易理解. + + + +--- + +**13. [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), 就不写了 + + + +--- + +**14. [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 + + + +--- + +**15. [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 + + + +--- + +**16. [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了 + + + +--- + +**17. [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 原题有一点点不一样. + + + + +--- + +**18. [[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)** Level: Medium Tags: [Array, Lint, Two Pointers] + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**19. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**20. [42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. + +#### Method1: Max wall from both sides +- Array: Left Max Wall vs Right Max Wall. +- 对于每个index而言, vertically 能存放的最大水柱, 就是靠 左 右 最高墙决定的: + - min(leftHighestWall, rightHighestWall) - currHeight. +- time: O(n) +- space: O(n) + +#### Method2: Two Pointers +- Optimization from Method1: two pointer, 还是找左边最高和右边最高. O(1) space. +- 利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +- always limited by the shorter wall: 左边按照maxLeft来计算, 右边按照maxRight来计算. +- time: O(n) +- space: O(1) + +#### Method3: 2 Pointers, start from 2 sides +- 1. 找中间最高bar的index +- 2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条 +- 3. 每次还要减去block自身的height +- time: O(n) +- space: O(1) + +#### Method4: Stack +- 主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +- 最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +- 用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. +- time: O(n) +- space: O(n) + + + + +--- + +**21. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**22. [360. Sort Transformed Array.java](https://github.com/awangdev/LintCode/blob/master/Java/360.%20Sort%20Transformed%20Array.java)** Level: Medium Tags: [Math, Two Pointers] + + +#### Two Pointers, Math +- Being able to analys the a*x^2 + b*x graph and find the `peak` or `valley` +- Math basics: x^2 dominates the overall curve so it is up to a to determine: + - `valley`: if a < 0, both sides will be small and center will be large. Prioritize larger value. + - `peak`: if a > 0, center will be small and both sides will be large. Prioritize smaller value. + - starting index being 0 or n-1, is driven by `a` + + + +--- + +**23. [849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array, Basic Implementation, Two Pointers] + + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, Two Pointers: 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` + + + + +--- + +**24. [1213. Intersection of Three Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/1213.%20Intersection%20of%20Three%20Sorted%20Arrays.java)** Level: Easy Tags: [Hash Table, Two Pointers] + + +Very similar to 349.Intersection of Two Arrays. + +#### Hash Table +- Use set to check +- Verify duplicates at end rst + +#### Two Pointers +- similar to Intersection of Two Sorted Arrays +- Start from front/back, process 1 item at a time + - if match, move all pointers +- Optoin1: check from back +- Optoin2: check from frotn + + + +--- + +**25. [986. Interval List Intersections.java](https://github.com/awangdev/LintCode/blob/master/Java/986.%20Interval%20List%20Intersections.java)** Level: Medium Tags: [Two Pointers] + + + + +#### Method1: Merge Interval +- There can be 1 overlapping on any interval, calculate the inner intersection: lo(A[i][0], B[j][0]), hi(A[i][1], B[j][1]) + - if low <= hi, a valid intersection exist; add + - also, if A[i][1] < B[j][1]; that is A[i].end < B[j].end, then i++; otherwise j++ + - because the further-away `end` has been used, so move on. +- O(n) + +#### Method2: Sweep line +- code is much more complex (pq, Point, process code... etc) than method1 +- we can use point to track open/close, also want to specify if point belongs to A/B +- mark 2 global parameters: aOpen, bOpen. + - process when A/B close, record if (aOpen, bOpen) has overlaop + - clear up corresponding global parameter after A/B closes +- sort all pointers in priority queue by index +- Point: {boolean isOpen; int index} +- process the queue and remember to clean up all items on same index +- time: O(nlogn) +- space: O(n) + + + + +--- + +**26. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**27. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**28. [80.Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/80.Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Basic +- 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其实就足够. + +#### Follow up: k duplicates, Two Pointers +- when index i and i-1 are diff, use count=1 to start +- in while loop, keep count++ until count==k +- reset when next diff comes in + + + +--- + +**29. [26.Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/26.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 + + + +--- + +**30. [259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +1. Similar to 15. 3Sum, but simpler. +1. 只需要count triplet, 但是不需要save triplet, 而且还不需要handle duplicated triplets +1. 发现start, end满足条件时候,(end - start)就是所有 sum target, 那么就end-- +1. 两层循环, O(n2) + + + +--- + +**31. [977. Squares of a Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/977.%20Squares%20of%20a%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +#### Two Pointers +- negative index i, positive index j + + + +--- + +**32. [67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%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 + +#### reverse +- Reverse string -> Convert to Integer List, add up -> Convert back to string +- pointer 从前向后, 所以只需要 1个pointer. +- 操作复杂, 如上, 证明可以解决. 没必要reverse. + +#### Incorrect: convert to Integer +把binary换成数字作加法. 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**33. [15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +#### sort array, for loop + two pointer +- 处理duplicate wthin triplets: + - 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. + - 如果是triplet内有重复, 用完start point, 移动到结尾. +- Note: + - 1. 找 value triplets, 多个结果。注意,并非找index。 + - 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + - 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 +- 时间 O(n^2), 两个nested loop + +#### For loop + 2Sum +- HashMap 2Sum. Remember to handle duplicates + - 1. For loop 挑个数字A + - 2. 2Sum 出一堆2个数字的结果 + - 3. Cross match 步骤1里面的A + + + +--- + +**34. [19. Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/19.%20Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Two Pointer +- 1 end pointer to define the window based n steps +- 1 pre pointer to track the node before the targeting node +- when end reaches null, remove nth node: link pre and head.next + + + +--- + +**35. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**36. [844. Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/844.%20Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + +#### Method1: Two pointers to backtack from end of string +- time: O(n) +- space: O(1) + +#### Method2: Stack +- need to remove entity just added +- use stack to hold array content; pop if # is found + + + +--- + +**37. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**38. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**39. [567. Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/567.%20Permutation%20in%20String.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + +#### Method1: Sliding window with left/right Pointers +- Sliding window template: + - 1) Check right pointer and move right + - 2) Move left when necessary + - 3) Verify count == 0 & end state + - Note: normally 2) and 3) are in reversed order; this problem is a bit different +- This is efficient when the number of characters is not limited to 26, the runtime is still O(m + n) +- time: O(m + n), m = s1 length, n = s2 length +- space: O(k), k = # of possible chars, 26 in this case + +#### Method2: Two Pointer, but brutle verify freq count +- 如果做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) 这一步 +- time: (m + n) + - However, if # of possible chars is more than 26 + - For example, `k unique characters`, then the runtime will become: O(m + nk) + +- space: O(k), k = # of possible chars, 26 in this case + + + +--- + +**40. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**41. [345. Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/345.%20Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + +**48. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**49. [1004. Max Consecutive Ones III.java](https://github.com/awangdev/LintCode/blob/master/Java/1004.%20Max%20Consecutive%20Ones%20III.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + + +#### Sliding window + Left/Right Two Pointers +- https://leetcode.com/problems/max-consecutive-ones-iii/solution/ +- Start with DFS thought, but realize redundant calculations: + - we never need to flip 2 indexes [A], [C] from 0 -> 1, if there is a [B] in middle that is 0 too + - the flipped k zeroes must be consecutive too +- we can utilize two pointers to establish a window that captures k zeroes + - always expend right pointer; if seeing an zero, k-- + - note: `len = right - left + 1` is the ongoing max length + - when k < 0 (too many zeros), we need to slide the left side of the window to make sure: + - keep window len + - potentially do k++ when A[left]==0 +- goal: matain a max size of the window, until right == n +- return (right - left). at this moment, right == n, so no need to (right - left + 1) + + + +--- + +**50. [283. Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/283.%20Move%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 + + + +--- + +**51. [125. Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/125.%20Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Two Pointers +- Time O(n), Space O(1). +- 普通方法, 两边check, 速度相比较regular expression更快. leetcode 4ms. +- Use helper functions. + +#### 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/ + + + +--- + +**52. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**53. [438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)** Level: Medium Tags: [Hash Table, Sliding Window, Two Pointers] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### Method1: Sliding Window, Hash Table. +- A creative way of using anagram char count `hash[c] >= 0` to determine if the curr c is a target char of the deesired anagram. + - because we always reduce hash[c]-- for all characters + - so only the anagram chars would be `hash[c] >= 0` after reducing. +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +- Slinding window always has left/right pointer: + - 1) at any given time move 1 index at a time: expand right window, process rsult, shrink left window + - 2) one of the basic goal is to maintain fixed window size +- algo: + - calc char freq of the target p, and store in a hash[256]; it will be used to distinguish anagram chars: `hash[c] >= 0` indicates a anagram char + - expand right window: move right to expand the window; ONLY when meeting a anagram char, count-- + - process result: if count reduces to 0, one anagram is found + - shrink left window: if (right - left) == p.length(), drop curr left char, and move forward +- how could we rely on only just `count == 0`? + - the hidden pre-condition is `right - left must already be p.length()`, which is validaterd in prev iteration +- time: O(n) +- space: O(1) + +#### Method2: HashTable +- count character apperance -> hash table, here just a int[26] + - use a window to record count++ and count--, in order to compare with countP +- prep the countP takes O(m) time +- time: O(n) + O(m) +- space: O(n) + + + + + +--- + +**54. [632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)** Level: Hard Tags: [Hash Table, Sliding Window, Two Pointers] + + +#### Method1: Sliding Window +- First sort all of the items together by actual val using `Node {int val, int row}` +- Slinding window goal: + - 1) use right to find range that touches all rows, + - 2) use left to shrink the range +- Sliding Window Template + - move right pointer + - Counts[i] = # of elements used in left/right range + - when counts[i] == 0, countUnique++; the number of row/list being included + - when count == row size: + - processing & save shorter range by using left/right Pointers + - move left pointer; when counts[i] == 0, countUnique-- +- time: O(nlogn) for initial sort and then O(n) to process +- space: O(n) +- What is hard here? To think of the idea of counting one usage of each row: + - when each all rows are used at least 1 time + - calculate the min dist + +#### Method2 PQ?, Similar to merging k array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104893/Java-Code-using-PriorityQueue.-similar-to-merge-k-array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/solution/ + + + +--- + +**55. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + +**56. [611. Valid Triangle Number.java](https://github.com/awangdev/LintCode/blob/master/Java/611.%20Valid%20Triangle%20Number.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Method1: Fix max and backward counting +- Sort nums: O(nlogn) +- Set max value fixed on right side at k + - set 2nd value from right index j + - set last value at min index i + - if `nums[i] + nums[j] > nums[k]`: with fixed j, i can pick from [i, j-1] combinations + - then j--, to pick another j candidate + - maintain a window [i,j]; if invalid, move i++ +- time: O(n^2) +- Note: very similar to 3-sum, fixing 1 index and use 2 pointers to move window + +#### Method2: Fix min and forward counting +- Sort nums: O(nlogn) +- Set min value at i + - set 2nd value at j=i+1; and 3rd value at k=i+2 + - find max of k that fits into triangle + - count all possible k candidates from [j+1, k] + - then move j to a new candidate +- O(n^2) + + + +--- + + + + + + + +## Basic Implementation (18) +**0. [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 + + + +--- + +**1. [Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)** Level: Easy Tags: [Basic Implementation] + +根据 Cosine Similarity 的公式, basic implementation + + + +--- + +**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 + + + +--- + +**3. [788. Rotated Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/788.%20Rotated%20Digits.java)** Level: Easy Tags: [Basic Implementation, String] + + +#### Basic Implementation of the rules +- [3,4,7] -> cannot rotate, failures. Must NOT have. set1 +- 2,5,6,9 -> good candidates. Must have 1. set2 +- [0,1,8] -> goes back to itself. can have +- loop over [1, N], count=int[10] appearance. + - set1 meet 0 + - set2 meet at least 1 + + + +--- + +**4. [849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array, Basic Implementation, Two Pointers] + + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, Two Pointers: 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` + + + + +--- + +**5. [408. Valid Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/408.%20Valid%20Word%20Abbreviation.java)** Level: Easy Tags: [Basic Implementation, String] + +tricky: find integer within a string +edge case: leading '0' should not be allow in such abbr. + + + +--- + +**6. [415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)** Level: Easy Tags: [Basic Implementation, Math, String] + + +#### Two Pointer +- Use i, j to process from end of 2 strings +- handle edge case for i, j + - if i < 0, its num = 0 (since we are doing sum, blindly setting 0 is okay) +- Note: `sb.insert(0, x)` is much slower than doing a final `sb.reverse()` + +#### If manually convertin to int[] +1. when converting to int[], remember to reverse string. +1. when converting to int[], remember to reserve extra space for carry + + + +--- + +**7. [1108. Defanging an IP Address.java](https://github.com/awangdev/LintCode/blob/master/Java/1108.%20Defanging%20an%20IP%20Address.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**8. [383. Ransom Note.java](https://github.com/awangdev/LintCode/blob/master/Java/383.%20Ransom%20Note.java)** Level: Easy Tags: [Basic Implementation, String] + +count chars in int[256] + + + +--- + +**9. [686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)** Level: Easy Tags: [Basic Implementation, Edge Case, String] + +Track: 纸上分析edge case. +Validation helps speed it up. + + + +--- + +**10. [485. Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/485.%20Max%20Consecutive%20Ones.java)** Level: Easy Tags: [Array, Basic Implementation] + + +- preserve max +- 清零count + + + +--- + +**11. [824. Goat Latin.java](https://github.com/awangdev/LintCode/blob/master/Java/824.%20Goat%20Latin.java)** Level: Easy Tags: [Basic Implementation, String] + + + + +--- + +**12. [119. Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/119.%20Pascal's%20Triangle%20II.java)** Level: Easy Tags: [Array, Basic Implementation] + + +简单处理 list. code is very similar to Pascal triangle I. + +- 注意 `list = Arrays.asList(x, y, z ...)` 给fixed-size list, 不能直接 list.add(). +- Use `new ArrayList<>(Arrays.asList(...))` to wrap it up. + + + + +--- + +**13. [443. String Compression.java](https://github.com/awangdev/LintCode/blob/master/Java/443.%20String%20Compression.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**14. [12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)** Level: Medium Tags: [Basic Implementation, Math, String] + + +#### String, Basic implementation +- Parse each digit based on rules +- 1) parse: analyze the situations + + +--- + +**15. [893. Groups of Special-Equivalent Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/893.%20Groups%20of%20Special-Equivalent%20Strings.java)** Level: Easy Tags: [Basic Implementation, String] + +Mark # of characters can be useful to print string signature + + + +--- + +**16. [118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)** Level: Easy Tags: [Array, Basic Implementation, List] + + + + +--- + +**17. [1033. Moving Stones Until Consecutive.java](https://github.com/awangdev/LintCode/blob/master/Java/1033.%20Moving%20Stones%20Until%20Consecutive.java)** Level: Easy Tags: [Basic Implementation, Sort] + + +#### Analyze to understand +- put 3 elements into array, sort and follow below rules: +- min: + - if 3 elements consecutive, 0 move. + - if only 1 pair of the two elemnets consecutive or if they have 1 slot in between, it needs exactly 1 move + - otherwise, at most 2 moves +- max: # of open slots between them (high - low + 1) - n, where n = 3 +- Follow up: `1040. Moving Stones Until Consecutive` is more interesting with special rulese (cannot move to `ending spot`), and it uses sliding window concept + + + +--- + + + + + + + +## Semaphore (1) +**0. [1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)** Level: Medium Tags: [Lock, Semaphore, Thread] + + +#### Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 + +#### Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +- semaphore: https://www.geeksforgeeks.org/semaphore-in-java/ + + + +--- + + + + + + + +## Backpack DP (8) +**0. [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, 而不考虑是哪个. + + + + + + +--- + +**1. [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) + + + +--- + +**2. [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 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**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 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) + + + +--- + +**5. [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 + + + +--- + +**6. [518. Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/518.%20Coin%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 + + + +--- + +**7. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + + + + + + + +## NestedInteger (3) +**0. [339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS, NestedInteger] + + +给一串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. + - 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) + +#### BFS +- bfs, queue, 处理queue.size() for a level +- use a level variable to track levels +- slower since it uses extra space, worst case O(n) of all items + + + +--- + +**1. [364. Nested List Weight Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/364.%20Nested%20List%20Weight%20Sum%20II.java)** Level: Medium Tags: [DFS, NestedInteger] + + +#### Method1: DFS +- Build a list of NestedInt +- DFS: + - sum up integers in the list are integers + - dfs on nested list + - overallSum = sum * (depth+1) + - End state: if no nested list (no more child dfs), return depth 1 +- Parent level: sum up all ints and times the (depth+1) + + +#### Method2: BFS +- Using stack to flatten all nestedList, and process in the end +- Can actually use list, does not need to be stack. +- uses more memory + + + +--- + +**2. [341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, NestedInteger, Stack] + + +#### Method1: Stack holds items from back of the list +- Option1: always set integer on top of the stack everywhere + - if not, poping stack until the top is integer + - code is easy +- Option2: in hasNext(), faltten the list in stack + +#### Method2: DFS preprocessing. +- 用queue to store all items. Kinda hack. Defeat the purpose of the problem. +- Super fast to query next(), however, needs to holds everything in memory +- O(n) + + + +--- + + + + + + + +## MaxHeap (5) +**0. [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个数字 + + + +--- + +**1. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**2. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**3. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**4. [373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)** Level: Medium Tags: [Heap, MaxHeap, MinHeap] + + +#### Method1: MinHeap wiht k size +- This approach follows the pattern of finding min pair: + - 1) only need to store k pairs + - 2) always start from min of A list and min of B list + - 3) pre-build k pairs honoring A list, and then pick the min pair, and start swapping with min of list B +- First attemp all first k pairs from nums1[i] against nums2[0] <=k : O(k) +- Use queue to pull min node and save results +- Use the nums1 val from the min node, pair up with nums2[j], add back to queue to sort +- overall runtime: O(klogk) +- space: O(k) + +#### Method2: MaxHeap with k size +- Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +- overall time: O(mnLogK) +- space: O(k) + + + +--- + + + + + + + +## Bit Manipulation (19) +**0. [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 + + + +--- + +**1. [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) 太慢, 不合题意 + + + +--- + +**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. [Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)** Level: Medium Tags: [Bit Manipulation] + + +给出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. + + + + +--- + +**4. [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. + + + +--- + +**5. [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的要求! + + + +--- + +**6. [Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)** Level: Easy Tags: [Bit Manipulation] + + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + + + +--- + +**7. [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 + + + +--- + +**8. [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] + + + +--- + +**9. [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负数补位. + + + +--- + +**10. [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找合适的数字. + + + +--- + +**11. [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 + + + +--- + +**12. [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看出有直接关系. + + + +--- + +**13. [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) + + + +--- + +**14. [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 + + + +--- + +**15. [405. Convert a Number to Hexadecimal.java](https://github.com/awangdev/LintCode/blob/master/Java/405.%20Convert%20a%20Number%20to%20Hexadecimal.java)** Level: Easy Tags: [Bit Manipulation] + +#### Unsigned Shift, Mask +- Move pointer: move digit after process 4 bits. + - `>>>` Unsigned right shift + - always fills 0 irrespective of the sign of the number +- Mas: `num & 0xf` = `num & 15` + + + +--- + +**16. [136. Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/136.%20Single%20Number.java)** Level: Easy Tags: [Bit Manipulation, Hash Table] + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + + + +--- + +**17. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**18. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + + + + + + + +## Quick Select (2) +**0. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**1. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high= 0 && y < col) {}` 确保不会跑脱 +- 同样的方式: 可以从右上角(0, col - 1) 开始, 代码稍微改一改 + +#### Divide and Conquer? +- TODO + + + +--- + +**1. [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,要乘一下。 + + + + +--- + +**2. [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 + + + +--- + +**3. [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 + + + +--- + +**4. [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 + + + +--- + +**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. [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)) + + + + +--- + +**7. [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 + + + +--- + +**8. [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/ + + + +--- + +**9. [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. + + + +--- + +**10. [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加上去就好. + + + +--- + +**11. [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) + + + +--- + +**12. [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了 + + + +--- + +**13. [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? + + + +--- + +**14. [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 一样 + + + +--- + +**15. [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. + + + + +--- + +**16. [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一下。 + + + +--- + +**17. [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 + + + + +--- + +**18. [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 +- 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 + + + +--- + +**20. [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 + + + +--- + +**21. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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, 自底向上建立起的。 + + + +--- + +**22. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**23. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**24. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**25. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**26. [1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +- time: O(n), visit all nodes +- space: O(logn), height of the tree + +#### Method2: HashMap, DFS. +- traverse tree and create `map ` to fast O(1) removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- This approach requires a dfs build of parentMap + - it is same amount of efforts to do the regular dfs removal. + - not a good solution +- time: O(n) +- space: O(n) + + + +--- + +**27. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**28. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**29. [973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)** Level: Medium Tags: [Divide and Conquer, Heap, Sort] + + +#### PriorityQueue +- Create customized Point{} class +- Sort by distance +- Maintain queue size <= K + +#### Divide and Conquer +- ?, select sort? + + + +--- + +**30. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**31. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**32. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**33. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**34. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**35. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**36. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**37. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, highvalue. + + + +--- + +**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. [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] + + +跟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] + + + +--- + +**3. [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; + + + +--- + +**4. [198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +搜刮房子, 相邻的不能碰. 每个房子里有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虽然抽象, 但是更加实用. + +#### Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- calculation + - dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]). The prior house can be either state. + - dp[i][1] = dp[i - 1][0] + nums[i]. The prior house must be `NOT ROBBED`. +- return `Math.max(dp[n - 1][0], dp[n - 1][1])` + + + +--- + +**5. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**6. [256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, depends on the color of dp[n-1] + - 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- Need to have status with dp array: int[index][color status] + - dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. + - dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- time: O(nm), m = # of colors +- space: O(nm) + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 +- space:O(1) + + + +--- + +**7. [265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + + +一排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) + + + +--- + + + + + + + +## Sliding Window (13) +**0. [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个数字 + + + +--- + +**1. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**2. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**3. [567. Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/567.%20Permutation%20in%20String.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + +#### Method1: Sliding window with left/right Pointers +- Sliding window template: + - 1) Check right pointer and move right + - 2) Move left when necessary + - 3) Verify count == 0 & end state + - Note: normally 2) and 3) are in reversed order; this problem is a bit different +- This is efficient when the number of characters is not limited to 26, the runtime is still O(m + n) +- time: O(m + n), m = s1 length, n = s2 length +- space: O(k), k = # of possible chars, 26 in this case + +#### Method2: Two Pointer, but brutle verify freq count +- 如果做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) 这一步 +- time: (m + n) + - However, if # of possible chars is more than 26 + - For example, `k unique characters`, then the runtime will become: O(m + nk) + +- space: O(k), k = # of possible chars, 26 in this case + + + +--- + +**4. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**5. [239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Method1: Deque, Monotonous queue +- 维持monotonuous queue: `front is always at max` and the `tail end is min`. Always need to return the max end of queue. +- when adding new elements x: + - 1) start from small-end of the queue + - 2) drop all smaller elements + - 3) append to the ending element that is larger than x. + - This is to maintain a front->tail decreasing queue +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`: better than using arraylist, since DeQueue(linked list) removes at O(1) cost +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +- Option1: sliding window template using right/left + while loop + - 1) tailing the new number to max queue, if applicable + - 2) process: record max + - 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] +- Option2: same concept, but use index `i` to mark right, and `i - k + 1` to mark left. +- time: O(n), one pass +- space: O(k), store the deque + + +#### Method2: Heap +- can always build a `class Node{index, val}`; and sort them with PQ of size k +- time: O(nlogK) +- space: O(k) +- this is not linear time, not as good as method1 + + + +--- + +**6. [1040. Moving Stones Until Consecutive II.java](https://github.com/awangdev/LintCode/blob/master/Java/1040.%20Moving%20Stones%20Until%20Consecutive%20II.java)** Level: Medium Tags: [Array, Sliding Window] + + + +#### Analyze to understand +- Make sure to sort array: we need to use the actual number range `A[j] - A[i]`, which requires the array to be sorted +- we want to form a new array where A[n-1] - A[0] + 1 == n; order does not matter but all slots need to be filled consecutivly +- max moves: https://leetcode.com/problems/moving-stones-until-consecutive-ii/discuss/289357/c%2B%2B-with-picture + - A interval will be automatically dropped between A[0] and A[1], if moving A[0] first + - Same, a interval between A[n-2] and A[n-1] will be dropped when moving A[n-1] first + - so largest possible move = firstItem + remaining range size - n items = 1 + (A[n-1] - A[1] + 1) - n = A[n-1] - A[1] -n + 2 + - or A[n-2] - A[0] - n + 2 +- min moves: `Sliding Window` + - use slinding window to assume a right pointer, to make sure A[right] - A[left] + 1 < n; otherwise, move left++ + - check # of included stones + - calculate remaining, which is remaining moves +- Handle min move edge case: + - Consecutive Array up to right = n - 1; need 2 moves to finish + + + +--- + +**7. [346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison +- Note: if we it is calculate moving-window-product, better to use deque :) +- Sliding window? + - It has the spirit of slinding window: 1) maintain a range; 2) check range size `if (queue.size() > size)` + - Though, the solution must use a data structure to store data; it is not the traditional sliding window type of `left/right` pointer problem + + + +--- + +**8. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**9. [1004. Max Consecutive Ones III.java](https://github.com/awangdev/LintCode/blob/master/Java/1004.%20Max%20Consecutive%20Ones%20III.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + + +#### Sliding window + Left/Right Two Pointers +- https://leetcode.com/problems/max-consecutive-ones-iii/solution/ +- Start with DFS thought, but realize redundant calculations: + - we never need to flip 2 indexes [A], [C] from 0 -> 1, if there is a [B] in middle that is 0 too + - the flipped k zeroes must be consecutive too +- we can utilize two pointers to establish a window that captures k zeroes + - always expend right pointer; if seeing an zero, k-- + - note: `len = right - left + 1` is the ongoing max length + - when k < 0 (too many zeros), we need to slide the left side of the window to make sure: + - keep window len + - potentially do k++ when A[left]==0 +- goal: matain a max size of the window, until right == n +- return (right - left). at this moment, right == n, so no need to (right - left + 1) + + + +--- + +**10. [438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)** Level: Medium Tags: [Hash Table, Sliding Window, Two Pointers] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### Method1: Sliding Window, Hash Table. +- A creative way of using anagram char count `hash[c] >= 0` to determine if the curr c is a target char of the deesired anagram. + - because we always reduce hash[c]-- for all characters + - so only the anagram chars would be `hash[c] >= 0` after reducing. +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +- Slinding window always has left/right pointer: + - 1) at any given time move 1 index at a time: expand right window, process rsult, shrink left window + - 2) one of the basic goal is to maintain fixed window size +- algo: + - calc char freq of the target p, and store in a hash[256]; it will be used to distinguish anagram chars: `hash[c] >= 0` indicates a anagram char + - expand right window: move right to expand the window; ONLY when meeting a anagram char, count-- + - process result: if count reduces to 0, one anagram is found + - shrink left window: if (right - left) == p.length(), drop curr left char, and move forward +- how could we rely on only just `count == 0`? + - the hidden pre-condition is `right - left must already be p.length()`, which is validaterd in prev iteration +- time: O(n) +- space: O(1) + +#### Method2: HashTable +- count character apperance -> hash table, here just a int[26] + - use a window to record count++ and count--, in order to compare with countP +- prep the countP takes O(m) time +- time: O(n) + O(m) +- space: O(n) + + + + + +--- + +**11. [632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)** Level: Hard Tags: [Hash Table, Sliding Window, Two Pointers] + + +#### Method1: Sliding Window +- First sort all of the items together by actual val using `Node {int val, int row}` +- Slinding window goal: + - 1) use right to find range that touches all rows, + - 2) use left to shrink the range +- Sliding Window Template + - move right pointer + - Counts[i] = # of elements used in left/right range + - when counts[i] == 0, countUnique++; the number of row/list being included + - when count == row size: + - processing & save shorter range by using left/right Pointers + - move left pointer; when counts[i] == 0, countUnique-- +- time: O(nlogn) for initial sort and then O(n) to process +- space: O(n) +- What is hard here? To think of the idea of counting one usage of each row: + - when each all rows are used at least 1 time + - calculate the min dist + +#### Method2 PQ?, Similar to merging k array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104893/Java-Code-using-PriorityQueue.-similar-to-merge-k-array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/solution/ + + + +--- + +**12. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + + + + + + + +## Topological Sort (6) +**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. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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 + + + +--- + +**2. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**3. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**4. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**5. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + + + + + + + +## Pruning (1) +**0. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + + + + + + + +## Quick Sort (7) +**0. [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) + + + +--- + +**1. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**2. [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) + + + +--- + +**3. [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 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 + + + +--- + +**5. [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 原题有一点点不一样. + + + + +--- + +**6. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**2. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**3. [274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.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. + +#### Couting Sort / 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 + +#### More about Counting Sort +1. Application: for number/character range +1. Steps: + - Find range, define countArray + - Count element and record in the array + - PreSum the countArray + - Start from beginning of the array, `print & decrese count` to produce the sorted elements + + + + +--- + + + + + + + +## Greedy (24) +**0. [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 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 + + + +--- + +**1. [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哪一个。 + + + +--- + +**2. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**3. [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 + + + +--- + +**4. [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. + + + +--- + +**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] + + + + +--- + +**6. [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 + + + +--- + +**7. [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] + + +跟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] + + + +--- + +**8. [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]) + + + +--- + +**9. [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 + + + +--- + +**10. [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. + +可能有简化的余地, 代码有点太长. +比如试一试不用额外空间? + + + +--- + +**11. [1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)** Level: Medium Tags: [Array, Greedy, Permutation] + + +#### Analyze Permutation behavior +- concept similar to `31. Next Permutation` +- 1) first pass: find the one that is in incorrect order +- 2) second pass: find the right spot to swap + + + +--- + +**12. [1007. Minimum Domino Rotations For Equal Row.java](https://github.com/awangdev/LintCode/blob/master/Java/1007.%20Minimum%20Domino%20Rotations%20For%20Equal%20Row.java)** Level: Medium Tags: [Array, Greedy] + + + +#### Method1: Count all occurrance, and count on overlap indexes +- when there is a value that can cover entire row of size n + - it must be: `n = countA[i] + countB[i] - overlap[i]` +- Code easy to write and read +- time: O(n) +- space: O(1) + +#### Method2: Negative count +- Observation: if A[0] works, no need to check B[0]. +- Because if both A[0] and B[0] exist in all dominoes, + - when you swap A[0] in a whole row, + - you will swap B[0] in a whole at the same time. + - The result of trying A[0] and B[0] will be the same. +- time: O(n) +- space: O(1) + +#### Method3: positive count Match +- there should exist 1 numbers, that can appear in (A[i], B[i]). +- failure case: there exist at least 1 index, that does not have the common number +- maximum case: there can be 2 numbers, that both will make it work. +- findCommon2, and count them: + - set.add(A[0], A[B]), + - if any new one does not exist in set, remove it from set + - if set is empty() , return -1 +- use the 2 numbers from set to do a sweep and count in A, O(n), return the less appearance one. +- time: O(n) +- space: O(1) + + + +--- + +**13. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**14. [55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%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 +- 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) +- space: O(1) + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (j + A[j] >= i), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- time: O(n^2) +- space: O(n) + + + + +--- + +**15. [134. Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/134.%20Gas%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 +- 而不是求: 最后点可否走完/最值/计数 + + + +--- + +**16. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + +**17. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**18. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**19. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**20. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**21. [605. Can Place Flowers.java](https://github.com/awangdev/LintCode/blob/master/Java/605.%20Can%20Place%20Flowers.java)** Level: Easy Tags: [Array, Greedy] + + +#### Array +- just check flowerbed[i-1] & flowerbed[i+1] and count + + + +--- + +**22. [402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)** Level: Medium Tags: [Greedy, Monotonous Stack, Stack] + + +#### Monotonous Stack (Increasing) +- Greedy: Remove 1) earlier digits(数位靠前权值大), 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. + + + + +--- + +**23. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + + + + + + + +## 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. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**2. [346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison +- Note: if we it is calculate moving-window-product, better to use deque :) +- Sliding window? + - It has the spirit of slinding window: 1) maintain a range; 2) check range size `if (queue.size() > size)` + - Though, the solution must use a data structure to store data; it is not the traditional sliding window type of `left/right` pointer problem + + + +--- + + + + + + + +## Coordinate DP (17) +**0. [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 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 + + + +--- + +**1. [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);` + + + +--- + +**2. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**3. [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作比较;求的所有情况的最大值嘛。 + + + +--- + +**4. [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. + + + + +--- + +**5. [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] + + +给一串 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) + + + +--- + +**6. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**7. [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) + + + + +--- + +**8. [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 +还没有做 + + + +--- + +**9. [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; + + + +--- + +**10. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**11. [221. Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/221.%20Maximal%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; +- init: 每个点都可能是边长1, 如果 matrix[i][j] == '1' +- Space, time O(mn) +- Rolling array: [i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + + +--- + +**12. [62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +#### DP, 加法原理 +- 计数DP: 2 ways to reach (i,j): dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + - non-overlapping: `dp[i - 1][j]`, `dp[i][j - 1]` + - covers the only 2 possible way to reach (i,j) +- initialization: dp[i][0] = 1, dp[0][i] = 1 + - Of course, row i = 0, or col j = 0, there is only 1 way to reach +- time O(mn), space O(mn) + +##### 滚动数组 Rolling Array +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + +#### DFS + Memoization +- move from (0,0) towards (m, n) +- use Map as memoization technique + + + +--- + +**13. [64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%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(N) + - 1) 需要在同一个for loop里面完成initialization, 2) reuse dp[i][j] + - Reason: dp[i % 2][j] 在被计算出来的时候, 马上在下一轮会被用. 被覆盖前不用,就白算 + - Option3 below initialize dp outside of loop: 看起来固然简单, 但是不方便空间优化 + +#### DFS (top-down) Thinking process +- Enumerate the possible 2 paths: go right, go down +- sub problem: dfs(i+1,j), dfs(i,j+1); pick the min of the two +- memoization: after the path from a point (i,j) to end is computed, record memo[i][j] to avoid repatative calculation +- time: O(mn), only visite and record memo[i][j] once. +- space: O(mn) extrem case of m=100000, n = 2; the stack height is O(mn) + + +--- + +**14. [63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%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] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + + + +--- + +**15. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + +**16. [361. Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/361.%20Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Method1: 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 的变换. + +#### Method2: Analyze, col count array: +- Inspired by: https://leetcode.com/problems/bomb-enemy/discuss/83387/Short-O(mn)-time-O(n)-space-solution +- Analyize the problem: need to add up 2 directions at any given point. + - Notice 1: if I traverse row by row, each colSum at a specific col j is likely to be the same + - Unless there is a Wall in last row, so we have to calclate the col sum starting from current row, below the Wall + - Notice 2: for row it is the same: + - If I in a new spot row[i][j], where (i-1) is Wall, i need to sum row from 0 +- we will process each point: + - process row by row and add up row sum + - buffer col[j] in an array vertically so we can resue + - make sure to recalculate row sum or col sum if last row index or last col index is Wall +- time: O(mn) traversal +- space: O(n) only use a column array + + + +--- + + + + + + + +## Monotonous Stack (3) +**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)的巧妙用法. + + + + +--- + +**1. [402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)** Level: Medium Tags: [Greedy, Monotonous Stack, Stack] + + +#### Monotonous Stack (Increasing) +- Greedy: Remove 1) earlier digits(数位靠前权值大), 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. + + + + +--- + +**2. [739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)** Level: Medium Tags: [Hash Table, Monotonous Stack, Stack] + + +#### Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) + +#### Method2: `Map `, kinda of like bucket sort +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: + - 1) record tempIndex[currTemp] = i; + - 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result + + +--- + + + + + + + +## Partition (3) +**0. [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) + + + +--- + +**1. [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看起来更容易理解. + + + +--- + +**2. [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 + + + +--- + + + + + + + +## 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. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**3. [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) + + + +--- + +**4. [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走过的地方 + + + +--- + +**5. [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 + + + +--- + +**6. [36. Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/36.%20Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited row/col/block. + - 在nest for loop里面validate row,col,and block. + - Special: validate block要利用i 和 j 增长的规律 +- i, j are [0~n) can build block boundary in a 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 +- 可能代码稍微复杂一点 + + + +--- + +**7. [158. Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/158.%20Read%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就好了. +- alaternatively: use queue to hold so we do not need to worry about size + + + +--- + +**8. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**9. [246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math, Two Pointers] + + +根据题意枚举, 再根据规则basic implementation + +#### HashTable + Two Pointer +- compare left/right + +#### Alter input +- flip number (6 and 9), and then reverse the string, see if the string is the same. +- takes more + + + + +--- + +**10. [639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%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]) +- Enumeration: + - 具体分析 '*' 出现的位置, 枚举出数字, 基本功. + - 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) + - 枚举好以后, 其实这个题目的写法和思考过程都不难 +- Mode: + 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. + + +#### DFS + memoization +- DFS top-down approach is used to analyze the problem. The logic flow: +- 1) consider the case of 1 letter or 2 letters. +- 2) one letter: + - [*]: + 9 * dfs(s, i + 1) + - [0~9]: + dfs(s, i + 1) +- 3) two letters: + - [_, *]: depends + - [*, _]: depends + - [*, *]: + 15 * dfs(s, i + 2) +- memo[i] records # of ways to decode from [i ~ n] +- space: O(n), Size of recursion tree can go upto n +- time: O(n), `memo array is filled exactly once`!!! + + + +--- + +**11. [68. Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/68.%20Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- greedy approach: line up as many words as possible; once exceed the MaxLength, justify the list of words +- Steps + - 1) Split & group + - 2) Juststify a row of words + - 3) clean up last row +- Calcualte bounded row length = `width + (list.size() - 1)`. `list.size()-1` = Minimum amount of slot/space used. +- Calculate max ave spaces ot insert into each slot = `totalExtraSpace/slot` +- `Juststify a row of words`: + - 1) take a list of words and assume minimum 1 space in-between words + - 2) distribute the remaining spaces evenly to each slot +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**12. [157. Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/157.%20Read%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)` +- Edge Case: + - When there is not enough space to the result buffer, `i + 3 > n`, then only copy what we can: `Math.min(n - i, count)` + - `count < 4` meaning there is not enough content to read, break + + + +--- + +**13. [273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%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来找到结果, 每段的处理方法都是一样的 +- Note: + - StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 + - 注意加 " " 的时候, 如果多余, 要`trim()` + - 注意, `小于20的数字, 有自己的特殊写法, 需要额外handle` + - 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 +- Thinking process: + - `1 ~ 19`: [one, two ... nine, ten, eleven, ...., ninteen] + - `20 ~ x0`: [twenty, thirty, fourty, ... ninety] + - `x00`: hundred: 100 + - thousand: 10^3 + - million: 10^6 + - billion: 10^9 + - trillian: 10^12 way over 2^31, not needed +- plan: + - parse 3 digits at a time + - convert the 3 digit to [xx hundred xx-ty x] + - come up with a string[] + - insert the thousands/million/billion to the string[] + + + +--- + +**14. [65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + + + + + + + +## PreProduct (2) +**0. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**1. [238. Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/238.%20Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 第一轮:PreProduct (跟preSum的感觉有点像) + - PreProduct[i] stores product from num[0] -> num[i-1] (skipping current num[i]) + - init preProduct[i] = 1, as base for product + - 错过一位操作: always `preProduct[i] *= carry;` and `carry *= nums[i]` +- 第二轮: 从右边乘起, 每次在index i, 收到的carry都是 `nums[i+1] *....* nums[end]` + - 第一轮的结果 * 第二轮的结果, 刚好在index i 缺少掉 nums[i]. 如题所愿. +- Time: O(n) + + + +--- + + + + + + + +## Binary Tree (14) +**0. [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, 但是数字可为多位 + + + +--- + +**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. [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的边框限制好,中间就全部遍历了。 + + + +--- + +**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. [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 + + + + +--- + +**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. [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. [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就可以了. + + + +--- + +**8. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**10. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**11. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**12. [257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%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 +- time: visit all n nodes +- space: to hold all paths, O(nlogn) + - O((n-1)/2) = O(n) nodes at leaf + - O(logn) depth + +#### 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 + + + + +--- + +**13. [114. Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/114.%20Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### Method1: DFS return tail node +- DFS to return the tail of the flattened list +- Careful handling the null child. Choose one and both works: + - Option1: put non-null as right child and continue dfs + - Option2: put non-null as left child and continue dfs + +#### Method2: void DFS +- latten the tree, no extra space. + - 1. Set right node in buffer, and connect: `root.right = root.left`, DFS flatten(root.right) + - 3. 移花接木: traverse to tail of the current root.right and attach the buffer node. + - 3. flatten the remaining of buffer + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + + + + + + + +## Reservior Sampling (1) +**0. [398. Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/398.%20Random%20Pick%20Index.java)** Level: Medium Tags: [Reservior Sampling] + + +#### 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. +- probability theory: + - 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 + + + + +--- + + + + + + + +## Slow Fast Pointer (3) +**0. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**1. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**2. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + + + + + + + +## Expression Tree (5) +**0. [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, 但是数字可为多位 + + + +--- + +**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. [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. [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` + + + +--- + +**4. [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就可以了. + + + +--- + + + + + + + +## Binary Search (45) +**0. [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 + + + +--- + +**1. [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) + + + +--- + +**2. [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 + + + +--- + +**3. [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) + + + +--- + +**4. [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 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**5. [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)) + + + + +--- + +**6. [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] + + +给一串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 + + + +--- + +**7. [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 是理所当然的 + + + +--- + +**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. [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) + + + +--- + +**10. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**11. [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. + + + + +--- + +**12. [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 + + + +--- + +**13. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + +--- + +**14. [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) + + + +--- + +**15. [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. + + + +--- + +**16. [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. + + + + +--- + +**17. [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), 就不写了 + + + +--- + +**18. [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. + + + +--- + +**19. [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 公式 + + + +--- + +**20. [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. + + + +--- + +**21. [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] + + +给一个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) + + + + +--- + +**22. [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 + + + +--- + +**23. [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 的情况. + + +--- + +**24. [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. + + + +--- + +**25. [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? + + + + +--- + +**26. [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 + + + +--- + +**27. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**28. [275. H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/275.%20H-Index%20II.java)** Level: Medium Tags: [Binary Search] + + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +Aim to find the lowest index mid, which maximize h = n - mid + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + + + +--- + +**29. [222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, DFS, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### Method1: DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样 + - 如果一样, 直接 2 ^ h - 1就好 + - 不一样的话, 再DFS +- calculate `2^(h)`: 位运算, Math.pow(2, h) = 2 << (h - 1). 神奇! + - 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- time: O(n) visit all nodes on 1 side +- space: O(h) visit all nodes on 1 side + + +#### Method2: Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +#### Method3: Binary Search +- NOT DONE, TODO: https://leetcode.com/problems/count-complete-tree-nodes/solution/ + + + +--- + +**30. [1060. Missing Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1060.%20Missing%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Binary Search] + + +#### Binary Search +- total missing nums = nums[curr] - nums[0] - curr +- edge case: if k > total missing nums, then just add the diff from nums[end] +- otherwise, find this `missing count == k` in the nums using binary search +- After binary search: `start + 1 == end`: + - re-calculate `count = nums[start] - nums[0] - start;` + - output final num: `nums[start] + k - count;` +- Option1: always compare total missing nums count +- Option2: compare partial missing nums count (inspired by: https://leetcode.com/problems/missing-element-in-sorted-array/discuss/303444/Java-O(logN)-solution-Binary-Search) + + + + +--- + +**31. [875. Koko Eating Bananas.java](https://github.com/awangdev/LintCode/blob/master/Java/875.%20Koko%20Eating%20Bananas.java)** Level: Medium Tags: [Binary Search] + + + +#### Binary Search +- Bianry serach on the min/max value range +- The mid value is calcualted with helper function `calc(piples, k)` +- find celing: `count += (i - 1) / k + 1`, faster than `Math.ceil(i / k)` +- time: O(logm) to find the best velocity, assume total range is m; O(n) for each `calc` call + + + +--- + +**32. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**33. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**34. [88. Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- Also most identical to `33. Search in Rotated Sorted Array`: + - find where nums[mid] lands by comparing to nums[start]. i.e., if nums[mid] < nums[start], on right half of the array + - when `nums[mid] == nums[start]`: duplicate. Shift by start++ +- the worst case of `nums[mid] == nums[start]` willl cause O(n), +- but if duplicate is not entire array, should be O(logn) + + + +--- + +**35. [367. Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/367.%20Valid%20Perfect%20Square.java)** Level: Easy Tags: [Binary Search, Math] + + +#### Binary找sqrt +- binary search template: mid+1, mid-1, `start <= end` +- define index as long. + + + +--- + +**36. [270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +Concept: Iterate over all logN nodes in the BST and record the closest. Rather than finding the value at +/- 0.5 precision + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, until null leaf +- time: O(logn), space O(1) since no extra space used + +#### DFS, Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return +- time: O(logn), space: O(logn) + + + + +--- + +**37. [852. Peak Index in a Mountain Array.java](https://github.com/awangdev/LintCode/blob/master/Java/852.%20Peak%20Index%20in%20a%20Mountain%20Array.java)** Level: Easy Tags: [Binary Search] + + +#### Binary Search +- binary search to find A[i-1] < A[i] < A[i+1] + - if [mid-1] < [mid+1], on left slope, start = mid + - if [mid-1] > [mid+1], on right slope, end = mid +- init: start == 1, end = n - 2; + + + +--- + +**38. [981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)** Level: Medium Tags: [Binary Search, Hash Table, TreeMap] + + +#### Method1: Binary Search +- use hash to store +- binary serach on list of values + +#### Method2: TreeMap +- use hash to store > +- treemap.floorKey(timestamp) finds the top item below certain timestamp + + + +--- + +**39. [69. Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/69.%20Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + +#### sqrt(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 + + + +--- + +**40. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + +**41. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**42. [33. Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/33.%20Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 + - 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` + - 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- Binary search template: + - 1) `start + 1 < end` (adjacent indexes) + - 2) start/end = mid, + - 3) compare start and end individually + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**43. [34. Find First and Last Position of Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/34.%20Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- need search left bound & right bound. +- use input parameter `direction` to binary search function +- Option0: simplification, inspired by `278. First Bad Version - Method1: Check is-NOT-BadVersion` + - 1) if found match, but NOT sure it is desired boundary, just leave it and keep going + - 2) check the final results after `binary search while loop` completes + - WHY? code is easier to read in this way. + + + +--- + +**44. [278. First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/278.%20First%20Bad%20Version.java)** Level: Easy Tags: [Binary Search] + + +#### Method1: Check is-NOT-BadVersion +- simply binary Search: if not bad, assign `start = mid+1` + +#### Method2: Check ifBadVersion +- 根据isBadVersion的性质,判断还如何end=mid or start=mid. +- A bit more code to handle + + + +--- + + + + + + + +## TreeMap (5) +**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. + + + + +--- + +**1. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**2. [981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)** Level: Medium Tags: [Binary Search, Hash Table, TreeMap] + + +#### Method1: Binary Search +- use hash to store +- binary serach on list of values + +#### Method2: TreeMap +- use hash to store > +- treemap.floorKey(timestamp) finds the top item below certain timestamp + + + +--- + +**3. [1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)** Level: Medium Tags: [Array, Hash Table, TreeMap] + + + +#### Hash Table, TreeMap; atomic save +- store efficiently: use List>. only preserve changed itemd +- if no match, find last modifed item based on snapId, use TreeMap.floorEntry + - map.floorEntry(id) return the item.key lower or equal to id +- Utilize a `buffer: Map` and perform atomic save + + + +--- + +**4. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + + + + + + + +## BIT (2) +**0. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**1. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + + + + + + + +## Rotation (1) +**0. [189. Rotate Array.java](https://github.com/awangdev/LintCode/blob/master/Java/189.%20Rotate%20Array.java)** Level: Easy Tags: [Array, Rotation] + +#### Rotate array in place +- rotate all +- rotate 2 sides: < k or >= + + +#### Rotate by buffer the k array + + + +--- + + + + + + + +## Adjacency Matrix (1) +**0. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + + + + + + + +## Deque (2) +**0. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**1. [239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Method1: Deque, Monotonous queue +- 维持monotonuous queue: `front is always at max` and the `tail end is min`. Always need to return the max end of queue. +- when adding new elements x: + - 1) start from small-end of the queue + - 2) drop all smaller elements + - 3) append to the ending element that is larger than x. + - This is to maintain a front->tail decreasing queue +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`: better than using arraylist, since DeQueue(linked list) removes at O(1) cost +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +- Option1: sliding window template using right/left + while loop + - 1) tailing the new number to max queue, if applicable + - 2) process: record max + - 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] +- Option2: same concept, but use index `i` to mark right, and `i - k + 1` to mark left. +- time: O(n), one pass +- space: O(k), store the deque + + +#### Method2: Heap +- can always build a `class Node{index, val}`; and sort them with PQ of size k +- time: O(nlogK) +- space: O(k) +- this is not linear time, not as good as method1 + + + +--- + + + + + + + +## Lock (1) +**0. [1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)** Level: Medium Tags: [Lock, Semaphore, Thread] + + +#### Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 + +#### Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +- semaphore: https://www.geeksforgeeks.org/semaphore-in-java/ + + + +--- + + + + + + + +## Thread (1) +**0. [1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)** Level: Medium Tags: [Lock, Semaphore, Thread] + + +#### Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 + +#### Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +- semaphore: https://www.geeksforgeeks.org/semaphore-in-java/ + + + +--- + + + + + + + +## Array (126) +**0. [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 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 + + + +--- + +**1. [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 + + + +--- + +**2. [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) 太慢, 不合题意 + + + +--- + +**3. [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);` + + + +--- + +**4. [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 + + + +--- + +**5. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**6. [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. + + +--- + +**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. [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) + + + +--- + +**9. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**10. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + + + +--- + +**11. [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)) + + + + +--- + +**12. [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 + + + + +--- + +**13. [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] + + + +--- + +**14. [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] + + +给一串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 + + + +--- + +**15. [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 + + + +--- + +**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. [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作比较;求的所有情况的最大值嘛。 + + + +--- + +**18. [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, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**19. [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 是理所当然的 + + + +--- + +**20. [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 + + + +--- + +**21. [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的用法: |, & + + + + +--- + +**22. [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. + + + +--- + +**23. [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 + + + +--- + +**24. [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来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**25. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**26. [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. + + + +--- + +**27. [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) + + + +--- + +**28. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**29. [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的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**30. [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[] + + + +--- + +**31. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + +--- + +**32. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + +--- + +**33. [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` + + + +--- + +**34. [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 + + + +--- + +**35. [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) + + + +--- + +**36. [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. + + + +--- + +**37. [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. + + + + +--- + +**38. [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), 就不写了 + + + +--- + +**39. [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; + + + + +--- + +**40. [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 + + + +--- + +**41. [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)))` + + + +--- + +**42. [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] + + +跟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] + + + +--- + +**43. [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]) + + + +--- + +**44. [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 + + + +--- + +**45. [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 + + + +--- + +**46. [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走过的地方 + + + +--- + +**47. [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]` + + + +--- + +**48. [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 原题有一点点不一样. + + + + +--- + +**49. [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. + + + +--- + +**50. [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? + + + + +--- + +**51. [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 + + + +--- + +**52. [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)的巧妙用法. + + + + +--- + +**53. [[lint]. Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array, Lint] + + + + +--- + +**54. [[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)** Level: Medium Tags: [Array, Hash Table, Lint] + + +把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. + + + + +--- + +**55. [[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)** Level: Medium Tags: [Array, Lint, Two Pointers] + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**56. [[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)** Level: Easy Tags: [Array, Lint, 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) + + + + +--- + +**57. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**58. [[lint]. Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array, Lint] + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**59. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**60. [42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. + +#### Method1: Max wall from both sides +- Array: Left Max Wall vs Right Max Wall. +- 对于每个index而言, vertically 能存放的最大水柱, 就是靠 左 右 最高墙决定的: + - min(leftHighestWall, rightHighestWall) - currHeight. +- time: O(n) +- space: O(n) + +#### Method2: Two Pointers +- Optimization from Method1: two pointer, 还是找左边最高和右边最高. O(1) space. +- 利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +- always limited by the shorter wall: 左边按照maxLeft来计算, 右边按照maxRight来计算. +- time: O(n) +- space: O(1) + +#### Method3: 2 Pointers, start from 2 sides +- 1. 找中间最高bar的index +- 2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条 +- 3. 每次还要减去block自身的height +- time: O(n) +- space: O(1) + +#### Method4: Stack +- 主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +- 最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +- 用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. +- time: O(n) +- space: O(n) + + + + +--- + +**61. [448. Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/448.%20Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)** Level: Easy Tags: [Array, Bucket Sort] + + +#### Method1: Bucket Sort concept, set val to its correct position +- Given: values are [1,n], so val can represent index. Therefore, set val to its correct position +- 小心handle i: + - value是 1-based + - 每次换位, 需要`i--`, 重新省察`nums[i]` + +#### Method2: 做标记 (negative number, or super large number) +- Option1: use negative number to mark visited: + - 很巧妙地运用了标记的方法, 标记成负数,证明visit过。 + - Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! +- Option2: use large number (larger than n) + - 跟方法2类似,也是做标记,这一次是加上一个大于n的数(因为题目给了n的border),最后check一下就好。为不超Integer.MAX_VALUE, 每次加n前,取余数。 + - 做标记的方法固然快,但是相对来说比较hacky,在常规的代码中,估计不会用到. + + + + +--- + +**62. [849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array, Basic Implementation, Two Pointers] + + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, Two Pointers: 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` + + + + +--- + +**63. [766. Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/766.%20Toeplitz%20Matrix.java)** Level: Easy Tags: [Array] + + +#### Check diagonal +- 似乎没什么算法特点, 就是array基本运算, 然后分割成一个helper function去做重复计算, 剪短代码. +- 注意check MxN 的分界线. + +#### Simpler Solution +- the goal is to check [i][j] == [i+1][j+1] for every i and j. + + + +--- + +**64. [1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)** Level: Medium Tags: [Array, Greedy, Permutation] + + +#### Analyze Permutation behavior +- concept similar to `31. Next Permutation` +- 1) first pass: find the one that is in incorrect order +- 2) second pass: find the right spot to swap + + + +--- + +**65. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**66. [665. Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/665.%20Non-decreasing%20Array.java)** Level: Easy Tags: [Array] + + +- 比较升序的时候, 必须要估计到 `i-1, i, i+1` 三个数位. +- 写出来`i-1, i, i+1`之间的关系, 然后做合理的fix. + 1. reduce nums[i+1] to fix + 1. raise nums[i+1] to fix +- 需要真的fix数组, 因为loop through做比较时会用到fix后的数字. + + + + +--- + +**67. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**68. [80.Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/80.Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Basic +- 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其实就足够. + +#### Follow up: k duplicates, Two Pointers +- when index i and i-1 are diff, use count=1 to start +- in while loop, keep count++ until count==k +- reset when next diff comes in + + + +--- + +**69. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**70. [1007. Minimum Domino Rotations For Equal Row.java](https://github.com/awangdev/LintCode/blob/master/Java/1007.%20Minimum%20Domino%20Rotations%20For%20Equal%20Row.java)** Level: Medium Tags: [Array, Greedy] + + + +#### Method1: Count all occurrance, and count on overlap indexes +- when there is a value that can cover entire row of size n + - it must be: `n = countA[i] + countB[i] - overlap[i]` +- Code easy to write and read +- time: O(n) +- space: O(1) + +#### Method2: Negative count +- Observation: if A[0] works, no need to check B[0]. +- Because if both A[0] and B[0] exist in all dominoes, + - when you swap A[0] in a whole row, + - you will swap B[0] in a whole at the same time. + - The result of trying A[0] and B[0] will be the same. +- time: O(n) +- space: O(1) + +#### Method3: positive count Match +- there should exist 1 numbers, that can appear in (A[i], B[i]). +- failure case: there exist at least 1 index, that does not have the common number +- maximum case: there can be 2 numbers, that both will make it work. +- findCommon2, and count them: + - set.add(A[0], A[B]), + - if any new one does not exist in set, remove it from set + - if set is empty() , return -1 +- use the 2 numbers from set to do a sweep and count in A, O(n), return the less appearance one. +- time: O(n) +- space: O(1) + + + +--- + +**71. [485. Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/485.%20Max%20Consecutive%20Ones.java)** Level: Easy Tags: [Array, Basic Implementation] + + +- preserve max +- 清零count + + + +--- + +**72. [896. Monotonic Array.java](https://github.com/awangdev/LintCode/blob/master/Java/896.%20Monotonic%20Array.java)** Level: Easy Tags: [Array] + +basic implementation + + + +--- + +**73. [26.Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/26.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 + + + +--- + +**74. [41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)** Level: Hard Tags: [Analysis, Array, Edge Case] + + +给一串无序数字, 有负数: 找这个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 +1. 如果nums==null, 其实missing positive integer 自然而然是 1 +1. 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) + - 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +1. 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + +**75. [717. 1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/717.%201-bit%20and%202-bit%20Characters.java)** Level: Easy Tags: [Array] + +理解题目: +1. single-bit always starts with '0', two-bits always start with '1'. +1. Therefore there is ONLY 1 way to reach end. + +#### 方法1 +Greedy. +从第一个bit开始: 如果 % 2 == 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. + + + +--- + +**76. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**77. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**78. [259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +1. Similar to 15. 3Sum, but simpler. +1. 只需要count triplet, 但是不需要save triplet, 而且还不需要handle duplicated triplets +1. 发现start, end满足条件时候,(end - start)就是所有 sum target, 那么就end-- +1. 两层循环, O(n2) + + + +--- + +**79. [977. Squares of a Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/977.%20Squares%20of%20a%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +#### Two Pointers +- negative index i, positive index j + + + +--- + +**80. [31. Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/31.%20Next%20Permutation.java)** Level: Medium Tags: [Array, Permutation] + + +#### Permutation Behavior +- Great write up: https://leetcode.com/problems/next-permutation/solution/ +- next lexicographically permutation: `smallest` but `larger than curr` permutation: + - find first low point from right [low] + - find the slight larger [high] to swap with [low] + - make sure right side of low is eventually the smallest +- Analyze the use cases, to find next low permutation, 2 major steps: + - 1) Find `first low/drop candidate` from right + - 2) Find `first high where nums[high] > nums[low]` from right + - 3) swap(low, high). + - By now, [low, n-1] forms a greater permutation + - but it is not the smallest, because right side [low + 1, n - 1] is descending + - 4) reverse(low + 1, n-1) to create ascending slopt on right of low (smallest next lexicographically permutation) +- Corner case: if input array is decending (1st low not found), reverse it all together O(n) +- time: O(n) visit all indexes +- space: O(1) not using additional +- Similar question: `1053. Previous Permutation With One Swap` + + + + +--- + +**81. [238. Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/238.%20Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 第一轮:PreProduct (跟preSum的感觉有点像) + - PreProduct[i] stores product from num[0] -> num[i-1] (skipping current num[i]) + - init preProduct[i] = 1, as base for product + - 错过一位操作: always `preProduct[i] *= carry;` and `carry *= nums[i]` +- 第二轮: 从右边乘起, 每次在index i, 收到的carry都是 `nums[i+1] *....* nums[end]` + - 第一轮的结果 * 第二轮的结果, 刚好在index i 缺少掉 nums[i]. 如题所愿. +- Time: O(n) + + + +--- + +**82. [62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +#### DP, 加法原理 +- 计数DP: 2 ways to reach (i,j): dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + - non-overlapping: `dp[i - 1][j]`, `dp[i][j - 1]` + - covers the only 2 possible way to reach (i,j) +- initialization: dp[i][0] = 1, dp[0][i] = 1 + - Of course, row i = 0, or col j = 0, there is only 1 way to reach +- time O(mn), space O(mn) + +##### 滚动数组 Rolling Array +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + +#### DFS + Memoization +- move from (0,0) towards (m, n) +- use Map as memoization technique + + + +--- + +**83. [15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +#### sort array, for loop + two pointer +- 处理duplicate wthin triplets: + - 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. + - 如果是triplet内有重复, 用完start point, 移动到结尾. +- Note: + - 1. 找 value triplets, 多个结果。注意,并非找index。 + - 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + - 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 +- 时间 O(n^2), 两个nested loop + +#### For loop + 2Sum +- HashMap 2Sum. Remember to handle duplicates + - 1. For loop 挑个数字A + - 2. 2Sum 出一堆2个数字的结果 + - 3. Cross match 步骤1里面的A + + + +--- + +**84. [55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%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 +- 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) +- space: O(1) + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (j + A[j] >= i), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- time: O(n^2) +- space: O(n) + + + + +--- + +**85. [189. Rotate Array.java](https://github.com/awangdev/LintCode/blob/master/Java/189.%20Rotate%20Array.java)** Level: Easy Tags: [Array, Rotation] + +#### Rotate array in place +- rotate all +- rotate 2 sides: < k or >= + + +#### Rotate by buffer the k array + + + +--- + +**86. [119. Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/119.%20Pascal's%20Triangle%20II.java)** Level: Easy Tags: [Array, Basic Implementation] + + +简单处理 list. code is very similar to Pascal triangle I. + +- 注意 `list = Arrays.asList(x, y, z ...)` 给fixed-size list, 不能直接 list.add(). +- Use `new ArrayList<>(Arrays.asList(...))` to wrap it up. + + + + +--- + +**87. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + +**88. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**89. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**90. [747. Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/747.%20Largest%20Number%20At%20Least%20Twice%20of%20Others.java)** Level: Easy Tags: [Array] + + +多种简单操作: +- O(n) solution: 找最大值, 和第二大的值, 看是否符合题意, 就行了. +- O(2n) 最简单方法: 可以loop 两遍: 找最值; 作比较. +- O(2n) 举反例: 有一个不满足, 就够反对这个'at least twice of alllll others'. + + + +--- + +**91. [88. Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- Also most identical to `33. Search in Rotated Sorted Array`: + - find where nums[mid] lands by comparing to nums[start]. i.e., if nums[mid] < nums[start], on right half of the array + - when `nums[mid] == nums[start]`: duplicate. Shift by start++ +- the worst case of `nums[mid] == nums[start]` willl cause O(n), +- but if duplicate is not entire array, should be O(logn) + + + +--- + +**92. [561. Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/561.%20Array%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发现规律: 升序排列会让 `高位的min(pair)` 最大化, 于是`一言不合先排列` +- 找到排列取单数位的规律,再考虑负数和正数的相同规律,即可找到排列求解的方法。 +- sort, O(nlogn) + + + + +--- + +**93. [39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + + + +--- + +**94. [1040. Moving Stones Until Consecutive II.java](https://github.com/awangdev/LintCode/blob/master/Java/1040.%20Moving%20Stones%20Until%20Consecutive%20II.java)** Level: Medium Tags: [Array, Sliding Window] + + + +#### Analyze to understand +- Make sure to sort array: we need to use the actual number range `A[j] - A[i]`, which requires the array to be sorted +- we want to form a new array where A[n-1] - A[0] + 1 == n; order does not matter but all slots need to be filled consecutivly +- max moves: https://leetcode.com/problems/moving-stones-until-consecutive-ii/discuss/289357/c%2B%2B-with-picture + - A interval will be automatically dropped between A[0] and A[1], if moving A[0] first + - Same, a interval between A[n-2] and A[n-1] will be dropped when moving A[n-1] first + - so largest possible move = firstItem + remaining range size - n items = 1 + (A[n-1] - A[1] + 1) - n = A[n-1] - A[1] -n + 2 + - or A[n-2] - A[0] - n + 2 +- min moves: `Sliding Window` + - use slinding window to assume a right pointer, to make sure A[right] - A[left] + 1 < n; otherwise, move left++ + - check # of included stones + - calculate remaining, which is remaining moves +- Handle min move edge case: + - Consecutive Array up to right = n - 1; need 2 moves to finish + + + +--- + +**95. [88. Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素: 从尾部,是大数字优先排末尾的. +- Deal with remaining: + - When A values are used up, put remian of B into it + - When B values are finished, there is nothing todo. The remain of A is already in place. + + + +--- + +**96. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**97. [243. Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/243.%20Shortest%20Word%20Distance.java)** Level: Easy Tags: [Array, Two Pointers] + + + +#### Two Pointers +- Use 2 pointers to record **most recent** pos of word1 and word2 + - move pointer i [0 ~ n) and keep refreshing pos1 and pos2 + - both pos1 and pos2 will be as adjacent as possible since they both moving towards same direction +- keep recalculating best distance when either word is matched +- 而同一时间,只需要计算一个最近的curr distance: greedy不断变更A/B index, 做比较 + + + + +--- + +**98. [414. Third Maximum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/414.%20Third%20Maximum%20Number.java)** Level: Easy Tags: [Array, PriorityQueue] + +#### pq +- 注意special case: `when less than 3 items, return maximum` +- PQ是natural order: remain peek() will be the 3rd maximum + + + + +--- + +**99. [1267. Count Servers that Communicate.java](https://github.com/awangdev/LintCode/blob/master/Java/1267.%20Count%20Servers%20that%20Communicate.java)** Level: Medium Tags: [Array, Graph] + + +#### two axis array, cross-reference +- analyze problem, and realize we want to eliminate `isolated servers` +- count row[], count col[] +- cross-reference row[] and col[]: `row[i]==1 & col[j]==1` indicates a isolated server + +### DFS brutle +- Unfortunately, this problems checks unconnected items, so dfs needs to brutlely check entire row or column +- Only add if `vertical + horizontal count` > 1 +- time: O(mn) * O(m + n) + + + +--- + +**100. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**101. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**102. [380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + + +#### Hash Table, Swap when removing +- Map: `map, Lis: tracks `index->value` +- list maintain 用来 insert/remove/random operations. +- Remove: swap input valueIndex & tialIndex = list.size() -1. + - list.remove(object) 应该是要O(logn) 做一个search的. + - list.remove(list.size() - 1) is cheapter + + + +--- + +**103. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**104. [219. Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/219.%20Contains%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 +- Time O(nm), m = # of duplicates. 太慢 +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k + + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**105. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + +**106. [217. Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/217.%20Contains%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 + + + +--- + +**107. [64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%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(N) + - 1) 需要在同一个for loop里面完成initialization, 2) reuse dp[i][j] + - Reason: dp[i % 2][j] 在被计算出来的时候, 马上在下一轮会被用. 被覆盖前不用,就白算 + - Option3 below initialize dp outside of loop: 看起来固然简单, 但是不方便空间优化 + +#### DFS (top-down) Thinking process +- Enumerate the possible 2 paths: go right, go down +- sub problem: dfs(i+1,j), dfs(i,j+1); pick the min of the two +- memoization: after the path from a point (i,j) to end is computed, record memo[i][j] to avoid repatative calculation +- time: O(mn), only visite and record memo[i][j] once. +- space: O(mn) extrem case of m=100000, n = 2; the stack height is O(mn) + + +--- + +**108. [229. Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/229.%20Majority%20Element%20II.java)** Level: Medium Tags: [Array, Moore Voting] + + +#### Two counters, Moore Voting +1. Moore voting: vote可以加减, 一旦为零, 换下一个candidate, 之前抵消掉的算作清零. +1. 一个array里面, 最多也只有2个数字 出现次数大于2次, 所以用A/B表示. +1. count overall apperance at the end for the two items: countA, countB +1. save to result as valA, valB +1. 有点 moore voting的意思: + - 当count == 0的时候, reset + - 两个candidate A/B都不等, 那么countA--, countB-- +1. 最终重新计数, 然后比较出结局. +1. 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +#### Sort + count +- O(nlogN) + + + +--- + +**109. [121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### min[n] +- 每天都就交易价格,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 dont need to buyin at 5, 3, 6, 4 later on; + - only need to sell on higher prices. + + + +--- + +**110. [1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)** Level: Medium Tags: [Array, Hash Table, TreeMap] + + + +#### Hash Table, TreeMap; atomic save +- store efficiently: use List>. only preserve changed itemd +- if no match, find last modifed item based on snapId, use TreeMap.floorEntry + - map.floorEntry(id) return the item.key lower or equal to id +- Utilize a `buffer: Map` and perform atomic save + + + +--- + +**111. [605. Can Place Flowers.java](https://github.com/awangdev/LintCode/blob/master/Java/605.%20Can%20Place%20Flowers.java)** Level: Easy Tags: [Array, Greedy] + + +#### Array +- just check flowerbed[i-1] & flowerbed[i+1] and count + + + +--- + +**112. [1. Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1.%20Two%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. + + + + +--- + +**113. [118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)** Level: Easy Tags: [Array, Basic Implementation, List] + + + + +--- + +**114. [283. Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/283.%20Move%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 + + + +--- + +**115. [63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%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] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + + + +--- + +**116. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**117. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + +**118. [724. Find Pivot Index.java](https://github.com/awangdev/LintCode/blob/master/Java/724.%20Find%20Pivot%20Index.java)** Level: Easy Tags: [Array, PreSum] + + +#### PreSum +- want to find `nums[i - 1] == nums[n - 1] - nums[i]`, given: + - preSum[i], sum from [0, i] inclusive + - preSum[j] - preSum[i] = [i+1, j] inclusive +- O(n) to build preSum +- O(n) to find pivot + + + +--- + +**119. [33. Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/33.%20Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 + - 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` + - 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- Binary search template: + - 1) `start + 1 < end` (adjacent indexes) + - 2) start/end = mid, + - 3) compare start and end individually + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**120. [1170. Compare Strings by Frequency of the Smallest Character.java](https://github.com/awangdev/LintCode/blob/master/Java/1170.%20Compare%20Strings%20by%20Frequency%20of%20the%20Smallest%20Character.java)** Level: Easy Tags: [Array, String] + + +#### Method1: letter frequency map, kinda bucket sort +- Goal: find word count that fits into `f(queries[i]) < f(W)` +- What if: we can store the f(W) as preSum, then goal: `rst[i] = preSum[end] - preSum[queryWordCount]` + - count(W) and store in count[i] + - calc preSum + - processs queries array +- kinda bucket sort: + - 1) we know the boundary of the word length, so we can create `bucket` + - 2) `function count(w)` can produce a value that sort a word into a specific bucket slot + - extend: the bucket can store keys that links back to the word (if there are follow up questions) +- time: O(m + n) +- space: O(m + n) + +#### Method2: No brain solution, basic impl based on the desc, w/o search. +- time: + - O(nm) to count all words, O(nlogn) to sort the wordCount + - O(nm) to to count all queries + - O(n^2) to perform the match +- space: O(n) + + + +--- + +**121. [34. Find First and Last Position of Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/34.%20Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- need search left bound & right bound. +- use input parameter `direction` to binary search function +- Option0: simplification, inspired by `278. First Bad Version - Method1: Check is-NOT-BadVersion` + - 1) if found match, but NOT sure it is desired boundary, just leave it and keep going + - 2) check the final results after `binary search while loop` completes + - WHY? code is easier to read in this way. + + + +--- + +**122. [689. Maximum Sum of 3 Non-Overlapping Subarrays.java](https://github.com/awangdev/LintCode/blob/master/Java/689.%20Maximum%20Sum%20of%203%20Non-Overlapping%20Subarrays.java)** Level: Hard Tags: [Array, DP] + + +#### DP, Divide and conquer +- split into 3 parts [0, i -1], [i, i + k -1]. [i + k, n - 1] +- NOTE: be very careful about index handling: + - `presum[i + 1] - presum[0]` gives inclusive range of `[0, i]` +- Use DP to record the starting position of max sum, +- inspired by: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C%2B%2BJava-DP-with-explanation-O(n) + - 1) calculate preSum with range [0, n] + - 2) calculate leftMaxIndex[], rightMaxIndex[] + - 3) test middle range to find max solution +- Note: the test range for 1, 2, 3 always start with assumption that k has been consumed from one side +- Note: When need to record at max/min value change, we can check/assign it manually (rather than use a object to carry & sort) + + + + +--- + +**123. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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" + + + +--- + +**124. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**125. [611. Valid Triangle Number.java](https://github.com/awangdev/LintCode/blob/master/Java/611.%20Valid%20Triangle%20Number.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Method1: Fix max and backward counting +- Sort nums: O(nlogn) +- Set max value fixed on right side at k + - set 2nd value from right index j + - set last value at min index i + - if `nums[i] + nums[j] > nums[k]`: with fixed j, i can pick from [i, j-1] combinations + - then j--, to pick another j candidate + - maintain a window [i,j]; if invalid, move i++ +- time: O(n^2) +- Note: very similar to 3-sum, fixing 1 index and use 2 pointers to move window + +#### Method2: Fix min and forward counting +- Sort nums: O(nlogn) +- Set min value at i + - set 2nd value at j=i+1; and 3rd value at k=i+2 + - find max of k that fits into triangle + - count all possible k candidates from [j+1, k] + - then move j to a new candidate +- O(n^2) + + + +--- + + + + + + + +## 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. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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" + + + +--- + + + + + + + +## Backpack (1) +**0. [416. Partition Equal Subset Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/416.%20Partition%20Equal%20Subset%20Sum.java)** Level: Medium Tags: [Backpack, DP] + +#### Backpack DP +- the problem turns into: can we find a subset of items that sum up to target sum? +- create `boolean dp[j]` to represent if we can sum up to j, where j = sum value + - want to try out all items in num, + +#### DFS +- use dfs to find a subset of items that sum up to target sum? + + + +--- + + + + + + + +## Edge Case (2) +**0. [686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)** Level: Easy Tags: [Basic Implementation, Edge Case, String] + +Track: 纸上分析edge case. +Validation helps speed it up. + + + +--- + +**1. [41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)** Level: Hard Tags: [Analysis, Array, Edge Case] + + +给一串无序数字, 有负数: 找这个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 +1. 如果nums==null, 其实missing positive integer 自然而然是 1 +1. 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) + - 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +1. 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + + + + + + + +## Memoization (15) +**0. [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);` + + + +--- + +**1. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**2. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**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. [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. [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. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**8. [509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.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. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + +**10. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**11. [70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%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]往往是有特殊状态的. 这里, dp[0]: 1种方式可以原地不动 + - dp[1]=1, 1种方式到达index=1, + - 之后的`dp[2] = dp[0]+dp[1]`: 就是dp[0]的走法 or dp[1]的走法 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**12. [170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)** Level: Easy Tags: [Design, Hash Table, Memoization] + + +#### Hash Table, Memo +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +- time: O(n), loop over all elements in map +- space: O(n), store all elements in map & memoization set + + + +--- + +**13. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + + +--- + +**14. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + + + + + + + +## Two Stacks (1) +**0. [145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Method1: DFS, Recursive +- trivial, 先加left recursively, 再加right recursively, 然后组成头部. +- Option1 w/o helper; option2 with dfs helper. + +#### Method2, Iterative, Stack +- Option1: reversely add to list +- 双stack的思想, 需要在图纸上画一画 + - 原本需要的顺序是: 先leftChild, rightChild, currNode. + - 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild + - 这样出来的结果是reverse的, 那么翻转一下就可以了. +- reverse add: `list.add(0, x)`; +- 利用stack的特点 + - 每次加element进stack的时候, 想要在 bottom/后process的, 先加 + - 想要下一轮立刻process的, 最后push进stack. +- Option2: regular sequence add to stack: add curr, right, left + - Use set to contain the processed children + - only process curr if its children is processed + + + + +--- + + + + + + + +## Tree DP (1) +**0. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + + + + 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/Adjacency Matrix.md b/review/Adjacency Matrix.md new file mode 100644 index 0000000..97a85a8 --- /dev/null +++ b/review/Adjacency Matrix.md @@ -0,0 +1,32 @@ + + + +## Adjacency Matrix (1) +**0. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + diff --git a/review/Analysis.md b/review/Analysis.md new file mode 100644 index 0000000..a71e002 --- /dev/null +++ b/review/Analysis.md @@ -0,0 +1,27 @@ + + + +## Analysis (1) +**0. [41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)** Level: Hard Tags: [Analysis, Array, Edge Case] + + +给一串无序数字, 有负数: 找这个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 +1. 如果nums==null, 其实missing positive integer 自然而然是 1 +1. 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) + - 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +1. 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + diff --git a/review/Array.md b/review/Array.md new file mode 100644 index 0000000..cef69ae --- /dev/null +++ b/review/Array.md @@ -0,0 +1,2633 @@ + + + +## Array (126) +**0. [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 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 + + + +--- + +**1. [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 + + + +--- + +**2. [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) 太慢, 不合题意 + + + +--- + +**3. [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);` + + + +--- + +**4. [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 + + + +--- + +**5. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**6. [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. + + +--- + +**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. [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) + + + +--- + +**9. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**10. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + + + +--- + +**11. [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)) + + + + +--- + +**12. [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 + + + + +--- + +**13. [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] + + + +--- + +**14. [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] + + +给一串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 + + + +--- + +**15. [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 + + + +--- + +**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. [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作比较;求的所有情况的最大值嘛。 + + + +--- + +**18. [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, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**19. [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 是理所当然的 + + + +--- + +**20. [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 + + + +--- + +**21. [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的用法: |, & + + + + +--- + +**22. [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. + + + +--- + +**23. [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 + + + +--- + +**24. [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来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**25. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**26. [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. + + + +--- + +**27. [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) + + + +--- + +**28. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**29. [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的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**30. [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[] + + + +--- + +**31. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + +--- + +**32. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + +--- + +**33. [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` + + + +--- + +**34. [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 + + + +--- + +**35. [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) + + + +--- + +**36. [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. + + + +--- + +**37. [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. + + + + +--- + +**38. [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), 就不写了 + + + +--- + +**39. [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; + + + + +--- + +**40. [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 + + + +--- + +**41. [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)))` + + + +--- + +**42. [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] + + +跟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] + + + +--- + +**43. [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]) + + + +--- + +**44. [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 + + + +--- + +**45. [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 + + + +--- + +**46. [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走过的地方 + + + +--- + +**47. [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]` + + + +--- + +**48. [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 原题有一点点不一样. + + + + +--- + +**49. [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. + + + +--- + +**50. [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? + + + + +--- + +**51. [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 + + + +--- + +**52. [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)的巧妙用法. + + + + +--- + +**53. [[lint]. Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array, Lint] + + + + +--- + +**54. [[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)** Level: Medium Tags: [Array, Hash Table, Lint] + + +把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. + + + + +--- + +**55. [[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)** Level: Medium Tags: [Array, Lint, Two Pointers] + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**56. [[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)** Level: Easy Tags: [Array, Lint, 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) + + + + +--- + +**57. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**58. [[lint]. Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array, Lint] + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**59. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**60. [42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. + +#### Method1: Max wall from both sides +- Array: Left Max Wall vs Right Max Wall. +- 对于每个index而言, vertically 能存放的最大水柱, 就是靠 左 右 最高墙决定的: + - min(leftHighestWall, rightHighestWall) - currHeight. +- time: O(n) +- space: O(n) + +#### Method2: Two Pointers +- Optimization from Method1: two pointer, 还是找左边最高和右边最高. O(1) space. +- 利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +- always limited by the shorter wall: 左边按照maxLeft来计算, 右边按照maxRight来计算. +- time: O(n) +- space: O(1) + +#### Method3: 2 Pointers, start from 2 sides +- 1. 找中间最高bar的index +- 2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条 +- 3. 每次还要减去block自身的height +- time: O(n) +- space: O(1) + +#### Method4: Stack +- 主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +- 最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +- 用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. +- time: O(n) +- space: O(n) + + + + +--- + +**61. [448. Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/448.%20Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)** Level: Easy Tags: [Array, Bucket Sort] + + +#### Method1: Bucket Sort concept, set val to its correct position +- Given: values are [1,n], so val can represent index. Therefore, set val to its correct position +- 小心handle i: + - value是 1-based + - 每次换位, 需要`i--`, 重新省察`nums[i]` + +#### Method2: 做标记 (negative number, or super large number) +- Option1: use negative number to mark visited: + - 很巧妙地运用了标记的方法, 标记成负数,证明visit过。 + - Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! +- Option2: use large number (larger than n) + - 跟方法2类似,也是做标记,这一次是加上一个大于n的数(因为题目给了n的border),最后check一下就好。为不超Integer.MAX_VALUE, 每次加n前,取余数。 + - 做标记的方法固然快,但是相对来说比较hacky,在常规的代码中,估计不会用到. + + + + +--- + +**62. [849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array, Basic Implementation, Two Pointers] + + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, Two Pointers: 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` + + + + +--- + +**63. [766. Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/766.%20Toeplitz%20Matrix.java)** Level: Easy Tags: [Array] + + +#### Check diagonal +- 似乎没什么算法特点, 就是array基本运算, 然后分割成一个helper function去做重复计算, 剪短代码. +- 注意check MxN 的分界线. + +#### Simpler Solution +- the goal is to check [i][j] == [i+1][j+1] for every i and j. + + + +--- + +**64. [1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)** Level: Medium Tags: [Array, Greedy, Permutation] + + +#### Analyze Permutation behavior +- concept similar to `31. Next Permutation` +- 1) first pass: find the one that is in incorrect order +- 2) second pass: find the right spot to swap + + + +--- + +**65. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**66. [665. Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/665.%20Non-decreasing%20Array.java)** Level: Easy Tags: [Array] + + +- 比较升序的时候, 必须要估计到 `i-1, i, i+1` 三个数位. +- 写出来`i-1, i, i+1`之间的关系, 然后做合理的fix. + 1. reduce nums[i+1] to fix + 1. raise nums[i+1] to fix +- 需要真的fix数组, 因为loop through做比较时会用到fix后的数字. + + + + +--- + +**67. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**68. [80.Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/80.Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Basic +- 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其实就足够. + +#### Follow up: k duplicates, Two Pointers +- when index i and i-1 are diff, use count=1 to start +- in while loop, keep count++ until count==k +- reset when next diff comes in + + + +--- + +**69. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**70. [1007. Minimum Domino Rotations For Equal Row.java](https://github.com/awangdev/LintCode/blob/master/Java/1007.%20Minimum%20Domino%20Rotations%20For%20Equal%20Row.java)** Level: Medium Tags: [Array, Greedy] + + + +#### Method1: Count all occurrance, and count on overlap indexes +- when there is a value that can cover entire row of size n + - it must be: `n = countA[i] + countB[i] - overlap[i]` +- Code easy to write and read +- time: O(n) +- space: O(1) + +#### Method2: Negative count +- Observation: if A[0] works, no need to check B[0]. +- Because if both A[0] and B[0] exist in all dominoes, + - when you swap A[0] in a whole row, + - you will swap B[0] in a whole at the same time. + - The result of trying A[0] and B[0] will be the same. +- time: O(n) +- space: O(1) + +#### Method3: positive count Match +- there should exist 1 numbers, that can appear in (A[i], B[i]). +- failure case: there exist at least 1 index, that does not have the common number +- maximum case: there can be 2 numbers, that both will make it work. +- findCommon2, and count them: + - set.add(A[0], A[B]), + - if any new one does not exist in set, remove it from set + - if set is empty() , return -1 +- use the 2 numbers from set to do a sweep and count in A, O(n), return the less appearance one. +- time: O(n) +- space: O(1) + + + +--- + +**71. [485. Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/485.%20Max%20Consecutive%20Ones.java)** Level: Easy Tags: [Array, Basic Implementation] + + +- preserve max +- 清零count + + + +--- + +**72. [896. Monotonic Array.java](https://github.com/awangdev/LintCode/blob/master/Java/896.%20Monotonic%20Array.java)** Level: Easy Tags: [Array] + +basic implementation + + + +--- + +**73. [26.Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/26.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 + + + +--- + +**74. [41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)** Level: Hard Tags: [Analysis, Array, Edge Case] + + +给一串无序数字, 有负数: 找这个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 +1. 如果nums==null, 其实missing positive integer 自然而然是 1 +1. 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) + - 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +1. 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + +**75. [717. 1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/717.%201-bit%20and%202-bit%20Characters.java)** Level: Easy Tags: [Array] + +理解题目: +1. single-bit always starts with '0', two-bits always start with '1'. +1. Therefore there is ONLY 1 way to reach end. + +#### 方法1 +Greedy. +从第一个bit开始: 如果 % 2 == 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. + + + +--- + +**76. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**77. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**78. [259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +1. Similar to 15. 3Sum, but simpler. +1. 只需要count triplet, 但是不需要save triplet, 而且还不需要handle duplicated triplets +1. 发现start, end满足条件时候,(end - start)就是所有 sum target, 那么就end-- +1. 两层循环, O(n2) + + + +--- + +**79. [977. Squares of a Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/977.%20Squares%20of%20a%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +#### Two Pointers +- negative index i, positive index j + + + +--- + +**80. [31. Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/31.%20Next%20Permutation.java)** Level: Medium Tags: [Array, Permutation] + + +#### Permutation Behavior +- Great write up: https://leetcode.com/problems/next-permutation/solution/ +- next lexicographically permutation: `smallest` but `larger than curr` permutation: + - find first low point from right [low] + - find the slight larger [high] to swap with [low] + - make sure right side of low is eventually the smallest +- Analyze the use cases, to find next low permutation, 2 major steps: + - 1) Find `first low/drop candidate` from right + - 2) Find `first high where nums[high] > nums[low]` from right + - 3) swap(low, high). + - By now, [low, n-1] forms a greater permutation + - but it is not the smallest, because right side [low + 1, n - 1] is descending + - 4) reverse(low + 1, n-1) to create ascending slopt on right of low (smallest next lexicographically permutation) +- Corner case: if input array is decending (1st low not found), reverse it all together O(n) +- time: O(n) visit all indexes +- space: O(1) not using additional +- Similar question: `1053. Previous Permutation With One Swap` + + + + +--- + +**81. [238. Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/238.%20Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 第一轮:PreProduct (跟preSum的感觉有点像) + - PreProduct[i] stores product from num[0] -> num[i-1] (skipping current num[i]) + - init preProduct[i] = 1, as base for product + - 错过一位操作: always `preProduct[i] *= carry;` and `carry *= nums[i]` +- 第二轮: 从右边乘起, 每次在index i, 收到的carry都是 `nums[i+1] *....* nums[end]` + - 第一轮的结果 * 第二轮的结果, 刚好在index i 缺少掉 nums[i]. 如题所愿. +- Time: O(n) + + + +--- + +**82. [62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +#### DP, 加法原理 +- 计数DP: 2 ways to reach (i,j): dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + - non-overlapping: `dp[i - 1][j]`, `dp[i][j - 1]` + - covers the only 2 possible way to reach (i,j) +- initialization: dp[i][0] = 1, dp[0][i] = 1 + - Of course, row i = 0, or col j = 0, there is only 1 way to reach +- time O(mn), space O(mn) + +##### 滚动数组 Rolling Array +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + +#### DFS + Memoization +- move from (0,0) towards (m, n) +- use Map as memoization technique + + + +--- + +**83. [15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +#### sort array, for loop + two pointer +- 处理duplicate wthin triplets: + - 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. + - 如果是triplet内有重复, 用完start point, 移动到结尾. +- Note: + - 1. 找 value triplets, 多个结果。注意,并非找index。 + - 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + - 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 +- 时间 O(n^2), 两个nested loop + +#### For loop + 2Sum +- HashMap 2Sum. Remember to handle duplicates + - 1. For loop 挑个数字A + - 2. 2Sum 出一堆2个数字的结果 + - 3. Cross match 步骤1里面的A + + + +--- + +**84. [55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%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 +- 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) +- space: O(1) + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (j + A[j] >= i), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- time: O(n^2) +- space: O(n) + + + + +--- + +**85. [189. Rotate Array.java](https://github.com/awangdev/LintCode/blob/master/Java/189.%20Rotate%20Array.java)** Level: Easy Tags: [Array, Rotation] + +#### Rotate array in place +- rotate all +- rotate 2 sides: < k or >= + + +#### Rotate by buffer the k array + + + +--- + +**86. [119. Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/119.%20Pascal's%20Triangle%20II.java)** Level: Easy Tags: [Array, Basic Implementation] + + +简单处理 list. code is very similar to Pascal triangle I. + +- 注意 `list = Arrays.asList(x, y, z ...)` 给fixed-size list, 不能直接 list.add(). +- Use `new ArrayList<>(Arrays.asList(...))` to wrap it up. + + + + +--- + +**87. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + +**88. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**89. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**90. [747. Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/747.%20Largest%20Number%20At%20Least%20Twice%20of%20Others.java)** Level: Easy Tags: [Array] + + +多种简单操作: +- O(n) solution: 找最大值, 和第二大的值, 看是否符合题意, 就行了. +- O(2n) 最简单方法: 可以loop 两遍: 找最值; 作比较. +- O(2n) 举反例: 有一个不满足, 就够反对这个'at least twice of alllll others'. + + + +--- + +**91. [88. Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- Also most identical to `33. Search in Rotated Sorted Array`: + - find where nums[mid] lands by comparing to nums[start]. i.e., if nums[mid] < nums[start], on right half of the array + - when `nums[mid] == nums[start]`: duplicate. Shift by start++ +- the worst case of `nums[mid] == nums[start]` willl cause O(n), +- but if duplicate is not entire array, should be O(logn) + + + +--- + +**92. [561. Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/561.%20Array%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发现规律: 升序排列会让 `高位的min(pair)` 最大化, 于是`一言不合先排列` +- 找到排列取单数位的规律,再考虑负数和正数的相同规律,即可找到排列求解的方法。 +- sort, O(nlogn) + + + + +--- + +**93. [39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + + + +--- + +**94. [1040. Moving Stones Until Consecutive II.java](https://github.com/awangdev/LintCode/blob/master/Java/1040.%20Moving%20Stones%20Until%20Consecutive%20II.java)** Level: Medium Tags: [Array, Sliding Window] + + + +#### Analyze to understand +- Make sure to sort array: we need to use the actual number range `A[j] - A[i]`, which requires the array to be sorted +- we want to form a new array where A[n-1] - A[0] + 1 == n; order does not matter but all slots need to be filled consecutivly +- max moves: https://leetcode.com/problems/moving-stones-until-consecutive-ii/discuss/289357/c%2B%2B-with-picture + - A interval will be automatically dropped between A[0] and A[1], if moving A[0] first + - Same, a interval between A[n-2] and A[n-1] will be dropped when moving A[n-1] first + - so largest possible move = firstItem + remaining range size - n items = 1 + (A[n-1] - A[1] + 1) - n = A[n-1] - A[1] -n + 2 + - or A[n-2] - A[0] - n + 2 +- min moves: `Sliding Window` + - use slinding window to assume a right pointer, to make sure A[right] - A[left] + 1 < n; otherwise, move left++ + - check # of included stones + - calculate remaining, which is remaining moves +- Handle min move edge case: + - Consecutive Array up to right = n - 1; need 2 moves to finish + + + +--- + +**95. [88. Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素: 从尾部,是大数字优先排末尾的. +- Deal with remaining: + - When A values are used up, put remian of B into it + - When B values are finished, there is nothing todo. The remain of A is already in place. + + + +--- + +**96. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**97. [243. Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/243.%20Shortest%20Word%20Distance.java)** Level: Easy Tags: [Array, Two Pointers] + + + +#### Two Pointers +- Use 2 pointers to record **most recent** pos of word1 and word2 + - move pointer i [0 ~ n) and keep refreshing pos1 and pos2 + - both pos1 and pos2 will be as adjacent as possible since they both moving towards same direction +- keep recalculating best distance when either word is matched +- 而同一时间,只需要计算一个最近的curr distance: greedy不断变更A/B index, 做比较 + + + + +--- + +**98. [414. Third Maximum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/414.%20Third%20Maximum%20Number.java)** Level: Easy Tags: [Array, PriorityQueue] + +#### pq +- 注意special case: `when less than 3 items, return maximum` +- PQ是natural order: remain peek() will be the 3rd maximum + + + + +--- + +**99. [1267. Count Servers that Communicate.java](https://github.com/awangdev/LintCode/blob/master/Java/1267.%20Count%20Servers%20that%20Communicate.java)** Level: Medium Tags: [Array, Graph] + + +#### two axis array, cross-reference +- analyze problem, and realize we want to eliminate `isolated servers` +- count row[], count col[] +- cross-reference row[] and col[]: `row[i]==1 & col[j]==1` indicates a isolated server + +### DFS brutle +- Unfortunately, this problems checks unconnected items, so dfs needs to brutlely check entire row or column +- Only add if `vertical + horizontal count` > 1 +- time: O(mn) * O(m + n) + + + +--- + +**100. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**101. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**102. [380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + + +#### Hash Table, Swap when removing +- Map: `map, Lis: tracks `index->value` +- list maintain 用来 insert/remove/random operations. +- Remove: swap input valueIndex & tialIndex = list.size() -1. + - list.remove(object) 应该是要O(logn) 做一个search的. + - list.remove(list.size() - 1) is cheapter + + + +--- + +**103. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**104. [219. Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/219.%20Contains%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 +- Time O(nm), m = # of duplicates. 太慢 +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k + + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**105. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + +**106. [217. Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/217.%20Contains%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 + + + +--- + +**107. [64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%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(N) + - 1) 需要在同一个for loop里面完成initialization, 2) reuse dp[i][j] + - Reason: dp[i % 2][j] 在被计算出来的时候, 马上在下一轮会被用. 被覆盖前不用,就白算 + - Option3 below initialize dp outside of loop: 看起来固然简单, 但是不方便空间优化 + +#### DFS (top-down) Thinking process +- Enumerate the possible 2 paths: go right, go down +- sub problem: dfs(i+1,j), dfs(i,j+1); pick the min of the two +- memoization: after the path from a point (i,j) to end is computed, record memo[i][j] to avoid repatative calculation +- time: O(mn), only visite and record memo[i][j] once. +- space: O(mn) extrem case of m=100000, n = 2; the stack height is O(mn) + + +--- + +**108. [229. Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/229.%20Majority%20Element%20II.java)** Level: Medium Tags: [Array, Moore Voting] + + +#### Two counters, Moore Voting +1. Moore voting: vote可以加减, 一旦为零, 换下一个candidate, 之前抵消掉的算作清零. +1. 一个array里面, 最多也只有2个数字 出现次数大于2次, 所以用A/B表示. +1. count overall apperance at the end for the two items: countA, countB +1. save to result as valA, valB +1. 有点 moore voting的意思: + - 当count == 0的时候, reset + - 两个candidate A/B都不等, 那么countA--, countB-- +1. 最终重新计数, 然后比较出结局. +1. 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +#### Sort + count +- O(nlogN) + + + +--- + +**109. [121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### min[n] +- 每天都就交易价格,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 dont need to buyin at 5, 3, 6, 4 later on; + - only need to sell on higher prices. + + + +--- + +**110. [1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)** Level: Medium Tags: [Array, Hash Table, TreeMap] + + + +#### Hash Table, TreeMap; atomic save +- store efficiently: use List>. only preserve changed itemd +- if no match, find last modifed item based on snapId, use TreeMap.floorEntry + - map.floorEntry(id) return the item.key lower or equal to id +- Utilize a `buffer: Map` and perform atomic save + + + +--- + +**111. [605. Can Place Flowers.java](https://github.com/awangdev/LintCode/blob/master/Java/605.%20Can%20Place%20Flowers.java)** Level: Easy Tags: [Array, Greedy] + + +#### Array +- just check flowerbed[i-1] & flowerbed[i+1] and count + + + +--- + +**112. [1. Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1.%20Two%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. + + + + +--- + +**113. [118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)** Level: Easy Tags: [Array, Basic Implementation, List] + + + + +--- + +**114. [283. Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/283.%20Move%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 + + + +--- + +**115. [63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%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] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + + + +--- + +**116. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**117. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + +**118. [724. Find Pivot Index.java](https://github.com/awangdev/LintCode/blob/master/Java/724.%20Find%20Pivot%20Index.java)** Level: Easy Tags: [Array, PreSum] + + +#### PreSum +- want to find `nums[i - 1] == nums[n - 1] - nums[i]`, given: + - preSum[i], sum from [0, i] inclusive + - preSum[j] - preSum[i] = [i+1, j] inclusive +- O(n) to build preSum +- O(n) to find pivot + + + +--- + +**119. [33. Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/33.%20Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 + - 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` + - 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- Binary search template: + - 1) `start + 1 < end` (adjacent indexes) + - 2) start/end = mid, + - 3) compare start and end individually + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**120. [1170. Compare Strings by Frequency of the Smallest Character.java](https://github.com/awangdev/LintCode/blob/master/Java/1170.%20Compare%20Strings%20by%20Frequency%20of%20the%20Smallest%20Character.java)** Level: Easy Tags: [Array, String] + + +#### Method1: letter frequency map, kinda bucket sort +- Goal: find word count that fits into `f(queries[i]) < f(W)` +- What if: we can store the f(W) as preSum, then goal: `rst[i] = preSum[end] - preSum[queryWordCount]` + - count(W) and store in count[i] + - calc preSum + - processs queries array +- kinda bucket sort: + - 1) we know the boundary of the word length, so we can create `bucket` + - 2) `function count(w)` can produce a value that sort a word into a specific bucket slot + - extend: the bucket can store keys that links back to the word (if there are follow up questions) +- time: O(m + n) +- space: O(m + n) + +#### Method2: No brain solution, basic impl based on the desc, w/o search. +- time: + - O(nm) to count all words, O(nlogn) to sort the wordCount + - O(nm) to to count all queries + - O(n^2) to perform the match +- space: O(n) + + + +--- + +**121. [34. Find First and Last Position of Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/34.%20Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- need search left bound & right bound. +- use input parameter `direction` to binary search function +- Option0: simplification, inspired by `278. First Bad Version - Method1: Check is-NOT-BadVersion` + - 1) if found match, but NOT sure it is desired boundary, just leave it and keep going + - 2) check the final results after `binary search while loop` completes + - WHY? code is easier to read in this way. + + + +--- + +**122. [689. Maximum Sum of 3 Non-Overlapping Subarrays.java](https://github.com/awangdev/LintCode/blob/master/Java/689.%20Maximum%20Sum%20of%203%20Non-Overlapping%20Subarrays.java)** Level: Hard Tags: [Array, DP] + + +#### DP, Divide and conquer +- split into 3 parts [0, i -1], [i, i + k -1]. [i + k, n - 1] +- NOTE: be very careful about index handling: + - `presum[i + 1] - presum[0]` gives inclusive range of `[0, i]` +- Use DP to record the starting position of max sum, +- inspired by: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C%2B%2BJava-DP-with-explanation-O(n) + - 1) calculate preSum with range [0, n] + - 2) calculate leftMaxIndex[], rightMaxIndex[] + - 3) test middle range to find max solution +- Note: the test range for 1, 2, 3 always start with assumption that k has been consumed from one side +- Note: When need to record at max/min value change, we can check/assign it manually (rather than use a object to carry & sort) + + + + +--- + +**123. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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" + + + +--- + +**124. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**125. [611. Valid Triangle Number.java](https://github.com/awangdev/LintCode/blob/master/Java/611.%20Valid%20Triangle%20Number.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Method1: Fix max and backward counting +- Sort nums: O(nlogn) +- Set max value fixed on right side at k + - set 2nd value from right index j + - set last value at min index i + - if `nums[i] + nums[j] > nums[k]`: with fixed j, i can pick from [i, j-1] combinations + - then j--, to pick another j candidate + - maintain a window [i,j]; if invalid, move i++ +- time: O(n^2) +- Note: very similar to 3-sum, fixing 1 index and use 2 pointers to move window + +#### Method2: Fix min and forward counting +- Sort nums: O(nlogn) +- Set min value at i + - set 2nd value at j=i+1; and 3rd value at k=i+2 + - find max of k that fits into triangle + - count all possible k candidates from [j+1, k] + - then move j to a new candidate +- O(n^2) + + + +--- + diff --git a/review/BFS.md b/review/BFS.md new file mode 100644 index 0000000..c9dad53 --- /dev/null +++ b/review/BFS.md @@ -0,0 +1,1223 @@ + + + +## BFS (54) +**0. [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开头 + + + +--- + +**1. [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()` + + + +--- + +**2. [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. + + + +--- + +**3. [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了。 + + + +--- + +**4. [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. + + + +--- + +**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. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**7. [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 + + + +--- + +**8. [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完 + + + +--- + +**9. [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 + + + +--- + +**10. [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. + + + +--- + +**11. [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 + + + +--- + +**12. [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. +- 分析过, 还没有写. + + + +--- + +**13. [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 + + + +--- + +**14. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**15. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + +--- + +**16. [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; + + + + +--- + +**17. [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 + + + +--- + +**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. + + + +--- + +**19. [Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)** Level: Hard Tags: [BFS] + + + +--- + +**20. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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. [102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### Method1: BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### Method2: DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**22. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**23. [301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)** Level: Hard Tags: [BFS, DFS, DP] + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- Goal: identify invalid parentheses and remove (minimum removals) +- Step: + - Detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` + - When invalid occurs: + - chance for correction. Remove the incorrect parentheses, one at a time + - dfs on the rest of the s that has not been tested yet: start index from index i + - Core edge cases: + - Do not correct twice of the same parenthesis by checking [j-1] pos + - Make sure to attempt correction of all possible parenthesis within tested range: because it outputs all results at the same level + - return/finish once correction done +- Success case: + - a string s passed test: make sure it passes REVERSED string test! + - Core Concept: `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**. +- How does it guarantee minimum removals? + - When seeing a chance to correct, it jumps into a for loop of DFS. It `return` after the for loop. This stops additional testing + - When invalid occurs, correct it right away: minimum correction +- Complexity: + - O(nk), k being the # of recursive calls. It takes n calls to finish a full string case. + +#### BFS +- Similar to DFS, we wnat to test: 1) test input s valid, 2) remove 1 invalid parenthesis at a time, 3) process substring +- instead of testing all substrings (timeout), we want to establish rules to improve reprocess: + - Test1: skip regular char. No need to test it. + - Test2: if redundant paren, do 1 is enough. skip adjacent ones. + - Test3: if last removed extra paren is '(', the next ')' must be a valid pair. LastRemoved char: pecial handling by using a struct: `class Node {String s, int index, char lastRemoved}` +- How to end tests? When there is data in rst, stop adding to queue. + + + +--- + +**24. [111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%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 Conquer to find min depth. + - if one of child is null, return the other child depth + 1 + - Pick the min of the two child depth + 1 +- need to visit all nodes + + + + +--- + +**25. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**26. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**27. [429. N-ary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/429.%20N-ary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Tree] + + +#### BFS +- use queue to hold each level. O(n) + + + +--- + +**28. [199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%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 +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n nodes in final results + + +#### DFS +- Use Map to override the result at each level +- dfs: + - dfs(node.left) and then dfs(node.right) because we want to log right side last +- record global max depth for iteration purpose +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n stacks (and n nodes in final results) + + + + +--- + +**29. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**30. [515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS +- faster than BFS, using less space if not couting final rst: stack size, O(logn) +- time: O(n), visit all + +#### Method2: BFS with queue +- loop over queue level and record max + + + + +--- + +**31. [1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph] + + + +#### BFS +- simply calc each level sum with BFS +- top-level is processed first, since we go from top level -> deeper level + - only update result if sum is truly > global MAX. + + + +--- + +**32. [1091. Shortest Path in Binary Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/1091.%20Shortest%20Path%20in%20Binary%20Matrix.java)** Level: Medium Tags: [BFS] + + + +#### BFS +- find shortest path using queue +- time/space: O(n^2), n = grid length +- why SKIP `boolean visited[i][j]`? after a position grid[i][j] is used: + - 1) the curr path will not return to (i, j) + - 2) other route that may eventually reach (i, j) need not to be recorded, + - because the other route is already longer than the curr path + - therefore, we just simply block the visited node by `grid[x][y] = 1` + - note: block it right after it is added to the queue, so other nodes at same level will not attempt this visited node. + + + +--- + +**33. [339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS, NestedInteger] + + +给一串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. + - 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) + +#### BFS +- bfs, queue, 处理queue.size() for a level +- use a level variable to track levels +- slower since it uses extra space, worst case O(n) of all items + + + +--- + +**34. [1197. Minimum Knight Moves.java](https://github.com/awangdev/LintCode/blob/master/Java/1197.%20Minimum%20Knight%20Moves.java)** Level: Medium Tags: [BFS] + + +#### BFS +- `from starting point, find min steps to reach certain point`: think of BFS + - similar: shortest path, shortest distance +- bfs: minimum steps, enumerate the possible moves + - move closer to x or y (test 8 possible directions) + - add possible moves in queue +- use visited to cache visited coordinates +- time: O(8^n), # of BFS branches +- space: O(8^n), # of BFS branche nodes + + + +--- + +**35. [1306. Jump Game III.java](https://github.com/awangdev/LintCode/blob/master/Java/1306.%20Jump%20Game%20III.java)** Level: Medium Tags: [BFS, Graph] + + +### Method1: BFS +- Find possibility to reach certain point, we can BFS: faster to find shortest candidate +- use queue to hold left, right candidates +- use set to record visited + +### Method2: DFS +- attemp all nodes, use set to record visited. +- time: O(n) +- space: O(n) + + + +--- + +**36. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**37. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + +**38. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + +**39. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**40. [100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### Method1: DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### Method2: BFS with 2 queues +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**41. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**42. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + +**43. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**44. [103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + + +#### 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 + + + +--- + +**45. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**46. [133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%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: `map` + - 1) make new curr node; + - 2) clone all neibhors and add them +- Use the map to avoid visited nodes +- time: O(n). visit all nodes +- space: O(n). Technically only travels n levels/stacks to circle all nodes (undirected & connected) + +#### 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. + + + +--- + +**47. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**48. [1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS, top-down +- key concetpL the `common ancester of deppest leaves` must have its `two branch being same depth`. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +- time: O(n) traversal 1 pass +- space: O(n) dfs worst case depth + +#### Method2: bottom-up, find leaves first and traverse backwards +- 1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +- 2) use leaf nodes to find way backwards till common node is found; return +- time: O(n) but two passes +- space: O(n) dsf + map storage +- this approach is more brutle and uses exrtra spaces + + + +--- + +**49. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + +**50. [785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)** Level: Medium Tags: [BFS, DFS, Garph] + + +#### DFS marking each node with a state +- `bipartite` require each node to be in exact 1 party, which means it only has 1 state +- DFS to mark node with one state; and mark its edges as reversed state + - If any node state has been assigned by different from desired one, return false. + +#### BFS, Queue +- Use `Boolean states[i]` to represent visted & state +- Try all nodes with for loop, and skip visited nodes (similar validation rules as in dfs) +- In `int next : graph[curr]`, test next level first before adding. + + + +--- + +**51. [101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### Method1: DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Method2: interative with queue +- put left or right children in pair + +#### Method3: iterative with Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**52. [671. Second Minimum Node In a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/671.%20Second%20Minimum%20Node%20In%20a%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +#### BFS +- min tree: parent node is the min of left/right child +- BFS to traverse the tree and find 1st non-root smallest val +- Improvement area: when `node.val >= nextMin`, no need to dive into node children since it is a min Tree. + +#### DFS +- Find left and right val: + - if left/right equals root.val, that means the left or right sub children could have larger number + - Therefore DFS into left or right +- compare and return min(left, right) + + + +--- + +**53. [254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)** Level: Medium Tags: [BFS, Backtracking, DFS] + + + +#### Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs + + +#### Method2: BFS +- Check if the number can be devided by [2, sqrt(n)], return a list of possible factors. Only check till `Math.sqrt(n)` + - build suffixes: use the factor to devide last element of list and replace last element + - build candidate: replace last element of the queue item with new list of suffixes; add to rst + - add success item back to queue: in case last element can be simplified +- **remove dupilcates**: since we start factor from [2, sqrt(n)], the final factor list should be **ascending**!! +- time: O(x), x is the # of results +- space: O(y), y is all ongoing candidates in queue + + + +--- + diff --git a/review/BIT.md b/review/BIT.md new file mode 100644 index 0000000..3c292a5 --- /dev/null +++ b/review/BIT.md @@ -0,0 +1,87 @@ + + + +## BIT (2) +**0. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**1. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + diff --git a/review/BST.md b/review/BST.md new file mode 100644 index 0000000..aea38d3 --- /dev/null +++ b/review/BST.md @@ -0,0 +1,486 @@ + + + +## BST (23) +**0. [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. + + + +--- + +**1. [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的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**2. [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 + + + +--- + +**3. [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 + + + +--- + +**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. [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的边框限制好,中间就全部遍历了。 + + + +--- + +**6. [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规律。 + + + +--- + +**7. [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 + + + +--- + +**8. [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? + + + +--- + +**9. [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维护清楚就行。 + + +--- + +**10. [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 + + + + +--- + +**11. [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一下。 + + + +--- + +**12. [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. 这个用法要记住吧, 没别的捷径. + + + +--- + +**13. [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= 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. + + + +--- + +**15. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**16. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**17. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**18. [270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +Concept: Iterate over all logN nodes in the BST and record the closest. Rather than finding the value at +/- 0.5 precision + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, until null leaf +- time: O(logn), space O(1) since no extra space used + +#### DFS, Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return +- time: O(logn), space: O(logn) + + + + +--- + +**19. [938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)** Level: Easy Tags: [BST, Recursion, Tree] + +#### DFS based on BST rules +- sum up the matching L & R + - Find (L,R) on left child + - Find (L,R) on right child + - Find (L,R) covering the root node +- space O(n), worst case O(logn), height of dfs. +- time O(n) to find all nodes between (L, R) + +#### Iterative +- Using stack, or queue, list: any data structure (we are not doing ordered search) +- space O(n) +- time O(n) + + + +--- + +**20. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**21. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**22. [235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的 path +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Beofre lasts common ancestor is found: p and q should follow same search pattern: + - compare with root, then dfs(left) or dfs(right) + - util p and q fall on two sides of root, then return root + - 非常巧妙, 但是也比较局限; 稍微变条件, 就很难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(logn) extra space: recursive stack space +- O(logn) time + + + +--- + diff --git a/review/Backpack DP.md b/review/Backpack DP.md new file mode 100644 index 0000000..59fcd16 --- /dev/null +++ b/review/Backpack DP.md @@ -0,0 +1,225 @@ + + + +## Backpack DP (8) +**0. [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, 而不考虑是哪个. + + + + + + +--- + +**1. [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) + + + +--- + +**2. [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 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**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 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) + + + +--- + +**5. [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 + + + +--- + +**6. [518. Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/518.%20Coin%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 + + + +--- + +**7. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + diff --git a/review/Backpack.md b/review/Backpack.md new file mode 100644 index 0000000..e6df4b7 --- /dev/null +++ b/review/Backpack.md @@ -0,0 +1,18 @@ + + + +## Backpack (1) +**0. [416. Partition Equal Subset Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/416.%20Partition%20Equal%20Subset%20Sum.java)** Level: Medium Tags: [Backpack, DP] + +#### Backpack DP +- the problem turns into: can we find a subset of items that sum up to target sum? +- create `boolean dp[j]` to represent if we can sum up to j, where j = sum value + - want to try out all items in num, + +#### DFS +- use dfs to find a subset of items that sum up to target sum? + + + +--- + diff --git a/review/Backtracking.md b/review/Backtracking.md new file mode 100644 index 0000000..9c82068 --- /dev/null +++ b/review/Backtracking.md @@ -0,0 +1,898 @@ + + + +## Backtracking (35) +**0. [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了 + + + +--- + +**1. [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) + + + +--- + +**2. [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 + + + + +--- + +**3. [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许多。 + + + + +--- + +**4. [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 + + + +--- + +**5. [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开始选。 + + + +--- + +**6. [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一下。 + + + + +--- + +**7. [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, 碰壁的时候就回头走。 + + + +--- + +**8. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**9. [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};` + + + +--- + +**10. [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] + + + + +--- + +**11. [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 一样 + + + +--- + +**12. [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的情况。要把遍历的例子写写 + + + +--- + +**13. [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里面, 这个想法非常值得思考. + + + +--- + +**14. [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)))` + + + +--- + +**15. [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 + + + +--- + +**16. [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。 + + + +--- + +**17. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**18. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**20. [131. Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/131.%20Palindrome%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. [257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%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 +- time: visit all n nodes +- space: to hold all paths, O(nlogn) + - O((n-1)/2) = O(n) nodes at leaf + - O(logn) depth + +#### 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 + + + + +--- + +**22. [1219. Path with Maximum Gold.java](https://github.com/awangdev/LintCode/blob/master/Java/1219.%20Path%20with%20Maximum%20Gold.java)** Level: Medium Tags: [Backtracking, DFS] + + + +### DFS, Backtracking +- typical recursive visit all situation + + + + +--- + +**23. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**24. [51. N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/51.%20N-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 +- Backtracking by replacement: each row has 1 queen, so just store it in int[] columns (CC book solution) + +#### 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 + + + + +--- + +**25. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + +**26. [211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +#### Trie, prefix tree. +- Trie Structure: `boolean isEnd`, `HashMap children` + - trie.addWord: 没node就加,有node就移动 + - trie.search: 没node就return false,有node就移动 +- Alternatively, the hash can be `TrieNode[26]` a fixed size array when applicable + - I like map better for the simplicity to write (w/o converting char -> index) + + + + +--- + +**27. [47. Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/47.%20Permutations%20II.java)** Level: Medium Tags: [Backtracking, DFS] + +space: O(n!) + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +Similar to 46. Permutations, but with dedup + +#### Backtracking +- Sort the list, and on same level, if last element is the same as curr, skip this recursive call +- time O(n!) + +#### 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]在重复时候, 不用重新记录. + + + +--- + +**28. [332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)** Level: Medium Tags: [Backtracking, DFS, Graph] + + +#### DFS with backtcking +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +- time: O(n^n). n = # of cities. worst case, each city has (n-1) edges and need to try all combinations +- space: O(n^2), there can at most be n * (n - 1) edges + + + +--- + +**29. [39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + + + +--- + +**30. [10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + + + +--- + +**31. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**32. [52. N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/52.%20N-Queens%20II.java)** Level: Hard Tags: [Backtracking] + + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking (with replacement) +- Each row has just 1 Queen value +- As CC book suggests, use `int[] columns` of length n to store all queen col positions for n rows + - `int[] columns` is slightly easier to backtrack by updating certain index i with new col + - list will usualy has the add/remove pattern for backtracking + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + + + + +--- + +**33. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + +**34. [254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)** Level: Medium Tags: [BFS, Backtracking, DFS] + + + +#### Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs + + +#### Method2: BFS +- Check if the number can be devided by [2, sqrt(n)], return a list of possible factors. Only check till `Math.sqrt(n)` + - build suffixes: use the factor to devide last element of list and replace last element + - build candidate: replace last element of the queue item with new list of suffixes; add to rst + - add success item back to queue: in case last element can be simplified +- **remove dupilcates**: since we start factor from [2, sqrt(n)], the final factor list should be **ascending**!! +- time: O(x), x is the # of results +- space: O(y), y is all ongoing candidates in queue + + + +--- + diff --git a/review/Basic Implementation.md b/review/Basic Implementation.md new file mode 100644 index 0000000..28bf41d --- /dev/null +++ b/review/Basic Implementation.md @@ -0,0 +1,198 @@ + + + +## Basic Implementation (18) +**0. [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 + + + +--- + +**1. [Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)** Level: Easy Tags: [Basic Implementation] + +根据 Cosine Similarity 的公式, basic implementation + + + +--- + +**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 + + + +--- + +**3. [788. Rotated Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/788.%20Rotated%20Digits.java)** Level: Easy Tags: [Basic Implementation, String] + + +#### Basic Implementation of the rules +- [3,4,7] -> cannot rotate, failures. Must NOT have. set1 +- 2,5,6,9 -> good candidates. Must have 1. set2 +- [0,1,8] -> goes back to itself. can have +- loop over [1, N], count=int[10] appearance. + - set1 meet 0 + - set2 meet at least 1 + + + +--- + +**4. [849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array, Basic Implementation, Two Pointers] + + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, Two Pointers: 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` + + + + +--- + +**5. [408. Valid Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/408.%20Valid%20Word%20Abbreviation.java)** Level: Easy Tags: [Basic Implementation, String] + +tricky: find integer within a string +edge case: leading '0' should not be allow in such abbr. + + + +--- + +**6. [415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)** Level: Easy Tags: [Basic Implementation, Math, String] + + +#### Two Pointer +- Use i, j to process from end of 2 strings +- handle edge case for i, j + - if i < 0, its num = 0 (since we are doing sum, blindly setting 0 is okay) +- Note: `sb.insert(0, x)` is much slower than doing a final `sb.reverse()` + +#### If manually convertin to int[] +1. when converting to int[], remember to reverse string. +1. when converting to int[], remember to reserve extra space for carry + + + +--- + +**7. [1108. Defanging an IP Address.java](https://github.com/awangdev/LintCode/blob/master/Java/1108.%20Defanging%20an%20IP%20Address.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**8. [383. Ransom Note.java](https://github.com/awangdev/LintCode/blob/master/Java/383.%20Ransom%20Note.java)** Level: Easy Tags: [Basic Implementation, String] + +count chars in int[256] + + + +--- + +**9. [686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)** Level: Easy Tags: [Basic Implementation, Edge Case, String] + +Track: 纸上分析edge case. +Validation helps speed it up. + + + +--- + +**10. [485. Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/485.%20Max%20Consecutive%20Ones.java)** Level: Easy Tags: [Array, Basic Implementation] + + +- preserve max +- 清零count + + + +--- + +**11. [824. Goat Latin.java](https://github.com/awangdev/LintCode/blob/master/Java/824.%20Goat%20Latin.java)** Level: Easy Tags: [Basic Implementation, String] + + + + +--- + +**12. [119. Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/119.%20Pascal's%20Triangle%20II.java)** Level: Easy Tags: [Array, Basic Implementation] + + +简单处理 list. code is very similar to Pascal triangle I. + +- 注意 `list = Arrays.asList(x, y, z ...)` 给fixed-size list, 不能直接 list.add(). +- Use `new ArrayList<>(Arrays.asList(...))` to wrap it up. + + + + +--- + +**13. [443. String Compression.java](https://github.com/awangdev/LintCode/blob/master/Java/443.%20String%20Compression.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**14. [12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)** Level: Medium Tags: [Basic Implementation, Math, String] + + +#### String, Basic implementation +- Parse each digit based on rules +- 1) parse: analyze the situations + + +--- + +**15. [893. Groups of Special-Equivalent Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/893.%20Groups%20of%20Special-Equivalent%20Strings.java)** Level: Easy Tags: [Basic Implementation, String] + +Mark # of characters can be useful to print string signature + + + +--- + +**16. [118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)** Level: Easy Tags: [Array, Basic Implementation, List] + + + + +--- + +**17. [1033. Moving Stones Until Consecutive.java](https://github.com/awangdev/LintCode/blob/master/Java/1033.%20Moving%20Stones%20Until%20Consecutive.java)** Level: Easy Tags: [Basic Implementation, Sort] + + +#### Analyze to understand +- put 3 elements into array, sort and follow below rules: +- min: + - if 3 elements consecutive, 0 move. + - if only 1 pair of the two elemnets consecutive or if they have 1 slot in between, it needs exactly 1 move + - otherwise, at most 2 moves +- max: # of open slots between them (high - low + 1) - n, where n = 3 +- Follow up: `1040. Moving Stones Until Consecutive` is more interesting with special rulese (cannot move to `ending spot`), and it uses sliding window concept + + + +--- + diff --git a/review/Binary Indexed Tree.md b/review/Binary Indexed Tree.md new file mode 100644 index 0000000..1385be9 --- /dev/null +++ b/review/Binary Indexed Tree.md @@ -0,0 +1,144 @@ + + + +## Binary Indexed Tree (4) +**0. [308. Range Sum Query 2D - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/308.%20Range%20Sum%20Query%202D%20-%20Mutable.java)** Level: Hard Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree +- Same concept as turning an array into a binary segment tree, + - HOWEVER, this is a 4-nary segmenet tree +- Reference. 307 Range Sum Query +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. +- Handling end stage, there are two approaches: + - ApproachA: check at beginning of recursive call (i.e in `build()`, `updateNode()`, `rangeQuery()`). + - pro: calling recursive function blindly; code is easy. + - con: be really clear about termination state, and catch it. + - ApproachB: check & come up with correct query condition before recursive call + - pro: input to recursive function is assumed to be correct + - con: sometimes really hard to write the conditions before recursive call; code is hard. + + + +--- + +**1. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**2. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**3. [307. Range Sum Query - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/307.%20Range%20Sum%20Query%20-%20Mutable.java)** Level: Medium Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree, devide and conquer +- sample problem for segment tree +- build(), update(), rangeQuery() + - build and update are standard + - rangeQuery: handle the range split check +- Null leaf node handling: NO, ideally it will not encounter null leaf. + - in update/rangeQuery: when final state (`start==end`) is reached, the recursive call ends + - there is no way for any node to dive futher into null child. +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. + + + +--- + diff --git a/review/Binary Search on Value.md b/review/Binary Search on Value.md new file mode 100644 index 0000000..c4dc67a --- /dev/null +++ b/review/Binary Search on Value.md @@ -0,0 +1,31 @@ + + + +## Binary Search on Value (1) +**0. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + diff --git a/review/Binary Search.md b/review/Binary Search.md new file mode 100644 index 0000000..f91f210 --- /dev/null +++ b/review/Binary Search.md @@ -0,0 +1,839 @@ + + + +## Binary Search (45) +**0. [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 + + + +--- + +**1. [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) + + + +--- + +**2. [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 + + + +--- + +**3. [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) + + + +--- + +**4. [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 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**5. [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)) + + + + +--- + +**6. [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] + + +给一串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 + + + +--- + +**7. [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 是理所当然的 + + + +--- + +**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. [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) + + + +--- + +**10. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**11. [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. + + + + +--- + +**12. [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 + + + +--- + +**13. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + +--- + +**14. [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) + + + +--- + +**15. [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. + + + +--- + +**16. [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. + + + + +--- + +**17. [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), 就不写了 + + + +--- + +**18. [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. + + + +--- + +**19. [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 公式 + + + +--- + +**20. [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. + + + +--- + +**21. [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] + + +给一个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) + + + + +--- + +**22. [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 + + + +--- + +**23. [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 的情况. + + +--- + +**24. [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. + + + +--- + +**25. [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? + + + + +--- + +**26. [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 + + + +--- + +**27. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**28. [275. H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/275.%20H-Index%20II.java)** Level: Medium Tags: [Binary Search] + + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +Aim to find the lowest index mid, which maximize h = n - mid + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + + + +--- + +**29. [222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, DFS, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### Method1: DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样 + - 如果一样, 直接 2 ^ h - 1就好 + - 不一样的话, 再DFS +- calculate `2^(h)`: 位运算, Math.pow(2, h) = 2 << (h - 1). 神奇! + - 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- time: O(n) visit all nodes on 1 side +- space: O(h) visit all nodes on 1 side + + +#### Method2: Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +#### Method3: Binary Search +- NOT DONE, TODO: https://leetcode.com/problems/count-complete-tree-nodes/solution/ + + + +--- + +**30. [1060. Missing Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1060.%20Missing%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Binary Search] + + +#### Binary Search +- total missing nums = nums[curr] - nums[0] - curr +- edge case: if k > total missing nums, then just add the diff from nums[end] +- otherwise, find this `missing count == k` in the nums using binary search +- After binary search: `start + 1 == end`: + - re-calculate `count = nums[start] - nums[0] - start;` + - output final num: `nums[start] + k - count;` +- Option1: always compare total missing nums count +- Option2: compare partial missing nums count (inspired by: https://leetcode.com/problems/missing-element-in-sorted-array/discuss/303444/Java-O(logN)-solution-Binary-Search) + + + + +--- + +**31. [875. Koko Eating Bananas.java](https://github.com/awangdev/LintCode/blob/master/Java/875.%20Koko%20Eating%20Bananas.java)** Level: Medium Tags: [Binary Search] + + + +#### Binary Search +- Bianry serach on the min/max value range +- The mid value is calcualted with helper function `calc(piples, k)` +- find celing: `count += (i - 1) / k + 1`, faster than `Math.ceil(i / k)` +- time: O(logm) to find the best velocity, assume total range is m; O(n) for each `calc` call + + + +--- + +**32. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**33. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**34. [88. Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- Also most identical to `33. Search in Rotated Sorted Array`: + - find where nums[mid] lands by comparing to nums[start]. i.e., if nums[mid] < nums[start], on right half of the array + - when `nums[mid] == nums[start]`: duplicate. Shift by start++ +- the worst case of `nums[mid] == nums[start]` willl cause O(n), +- but if duplicate is not entire array, should be O(logn) + + + +--- + +**35. [367. Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/367.%20Valid%20Perfect%20Square.java)** Level: Easy Tags: [Binary Search, Math] + + +#### Binary找sqrt +- binary search template: mid+1, mid-1, `start <= end` +- define index as long. + + + +--- + +**36. [270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +Concept: Iterate over all logN nodes in the BST and record the closest. Rather than finding the value at +/- 0.5 precision + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, until null leaf +- time: O(logn), space O(1) since no extra space used + +#### DFS, Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return +- time: O(logn), space: O(logn) + + + + +--- + +**37. [852. Peak Index in a Mountain Array.java](https://github.com/awangdev/LintCode/blob/master/Java/852.%20Peak%20Index%20in%20a%20Mountain%20Array.java)** Level: Easy Tags: [Binary Search] + + +#### Binary Search +- binary search to find A[i-1] < A[i] < A[i+1] + - if [mid-1] < [mid+1], on left slope, start = mid + - if [mid-1] > [mid+1], on right slope, end = mid +- init: start == 1, end = n - 2; + + + +--- + +**38. [981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)** Level: Medium Tags: [Binary Search, Hash Table, TreeMap] + + +#### Method1: Binary Search +- use hash to store +- binary serach on list of values + +#### Method2: TreeMap +- use hash to store > +- treemap.floorKey(timestamp) finds the top item below certain timestamp + + + +--- + +**39. [69. Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/69.%20Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + +#### sqrt(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 + + + +--- + +**40. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + +**41. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**42. [33. Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/33.%20Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 + - 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` + - 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- Binary search template: + - 1) `start + 1 < end` (adjacent indexes) + - 2) start/end = mid, + - 3) compare start and end individually + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**43. [34. Find First and Last Position of Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/34.%20Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- need search left bound & right bound. +- use input parameter `direction` to binary search function +- Option0: simplification, inspired by `278. First Bad Version - Method1: Check is-NOT-BadVersion` + - 1) if found match, but NOT sure it is desired boundary, just leave it and keep going + - 2) check the final results after `binary search while loop` completes + - WHY? code is easier to read in this way. + + + +--- + +**44. [278. First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/278.%20First%20Bad%20Version.java)** Level: Easy Tags: [Binary Search] + + +#### Method1: Check is-NOT-BadVersion +- simply binary Search: if not bad, assign `start = mid+1` + +#### Method2: Check ifBadVersion +- 根据isBadVersion的性质,判断还如何end=mid or start=mid. +- A bit more code to handle + + + +--- + diff --git a/review/Binary Tree.md b/review/Binary Tree.md new file mode 100644 index 0000000..69965ca --- /dev/null +++ b/review/Binary Tree.md @@ -0,0 +1,270 @@ + + + +## Binary Tree (14) +**0. [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, 但是数字可为多位 + + + +--- + +**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. [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的边框限制好,中间就全部遍历了。 + + + +--- + +**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. [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 + + + + +--- + +**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. [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. [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就可以了. + + + +--- + +**8. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**10. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**11. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**12. [257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%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 +- time: visit all n nodes +- space: to hold all paths, O(nlogn) + - O((n-1)/2) = O(n) nodes at leaf + - O(logn) depth + +#### 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 + + + + +--- + +**13. [114. Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/114.%20Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### Method1: DFS return tail node +- DFS to return the tail of the flattened list +- Careful handling the null child. Choose one and both works: + - Option1: put non-null as right child and continue dfs + - Option2: put non-null as left child and continue dfs + +#### Method2: void DFS +- latten the tree, no extra space. + - 1. Set right node in buffer, and connect: `root.right = root.left`, DFS flatten(root.right) + - 3. 移花接木: traverse to tail of the current root.right and attach the buffer node. + - 3. flatten the remaining of buffer + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + diff --git a/review/Bit Manipulation.md b/review/Bit Manipulation.md new file mode 100644 index 0000000..1e8820a --- /dev/null +++ b/review/Bit Manipulation.md @@ -0,0 +1,321 @@ + + + +## Bit Manipulation (19) +**0. [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 + + + +--- + +**1. [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) 太慢, 不合题意 + + + +--- + +**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. [Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)** Level: Medium Tags: [Bit Manipulation] + + +给出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. + + + + +--- + +**4. [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. + + + +--- + +**5. [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的要求! + + + +--- + +**6. [Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)** Level: Easy Tags: [Bit Manipulation] + + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + + + +--- + +**7. [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 + + + +--- + +**8. [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] + + + +--- + +**9. [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负数补位. + + + +--- + +**10. [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找合适的数字. + + + +--- + +**11. [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 + + + +--- + +**12. [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看出有直接关系. + + + +--- + +**13. [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) + + + +--- + +**14. [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 + + + +--- + +**15. [405. Convert a Number to Hexadecimal.java](https://github.com/awangdev/LintCode/blob/master/Java/405.%20Convert%20a%20Number%20to%20Hexadecimal.java)** Level: Easy Tags: [Bit Manipulation] + +#### Unsigned Shift, Mask +- Move pointer: move digit after process 4 bits. + - `>>>` Unsigned right shift + - always fills 0 irrespective of the sign of the number +- Mas: `num & 0xf` = `num & 15` + + + +--- + +**16. [136. Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/136.%20Single%20Number.java)** Level: Easy Tags: [Bit Manipulation, Hash Table] + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + + + +--- + +**17. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**18. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + diff --git a/review/Bitwise DP.md b/review/Bitwise DP.md new file mode 100644 index 0000000..79d5c76 --- /dev/null +++ b/review/Bitwise DP.md @@ -0,0 +1,20 @@ + + + +## 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..8728205 --- /dev/null +++ b/review/Brainteaser.md @@ -0,0 +1,35 @@ + + + +## Brainteaser (2) +**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的做法写出结果. + + + +--- + +**1. [319. Bulb Switcher.java](https://github.com/awangdev/LintCode/blob/master/Java/319.%20Bulb%20Switcher.java)** Level: Medium Tags: [Brainteaser, Math] + + +#### Brainteaser +- https://leetcode.com/problems/bulb-switcher/discuss/77104/Math-solution.. + +#### Brutle: +- if just impl, it take O(n^2): +- repating: some pos are toggled mutiple times: if we know total times, easy to determin each pos. +- loop over [2, n], count times on each index + + + +--- + diff --git a/review/Bucket Sort.md b/review/Bucket Sort.md new file mode 100644 index 0000000..2e0da78 --- /dev/null +++ b/review/Bucket Sort.md @@ -0,0 +1,108 @@ + + + +## Bucket Sort (4) +**0. [448. Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/448.%20Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)** Level: Easy Tags: [Array, Bucket Sort] + + +#### Method1: Bucket Sort concept, set val to its correct position +- Given: values are [1,n], so val can represent index. Therefore, set val to its correct position +- 小心handle i: + - value是 1-based + - 每次换位, 需要`i--`, 重新省察`nums[i]` + +#### Method2: 做标记 (negative number, or super large number) +- Option1: use negative number to mark visited: + - 很巧妙地运用了标记的方法, 标记成负数,证明visit过。 + - Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! +- Option2: use large number (larger than n) + - 跟方法2类似,也是做标记,这一次是加上一个大于n的数(因为题目给了n的border),最后check一下就好。为不超Integer.MAX_VALUE, 每次加n前,取余数。 + - 做标记的方法固然快,但是相对来说比较hacky,在常规的代码中,估计不会用到. + + + + +--- + +**1. [1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)** Level: Medium Tags: [Bucket Sort, DP, Hash Table, Sort] + + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**2. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**3. [274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.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. + +#### Couting Sort / 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 + +#### More about Counting Sort +1. Application: for number/character range +1. Steps: + - Find range, define countArray + - Count element and record in the array + - PreSum the countArray + - Start from beginning of the array, `print & decrese count` to produce the sorted elements + + + + +--- + diff --git a/review/Combination.md b/review/Combination.md new file mode 100644 index 0000000..0a2fc3f --- /dev/null +++ b/review/Combination.md @@ -0,0 +1,92 @@ + + + +## 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 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)))` + + + +--- + +**2. [39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + + + +--- + +**3. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + diff --git a/review/Coordinate DP.md b/review/Coordinate DP.md new file mode 100644 index 0000000..42bbeac --- /dev/null +++ b/review/Coordinate DP.md @@ -0,0 +1,441 @@ + + + +## Coordinate DP (17) +**0. [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 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 + + + +--- + +**1. [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);` + + + +--- + +**2. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**3. [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作比较;求的所有情况的最大值嘛。 + + + +--- + +**4. [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. + + + + +--- + +**5. [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] + + +给一串 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) + + + +--- + +**6. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**7. [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) + + + + +--- + +**8. [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 +还没有做 + + + +--- + +**9. [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; + + + +--- + +**10. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**11. [221. Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/221.%20Maximal%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; +- init: 每个点都可能是边长1, 如果 matrix[i][j] == '1' +- Space, time O(mn) +- Rolling array: [i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + + +--- + +**12. [62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +#### DP, 加法原理 +- 计数DP: 2 ways to reach (i,j): dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + - non-overlapping: `dp[i - 1][j]`, `dp[i][j - 1]` + - covers the only 2 possible way to reach (i,j) +- initialization: dp[i][0] = 1, dp[0][i] = 1 + - Of course, row i = 0, or col j = 0, there is only 1 way to reach +- time O(mn), space O(mn) + +##### 滚动数组 Rolling Array +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + +#### DFS + Memoization +- move from (0,0) towards (m, n) +- use Map as memoization technique + + + +--- + +**13. [64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%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(N) + - 1) 需要在同一个for loop里面完成initialization, 2) reuse dp[i][j] + - Reason: dp[i % 2][j] 在被计算出来的时候, 马上在下一轮会被用. 被覆盖前不用,就白算 + - Option3 below initialize dp outside of loop: 看起来固然简单, 但是不方便空间优化 + +#### DFS (top-down) Thinking process +- Enumerate the possible 2 paths: go right, go down +- sub problem: dfs(i+1,j), dfs(i,j+1); pick the min of the two +- memoization: after the path from a point (i,j) to end is computed, record memo[i][j] to avoid repatative calculation +- time: O(mn), only visite and record memo[i][j] once. +- space: O(mn) extrem case of m=100000, n = 2; the stack height is O(mn) + + +--- + +**14. [63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%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] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + + + +--- + +**15. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + +**16. [361. Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/361.%20Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Method1: 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 的变换. + +#### Method2: Analyze, col count array: +- Inspired by: https://leetcode.com/problems/bomb-enemy/discuss/83387/Short-O(mn)-time-O(n)-space-solution +- Analyize the problem: need to add up 2 directions at any given point. + - Notice 1: if I traverse row by row, each colSum at a specific col j is likely to be the same + - Unless there is a Wall in last row, so we have to calclate the col sum starting from current row, below the Wall + - Notice 2: for row it is the same: + - If I in a new spot row[i][j], where (i-1) is Wall, i need to sum row from 0 +- we will process each point: + - process row by row and add up row sum + - buffer col[j] in an array vertically so we can resue + - make sure to recalculate row sum or col sum if last row index or last col index is Wall +- time: O(mn) traversal +- space: O(n) only use a column array + + + +--- + diff --git a/review/Cycle Detection.md b/review/Cycle Detection.md new file mode 100644 index 0000000..1622fd6 --- /dev/null +++ b/review/Cycle Detection.md @@ -0,0 +1,58 @@ + + + +## Cycle Detection (3) +**0. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**1. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**2. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + diff --git a/review/DFS.md b/review/DFS.md new file mode 100644 index 0000000..47e8feb --- /dev/null +++ b/review/DFS.md @@ -0,0 +1,2725 @@ + + + +## DFS (121) +**0. [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. + + + +--- + +**1. [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 + + + +--- + +**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. [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了 + + + +--- + +**4. [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);` + + + +--- + +**5. [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,要乘一下。 + + + + +--- + +**6. [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 + + + +--- + +**7. [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) + + + +--- + +**8. [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 + + + +--- + +**9. [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()` + + + +--- + +**10. [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 + + + + +--- + +**11. [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. + + + +--- + +**12. [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)) + + + + +--- + +**13. [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 + + + + +--- + +**14. [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 + + + +--- + +**15. [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 + + + +--- + +**16. [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. + + + +--- + +**17. [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. + + + +--- + +**18. [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. + + + +--- + +**19. [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开始选。 + + + +--- + +**20. [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, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**21. [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一下。 + + + + +--- + +**22. [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. + + + +--- + +**23. [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加上去就好. + + + +--- + +**24. [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) + + + +--- + +**25. [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 + + + +--- + +**26. [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的基础上, 比对左左,左右,右左,右右 + + + +--- + +**27. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**28. [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了 + + + +--- + +**29. [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 + + + +--- + +**30. [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};` + + + +--- + +**31. [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完 + + + +--- + +**32. [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 + + + +--- + +**33. [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. + + + +--- + +**34. [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 + + + +--- + +**35. [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 一样 + + + +--- + +**36. [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 + + + +--- + +**37. [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的判断 + + + +--- + +**38. [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的情况。要把遍历的例子写写 + + + +--- + +**39. [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. + + + + +--- + +**40. [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 +还没有做 + + + +--- + +**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. [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) + + + +--- + +**43. [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 + + + +--- + +**44. [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一下。 + + + +--- + +**45. [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] + + +给一个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 + + + +--- + +**46. [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)))` + + + +--- + +**47. [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` + + + +--- + +**48. [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就可以了. + + + +--- + +**49. [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 + + + +--- + +**50. [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. + + + +--- + +**51. [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 + + + +--- + +**52. [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) + + + +--- + +**53. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + +// 如何想到从中间initialize + + + +--- + +**54. [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] + + +给一个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才可以施行. + + + +--- + +**55. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**56. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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 + + + +--- + +**57. [102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### Method1: BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### Method2: DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**58. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**59. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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 + + + +--- + +**60. [236. Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/236.%20Lowest%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 + - Base Case: 当root == null, p or q is found (`root == p || root == q`),那么就return the root as LCA + - 三种情况: + - 1. leftLCA and rightLCA all found: `each path has found one of p and q node as LCA`. Therefore, curr root is the lowest ancestor + - 2. One of leftLCA and rightLCA is found: return whichever one found + - 3. both LCAs are null, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time O(n), stack space O(n) + + + +--- + +**61. [301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)** Level: Hard Tags: [BFS, DFS, DP] + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- Goal: identify invalid parentheses and remove (minimum removals) +- Step: + - Detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` + - When invalid occurs: + - chance for correction. Remove the incorrect parentheses, one at a time + - dfs on the rest of the s that has not been tested yet: start index from index i + - Core edge cases: + - Do not correct twice of the same parenthesis by checking [j-1] pos + - Make sure to attempt correction of all possible parenthesis within tested range: because it outputs all results at the same level + - return/finish once correction done +- Success case: + - a string s passed test: make sure it passes REVERSED string test! + - Core Concept: `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**. +- How does it guarantee minimum removals? + - When seeing a chance to correct, it jumps into a for loop of DFS. It `return` after the for loop. This stops additional testing + - When invalid occurs, correct it right away: minimum correction +- Complexity: + - O(nk), k being the # of recursive calls. It takes n calls to finish a full string case. + +#### BFS +- Similar to DFS, we wnat to test: 1) test input s valid, 2) remove 1 invalid parenthesis at a time, 3) process substring +- instead of testing all substrings (timeout), we want to establish rules to improve reprocess: + - Test1: skip regular char. No need to test it. + - Test2: if redundant paren, do 1 is enough. skip adjacent ones. + - Test3: if last removed extra paren is '(', the next ')' must be a valid pair. LastRemoved char: pecial handling by using a struct: `class Node {String s, int index, char lastRemoved}` +- How to end tests? When there is data in rst, stop adding to queue. + + + +--- + +**62. [111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%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 Conquer to find min depth. + - if one of child is null, return the other child depth + 1 + - Pick the min of the two child depth + 1 +- need to visit all nodes + + + + +--- + +**63. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**64. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**65. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**66. [694. Number of Distinct Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/694.%20Number%20of%20Distinct%20Islands.java)** Level: Medium Tags: [DFS, Hash Table] + + +#### DFS + HashSet +- DFS can find # of island, just like `200.Number of Islands`, aim to count total +- We need to map same-shap land + - One approach: print the **footprint** starting from coordinate (0, 0) + - Another approach: print the actual island in its boundary, like a QR code. (too hard to code, skip) +- Footprint approach: + - 1. always assume a newly found islands starts from (0, 0) + - 2. take 4 direction from init pos and keep printing the footprint + - 3. Since we always visit nodes from top->right->nextrow, we always visit top-left cornor of a new island, and the footprint will be identical + - Otherwises, if needed, we can sort the footprint and output the hash +- time: O(n), visit all +- space: O(n), store footprint, and dfs stacks worst case visit all nodes + + + +--- + +**67. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**68. [199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%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 +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n nodes in final results + + +#### DFS +- Use Map to override the result at each level +- dfs: + - dfs(node.left) and then dfs(node.right) because we want to log right side last +- record global max depth for iteration purpose +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n stacks (and n nodes in final results) + + + + +--- + +**69. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**70. [1008. Construct Binary Search Tree from Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/1008.%20Construct%20Binary%20Search%20Tree%20from%20Preorder%20Traversal.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top Down DFS +- This approach highly relies on the preorder rules + - we can use validation rules to navigate throug hteh preorder array + - use a global index +- time: O(n) + + + + +--- + +**71. [515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS +- faster than BFS, using less space if not couting final rst: stack size, O(logn) +- time: O(n), visit all + +#### Method2: BFS with queue +- loop over queue level and record max + + + + +--- + +**72. [1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph] + + + +#### BFS +- simply calc each level sum with BFS +- top-level is processed first, since we go from top level -> deeper level + - only update result if sum is truly > global MAX. + + + +--- + +**73. [131. Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/131.%20Palindrome%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) + + + +--- + +**74. [222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, DFS, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### Method1: DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样 + - 如果一样, 直接 2 ^ h - 1就好 + - 不一样的话, 再DFS +- calculate `2^(h)`: 位运算, Math.pow(2, h) = 2 << (h - 1). 神奇! + - 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- time: O(n) visit all nodes on 1 side +- space: O(h) visit all nodes on 1 side + + +#### Method2: Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +#### Method3: Binary Search +- NOT DONE, TODO: https://leetcode.com/problems/count-complete-tree-nodes/solution/ + + + +--- + +**75. [257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%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 +- time: visit all n nodes +- space: to hold all paths, O(nlogn) + - O((n-1)/2) = O(n) nodes at leaf + - O(logn) depth + +#### 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 + + + + +--- + +**76. [1219. Path with Maximum Gold.java](https://github.com/awangdev/LintCode/blob/master/Java/1219.%20Path%20with%20Maximum%20Gold.java)** Level: Medium Tags: [Backtracking, DFS] + + + +### DFS, Backtracking +- typical recursive visit all situation + + + + +--- + +**77. [1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +- time: O(n), visit all nodes +- space: O(logn), height of the tree + +#### Method2: HashMap, DFS. +- traverse tree and create `map ` to fast O(1) removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- This approach requires a dfs build of parentMap + - it is same amount of efforts to do the regular dfs removal. + - not a good solution +- time: O(n) +- space: O(n) + + + +--- + +**78. [339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS, NestedInteger] + + +给一串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. + - 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) + +#### BFS +- bfs, queue, 处理queue.size() for a level +- use a level variable to track levels +- slower since it uses extra space, worst case O(n) of all items + + + +--- + +**79. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + +**80. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**81. [741. Cherry Pickup.java](https://github.com/awangdev/LintCode/blob/master/Java/741.%20Cherry%20Pickup.java)** Level: Hard Tags: [DFS, DP] + + +special hint: `r1 + c1 = constant t = r2 + c2`, if the two points are moving at same time. + +#### DFS + Memo: TOP-DOWN +- Similar concept to Minimum Path Sum +- https://leetcode.com/problems/cherry-pickup/solution/ +- realize r1 + c1 = r2 + c2. Knowing 3 parameters can uniquely identify the 4th. +- assume there are 2 people starting from origin, and the 2 people can go total 4 directions + - perform DFS based on the 4 directions + - concern: do they visit the same spot? possible. when that happens, make sure we do not double count the grid[i][j] +- when is the end state? + - then anyone, for example, (r1,c1) reaches end (n-1, n-1). + - it means the other person also reaches end +- use memo: memo[r1][c1][r2], it records any given (r1, c1, r2, c2) state + + + + +--- + +**82. [104. Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/104.%20Maximum%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 + + + +--- + +**83. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**84. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + +**85. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + +**86. [47. Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/47.%20Permutations%20II.java)** Level: Medium Tags: [Backtracking, DFS] + +space: O(n!) + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +Similar to 46. Permutations, but with dedup + +#### Backtracking +- Sort the list, and on same level, if last element is the same as curr, skip this recursive call +- time O(n!) + +#### 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]在重复时候, 不用重新记录. + + + +--- + +**87. [332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)** Level: Medium Tags: [Backtracking, DFS, Graph] + + +#### DFS with backtcking +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +- time: O(n^n). n = # of cities. worst case, each city has (n-1) edges and need to try all combinations +- space: O(n^2), there can at most be n * (n - 1) edges + + + +--- + +**88. [39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + + + +--- + +**89. [1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)** Level: Hard Tags: [DFS, Stack, String] + +#### Parse exp as sub problem +- Analyze the pattern: 1) single char, 2) with !, 3) with &, | +- Identify sub problem + - Use stack to parse the data in "()", which is a sub problem to solve with recursive call + - Handle &, | case: need to parse multiple +- Be comfortable with string parsing +- Slight improve: + - If see obvious result, directly return evaluation w/o further parsing + - use memo to store evaluated exp + +#### Evaluate inner exp and save back to Stack +- Use '(' and ')' to mark inner exp +- Evaluate the inner exp and save result back to Stack: the result will be 'f' or 't' +- This is slightly slow because: + - It requires all stack items on top to be processed before reaching the operator + - There is no room to optimize even there is simplification for specific operator + + + +--- + +**90. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**91. [110. Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/110.%20Balanced%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + +给一个binary tree, 看是否是height-balanced + +#### DFS with end state +- 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 above solution, but cost more traversal efforts +- 试图计算所有情况 + + + +--- + +**92. [100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### Method1: DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### Method2: BFS with 2 queues +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**93. [112. Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/112.%20Path%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. + + + + +--- + +**94. [1026. Maximum Difference Between Node and Ancestor.java](https://github.com/awangdev/LintCode/blob/master/Java/1026.%20Maximum%20Difference%20Between%20Node%20and%20Ancestor.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top-Down DFS +- cache parent max and min => produce current max and min +- pass the max and min to dfs +- compare and return the max of dfs(left), dfs(right) +- time: O(n) +- space: O(logn) +- easy to write, a bit hard to think of + +#### Method2: Bottom-up DFS +- pass up the local (min, max) as object `Val{max, min}` +- easy to think of, but more code to write + + + +--- + +**95. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**96. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + +**97. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**98. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**99. [114. Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/114.%20Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### Method1: DFS return tail node +- DFS to return the tail of the flattened list +- Careful handling the null child. Choose one and both works: + - Option1: put non-null as right child and continue dfs + - Option2: put non-null as left child and continue dfs + +#### Method2: void DFS +- latten the tree, no extra space. + - 1. Set right node in buffer, and connect: `root.right = root.left`, DFS flatten(root.right) + - 3. 移花接木: traverse to tail of the current root.right and attach the buffer node. + - 3. flatten the remaining of buffer + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**100. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + + +--- + +**101. [430. Flatten a Multilevel Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/430.%20Flatten%20a%20Multilevel%20Doubly%20Linked%20List.java)** Level: Medium Tags: [DFS, Linked List] + + +#### DFS +- Depth-first: + - 1) process curr.child, return tailChild + - 2) connect tailChild.next = curr.next +- function: link(Node a, Node b); + + + +--- + +**102. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**103. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + +**104. [364. Nested List Weight Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/364.%20Nested%20List%20Weight%20Sum%20II.java)** Level: Medium Tags: [DFS, NestedInteger] + + +#### Method1: DFS +- Build a list of NestedInt +- DFS: + - sum up integers in the list are integers + - dfs on nested list + - overallSum = sum * (depth+1) + - End state: if no nested list (no more child dfs), return depth 1 +- Parent level: sum up all ints and times the (depth+1) + + +#### Method2: BFS +- Using stack to flatten all nestedList, and process in the end +- Can actually use list, does not need to be stack. +- uses more memory + + + +--- + +**105. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + +**106. [133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%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: `map` + - 1) make new curr node; + - 2) clone all neibhors and add them +- Use the map to avoid visited nodes +- time: O(n). visit all nodes +- space: O(n). Technically only travels n levels/stacks to circle all nodes (undirected & connected) + +#### 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. + + + +--- + +**107. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**108. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**109. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**110. [1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS, top-down +- key concetpL the `common ancester of deppest leaves` must have its `two branch being same depth`. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +- time: O(n) traversal 1 pass +- space: O(n) dfs worst case depth + +#### Method2: bottom-up, find leaves first and traverse backwards +- 1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +- 2) use leaf nodes to find way backwards till common node is found; return +- time: O(n) but two passes +- space: O(n) dsf + map storage +- this approach is more brutle and uses exrtra spaces + + + +--- + +**111. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + +**112. [785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)** Level: Medium Tags: [BFS, DFS, Garph] + + +#### DFS marking each node with a state +- `bipartite` require each node to be in exact 1 party, which means it only has 1 state +- DFS to mark node with one state; and mark its edges as reversed state + - If any node state has been assigned by different from desired one, return false. + +#### BFS, Queue +- Use `Boolean states[i]` to represent visted & state +- Try all nodes with for loop, and skip visited nodes (similar validation rules as in dfs) +- In `int next : graph[curr]`, test next level first before adding. + + + +--- + +**113. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + +**114. [721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Union Find] + +- time O(mn) + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Method1: 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, 最终用来输出. + +#### Method2: 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) + +### DFS +- TODO + + + +--- + +**115. [101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### Method1: DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Method2: interative with queue +- put left or right children in pair + +#### Method3: iterative with Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**116. [698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)** Level: Medium Tags: [DFS, DP, Recursion] + + +#### Method1: Brutle DFS to find subsets and return results +- Target = total / k, fixed. +- DFS: Pick one num and dfs with the remaining nums for subproblem + - subproblem1: accumulate local sum to target + - EndState: accumulatedSum == target, continue with below + - subproblem2: after accumulatedSum == target, continue dfs with k-1 + - EndState: k == 0, return overall true +- Option1: DFS with nums, and boolean[] visited. Fast +- Option2: DFS with queue. + - Specially handling: dfs(size+1) to prevent `while(size-- >0)` from skipping last item at index 0 + + +#### Method2: DP +- the problem aims to find true/false capability +- bit-masking. TODO. The DP approach is kinda hard-level +- https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/335668/DP-with-Bit-Masking-Solution-%3A-Best-for-Interviews + + + +--- + +**117. [366. Find Leaves of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/366.%20Find%20Leaves%20of%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS: store nodes at the same depth +- the leaves are at depth 0 and the root is at highest depth +- dfs: the depth = index of the rst, start from depth = 0 at leaf +- end state: leaf node, add to rst, and return depth + + + + +--- + +**118. [235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的 path +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Beofre lasts common ancestor is found: p and q should follow same search pattern: + - compare with root, then dfs(left) or dfs(right) + - util p and q fall on two sides of root, then return root + - 非常巧妙, 但是也比较局限; 稍微变条件, 就很难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(logn) extra space: recursive stack space +- O(logn) time + + + +--- + +**119. [156. Binary Tree Upside Down.java](https://github.com/awangdev/LintCode/blob/master/Java/156.%20Binary%20Tree%20Upside%20Down.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS +- Given that left & right nodes are always available in pair, at each level: + - perform dfs to find new root: return deepest left node as root + - pick out curr left node and fix current level: + - add right node as left node + - add root as right node + + + +--- + +**120. [254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)** Level: Medium Tags: [BFS, Backtracking, DFS] + + + +#### Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs + + +#### Method2: BFS +- Check if the number can be devided by [2, sqrt(n)], return a list of possible factors. Only check till `Math.sqrt(n)` + - build suffixes: use the factor to devide last element of list and replace last element + - build candidate: replace last element of the queue item with new list of suffixes; add to rst + - add success item back to queue: in case last element can be simplified +- **remove dupilcates**: since we start factor from [2, sqrt(n)], the final factor list should be **ascending**!! +- time: O(x), x is the # of results +- space: O(y), y is all ongoing candidates in queue + + + +--- + diff --git a/review/DP.md b/review/DP.md new file mode 100644 index 0000000..7e3fd52 --- /dev/null +++ b/review/DP.md @@ -0,0 +1,2403 @@ + + + +## DP (94) +**0. [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 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 + + + +--- + +**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. [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. [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) + + + +--- + +**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. [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);` + + + +--- + +**6. [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 + + + +--- + +**7. [Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)** Level: Medium Tags: [DP] + +这个DP有点诡异. 需要斟酌。 +NOT DONE YET + + +--- + +**8. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**9. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + + +#### 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题,想不到,就是搞不出。 + + + + +--- + +**10. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**11. [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) + + + +--- + +**12. [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 + + + +--- + +**13. [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了。 + + + +--- + +**14. [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 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**15. [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) + + + +--- + +**16. [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) + + + + +--- + +**17. [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. + + + +--- + +**18. [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? + + + +--- + +**19. [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, 看解答 + + + +--- + +**20. [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作比较;求的所有情况的最大值嘛。 + + + +--- + +**21. [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), 太慢. + + + +--- + +**22. [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. + + + + +--- + +**23. [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 + + + +--- + +**24. [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) + + + +--- + +**25. [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 + + + +--- + +**26. [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] + + +给一串 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) + + + +--- + +**27. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**28. [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) + + + +--- + +**29. [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 + + + +--- + +**30. [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[] + + + +--- + +**31. [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? + + + +--- + +**32. [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, 节约时间复杂度. + + + +--- + +**33. [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] + + + + +--- + +**34. [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]。 + + + + + +--- + +**35. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**36. [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. + + + + +--- + +**37. [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 +还没有做 + + + +--- + +**38. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**39. [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看出有直接关系. + + + +--- + +**40. [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) + + + +--- + +**41. [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; + + + + +--- + +**42. [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 + + + + +--- + +**43. [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的状态, 然后看最后一步. + + + +--- + +**44. [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的理所应当。 + + + + +--- + +**45. [Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)** Level: Hard Tags: [DP] + + + +--- + +**46. [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] + + +跟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] + + + +--- + +**47. [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]) + + + +--- + +**48. [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` + + + +--- + +**49. [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 + + + +--- + +**50. [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 + + + +--- + +**51. [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; + + + +--- + +**52. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + +// 如何想到从中间initialize + + + +--- + +**53. [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. + + + +--- + +**54. [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的做法写出结果. + + + +--- + +**55. [198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +搜刮房子, 相邻的不能碰. 每个房子里有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虽然抽象, 但是更加实用. + +#### Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- calculation + - dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]). The prior house can be either state. + - dp[i][1] = dp[i - 1][0] + nums[i]. The prior house must be `NOT ROBBED`. +- return `Math.max(dp[n - 1][0], dp[n - 1][1])` + + + +--- + +**56. [301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)** Level: Hard Tags: [BFS, DFS, DP] + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- Goal: identify invalid parentheses and remove (minimum removals) +- Step: + - Detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` + - When invalid occurs: + - chance for correction. Remove the incorrect parentheses, one at a time + - dfs on the rest of the s that has not been tested yet: start index from index i + - Core edge cases: + - Do not correct twice of the same parenthesis by checking [j-1] pos + - Make sure to attempt correction of all possible parenthesis within tested range: because it outputs all results at the same level + - return/finish once correction done +- Success case: + - a string s passed test: make sure it passes REVERSED string test! + - Core Concept: `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**. +- How does it guarantee minimum removals? + - When seeing a chance to correct, it jumps into a for loop of DFS. It `return` after the for loop. This stops additional testing + - When invalid occurs, correct it right away: minimum correction +- Complexity: + - O(nk), k being the # of recursive calls. It takes n calls to finish a full string case. + +#### BFS +- Similar to DFS, we wnat to test: 1) test input s valid, 2) remove 1 invalid parenthesis at a time, 3) process substring +- instead of testing all substrings (timeout), we want to establish rules to improve reprocess: + - Test1: skip regular char. No need to test it. + - Test2: if redundant paren, do 1 is enough. skip adjacent ones. + - Test3: if last removed extra paren is '(', the next ')' must be a valid pair. LastRemoved char: pecial handling by using a struct: `class Node {String s, int index, char lastRemoved}` +- How to end tests? When there is data in rst, stop adding to queue. + + + +--- + +**57. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**58. [5. Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/5.%20Longest%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)? + +#### Method1: DP of interval +- Very similar to `216. Longest Palindromic Subsequence`, but this problem requires solid substring(i+1, j-1) to be palindromic +- Similarly: process i = n-1, from end so [i + 1, j] is always ready to consume +- boolean dp[i][j] to mark range (i, j) as palindrome or not. +- 在计算 dp[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- time: O(n^2) dp +- space: O(n^2) + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + + + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**59. [303. Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/303.%20Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**60. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**61. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**62. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**63. [518. Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/518.%20Coin%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 + + + +--- + +**64. [509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.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. + + + + +--- + +**65. [221. Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/221.%20Maximal%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; +- init: 每个点都可能是边长1, 如果 matrix[i][j] == '1' +- Space, time O(mn) +- Rolling array: [i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + + +--- + +**66. [1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)** Level: Medium Tags: [Bucket Sort, DP, Hash Table, Sort] + + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**67. [62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +#### DP, 加法原理 +- 计数DP: 2 ways to reach (i,j): dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + - non-overlapping: `dp[i - 1][j]`, `dp[i][j - 1]` + - covers the only 2 possible way to reach (i,j) +- initialization: dp[i][0] = 1, dp[0][i] = 1 + - Of course, row i = 0, or col j = 0, there is only 1 way to reach +- time O(mn), space O(mn) + +##### 滚动数组 Rolling Array +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + +#### DFS + Memoization +- move from (0,0) towards (m, n) +- use Map as memoization technique + + + +--- + +**68. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + +**69. [55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%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 +- 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) +- space: O(1) + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (j + A[j] >= i), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- time: O(n^2) +- space: O(n) + + + + +--- + +**70. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**71. [741. Cherry Pickup.java](https://github.com/awangdev/LintCode/blob/master/Java/741.%20Cherry%20Pickup.java)** Level: Hard Tags: [DFS, DP] + + +special hint: `r1 + c1 = constant t = r2 + c2`, if the two points are moving at same time. + +#### DFS + Memo: TOP-DOWN +- Similar concept to Minimum Path Sum +- https://leetcode.com/problems/cherry-pickup/solution/ +- realize r1 + c1 = r2 + c2. Knowing 3 parameters can uniquely identify the 4th. +- assume there are 2 people starting from origin, and the 2 people can go total 4 directions + - perform DFS based on the 4 directions + - concern: do they visit the same spot? possible. when that happens, make sure we do not double count the grid[i][j] +- when is the end state? + - then anyone, for example, (r1,c1) reaches end (n-1, n-1). + - it means the other person also reaches end +- use memo: memo[r1][c1][r2], it records any given (r1, c1, r2, c2) state + + + + +--- + +**72. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**73. [70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%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]往往是有特殊状态的. 这里, dp[0]: 1种方式可以原地不动 + - dp[1]=1, 1种方式到达index=1, + - 之后的`dp[2] = dp[0]+dp[1]`: 就是dp[0]的走法 or dp[1]的走法 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**74. [10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + + + +--- + +**75. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**76. [91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Method1: DP, Bottom-Up by calculating base case first +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- there can be 2 final states at dp[i]: single digit or double digit. + - validate if `single digit`, dp[i] += dp[i - 1]. Last 1 digit can be decoded. + - validate if `double digit`, dp[i] += dp[i - 2]. Last 2 digits can be decoded. +- note: + - get number from char: `c - '0'` + - validatae single digit != '0', 因为'0' 不在条件之中(A-Z) +- Option1: dp[i] as # ways to decode at index i, including letter i + - The validation is done on: 1) single digit i, or 2) double digit [i-1, i] +- Option2: Partition DP, dp[i] as # ways to decode for first i letters (excluding letter i) + - 定义`dp[i] = 前i个digits最多有多少种decode的方法`: new dp[n + 1]. + - dp[i] += dp[i - x], where x = 1, 2 + - The validation is done on: 1) single digit [i-1], or 2) double digit [i-2, i-1] + - Option2 is better in terms of clean coding. We assume `dp[0]=1` as 1 way to decode 0 digits. + - No need to specially handle length == 1, because it is covered later + - No need to manualy init the first 2-digit case as in Option1 + - Return of `dp[n]` is clean +- 引申: 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +#### Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 + - especially when i = n - 2, handle 2-digt edge case carefully: + - 1) check if two-digit range [i, i+1] is valid + - 2) check if single-digit [i] is valid; if so, += dfs(s, i + 1) +- memo[i]: # ways to decode from [i, n). init with `memo[i]=-1` + + + +--- + +**77. [639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%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]) +- Enumeration: + - 具体分析 '*' 出现的位置, 枚举出数字, 基本功. + - 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) + - 枚举好以后, 其实这个题目的写法和思考过程都不难 +- Mode: + 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. + + +#### DFS + memoization +- DFS top-down approach is used to analyze the problem. The logic flow: +- 1) consider the case of 1 letter or 2 letters. +- 2) one letter: + - [*]: + 9 * dfs(s, i + 1) + - [0~9]: + dfs(s, i + 1) +- 3) two letters: + - [_, *]: depends + - [*, _]: depends + - [*, *]: + 15 * dfs(s, i + 2) +- memo[i] records # of ways to decode from [i ~ n] +- space: O(n), Size of recursion tree can go upto n +- time: O(n), `memo array is filled exactly once`!!! + + + +--- + +**78. [64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%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(N) + - 1) 需要在同一个for loop里面完成initialization, 2) reuse dp[i][j] + - Reason: dp[i % 2][j] 在被计算出来的时候, 马上在下一轮会被用. 被覆盖前不用,就白算 + - Option3 below initialize dp outside of loop: 看起来固然简单, 但是不方便空间优化 + +#### DFS (top-down) Thinking process +- Enumerate the possible 2 paths: go right, go down +- sub problem: dfs(i+1,j), dfs(i,j+1); pick the min of the two +- memoization: after the path from a point (i,j) to end is computed, record memo[i][j] to avoid repatative calculation +- time: O(mn), only visite and record memo[i][j] once. +- space: O(mn) extrem case of m=100000, n = 2; the stack height is O(mn) + + +--- + +**79. [121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### min[n] +- 每天都就交易价格,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 dont need to buyin at 5, 3, 6, 4 later on; + - only need to sell on higher prices. + + + +--- + +**80. [304. Range Sum Query 2D - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/304.%20Range%20Sum%20Query%202D%20-%20Immutable.java)** Level: Medium Tags: [DP, PreSum] + + +#### Method1: rangeSum/caching +- build rangeSum[i][j]: square range sum from (0,0) to (i,j), O(mn) to init +- query: time O(1) + +#### Method2: presum/caching +- build rowPreSum[i][j]: row i sum from [0 ~ j], O(mn) to init +- callign takes O(m); space O(mn) + + + + +--- + +**81. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + + +--- + +**82. [63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%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] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + + + +--- + +**83. [139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- Bottom-up, test simply case. Sequence DP. +- true/false problem, think about dp + - 子问题: 前i个字母, 是否可以有valid break + - check: 1) dp[j] && 2) `if substring(j, i) valid`, for all j = [0 ~ i] + - dp = new boolean[n + 1]; dp[0] = true; + - test: `dp[i] |= dp[j] == true && word[j, n] in dict`. + - Need iterate over i = [0 ~ n], also j = [0, i] + - When there is a way to make dp[i] == true, then break the [j ~ i] loop, move on to test dp[i++] +- Use set dict: `dict.contains()` +- Improvement: O(n) to figure out max length, so we can skip some substring[j~i] dict.contains() +- overall O(n^2) time since the double for loop + +#### DFS +- Top-Down, break into small problems: Check front subString, and put the rest substring into dfs to test +- Memoization: for tested failed substring, record and do NOT test them again. +- Same Improvement as in DP: use max/min length of dict words as boundary + + + +--- + +**84. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + +**85. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + +**86. [361. Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/361.%20Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Method1: 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 的变换. + +#### Method2: Analyze, col count array: +- Inspired by: https://leetcode.com/problems/bomb-enemy/discuss/83387/Short-O(mn)-time-O(n)-space-solution +- Analyize the problem: need to add up 2 directions at any given point. + - Notice 1: if I traverse row by row, each colSum at a specific col j is likely to be the same + - Unless there is a Wall in last row, so we have to calclate the col sum starting from current row, below the Wall + - Notice 2: for row it is the same: + - If I in a new spot row[i][j], where (i-1) is Wall, i need to sum row from 0 +- we will process each point: + - process row by row and add up row sum + - buffer col[j] in an array vertically so we can resue + - make sure to recalculate row sum or col sum if last row index or last col index is Wall +- time: O(mn) traversal +- space: O(n) only use a column array + + + +--- + +**87. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + +**88. [689. Maximum Sum of 3 Non-Overlapping Subarrays.java](https://github.com/awangdev/LintCode/blob/master/Java/689.%20Maximum%20Sum%20of%203%20Non-Overlapping%20Subarrays.java)** Level: Hard Tags: [Array, DP] + + +#### DP, Divide and conquer +- split into 3 parts [0, i -1], [i, i + k -1]. [i + k, n - 1] +- NOTE: be very careful about index handling: + - `presum[i + 1] - presum[0]` gives inclusive range of `[0, i]` +- Use DP to record the starting position of max sum, +- inspired by: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C%2B%2BJava-DP-with-explanation-O(n) + - 1) calculate preSum with range [0, n] + - 2) calculate leftMaxIndex[], rightMaxIndex[] + - 3) test middle range to find max solution +- Note: the test range for 1, 2, 3 always start with assumption that k has been consumed from one side +- Note: When need to record at max/min value change, we can check/assign it manually (rather than use a object to carry & sort) + + + + +--- + +**89. [698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)** Level: Medium Tags: [DFS, DP, Recursion] + + +#### Method1: Brutle DFS to find subsets and return results +- Target = total / k, fixed. +- DFS: Pick one num and dfs with the remaining nums for subproblem + - subproblem1: accumulate local sum to target + - EndState: accumulatedSum == target, continue with below + - subproblem2: after accumulatedSum == target, continue dfs with k-1 + - EndState: k == 0, return overall true +- Option1: DFS with nums, and boolean[] visited. Fast +- Option2: DFS with queue. + - Specially handling: dfs(size+1) to prevent `while(size-- >0)` from skipping last item at index 0 + + +#### Method2: DP +- the problem aims to find true/false capability +- bit-masking. TODO. The DP approach is kinda hard-level +- https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/335668/DP-with-Bit-Masking-Solution-%3A-Best-for-Interviews + + + +--- + +**90. [416. Partition Equal Subset Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/416.%20Partition%20Equal%20Subset%20Sum.java)** Level: Medium Tags: [Backpack, DP] + +#### Backpack DP +- the problem turns into: can we find a subset of items that sum up to target sum? +- create `boolean dp[j]` to represent if we can sum up to j, where j = sum value + - want to try out all items in num, + +#### DFS +- use dfs to find a subset of items that sum up to target sum? + + + +--- + +**91. [256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, depends on the color of dp[n-1] + - 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- Need to have status with dp array: int[index][color status] + - dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. + - dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- time: O(nm), m = # of colors +- space: O(nm) + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 +- space:O(1) + + + +--- + +**92. [265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + + +一排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) + + + +--- + +**93. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + diff --git a/review/Deque.md b/review/Deque.md new file mode 100644 index 0000000..fb9a04d --- /dev/null +++ b/review/Deque.md @@ -0,0 +1,72 @@ + + + +## Deque (2) +**0. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**1. [239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Method1: Deque, Monotonous queue +- 维持monotonuous queue: `front is always at max` and the `tail end is min`. Always need to return the max end of queue. +- when adding new elements x: + - 1) start from small-end of the queue + - 2) drop all smaller elements + - 3) append to the ending element that is larger than x. + - This is to maintain a front->tail decreasing queue +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`: better than using arraylist, since DeQueue(linked list) removes at O(1) cost +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +- Option1: sliding window template using right/left + while loop + - 1) tailing the new number to max queue, if applicable + - 2) process: record max + - 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] +- Option2: same concept, but use index `i` to mark right, and `i - k + 1` to mark left. +- time: O(n), one pass +- space: O(k), store the deque + + +#### Method2: Heap +- can always build a `class Node{index, val}`; and sort them with PQ of size k +- time: O(nlogK) +- space: O(k) +- this is not linear time, not as good as method1 + + + +--- + diff --git a/review/Design.md b/review/Design.md new file mode 100644 index 0000000..9c2c913 --- /dev/null +++ b/review/Design.md @@ -0,0 +1,519 @@ + + + +## Design (27) +**0. [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个数字 + + + +--- + +**1. [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 + + + +--- + +**2. [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 一样) + + + +--- + +**3. [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()里, 每次换水,查看末尾项. + + + + +--- + +**4. [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). + + + + +--- + +**5. [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,可惜错了。 + + + + +--- + +**6. [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 + + + +--- + +**7. [Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)** Level: Easy Tags: [Design] + +让一个class 是 singleton + + + +--- + +**8. [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,也是类似做法 + + + +--- + +**9. [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] + + +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 +- + + + +--- + +**10. [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本身. + + + +--- + +**11. [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了. + + + + +--- + +**12. [359. Logger Rate Limiter.java](https://github.com/awangdev/LintCode/blob/master/Java/359.%20Logger%20Rate%20Limiter.java)** Level: Easy Tags: [Design, Hash Table] + + +#### HashMap +- map: avoid duplicate message, records timestamp for validation +- time: O(1) +- space: O(n) + +#### Queue + Set +- 1) keep a trimmed queue and set (all tasks to be within 10 secs); +- 2) use set to O(1) check if incoming message exists. +- time: O(x), trimQueue() +- space: O(n) + + + +--- + +**13. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**14. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**15. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**16. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**17. [211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +#### Trie, prefix tree. +- Trie Structure: `boolean isEnd`, `HashMap children` + - trie.addWord: 没node就加,有node就移动 + - trie.search: 没node就return false,有node就移动 +- Alternatively, the hash can be `TrieNode[26]` a fixed size array when applicable + - I like map better for the simplicity to write (w/o converting char -> index) + + + + +--- + +**18. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**19. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**20. [170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)** Level: Easy Tags: [Design, Hash Table, Memoization] + + +#### Hash Table, Memo +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +- time: O(n), loop over all elements in map +- space: O(n), store all elements in map & memoization set + + + +--- + +**21. [432. All One Data Structure.java](https://github.com/awangdev/LintCode/blob/master/Java/432.%20All%20One%20Data%20Structure.java)** Level: Hard Tags: [Design, Doubly Linked List] + + +#### Doubly Linked List +- IMPORTANT: the problem aims to put keys of same frequency in same node! This affects the design of node +- Main a class `Node {keySet, count, last/next pointers}` +- Each operation: + - 1) finds target node and extract the key + - 2) calculate: count +/- 1 + - 3) find new spot to store the key (prior positions or later positions) +- Be careful when handling the cases in inc() and dec() + + + +--- + +**22. [380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + + +#### Hash Table, Swap when removing +- Map: `map, Lis: tracks `index->value` +- list maintain 用来 insert/remove/random operations. +- Remove: swap input valueIndex & tialIndex = list.size() -1. + - list.remove(object) 应该是要O(logn) 做一个search的. + - list.remove(list.size() - 1) is cheapter + + + +--- + +**23. [346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison +- Note: if we it is calculate moving-window-product, better to use deque :) +- Sliding window? + - It has the spirit of slinding window: 1) maintain a range; 2) check range size `if (queue.size() > size)` + - Though, the solution must use a data structure to store data; it is not the traditional sliding window type of `left/right` pointer problem + + + +--- + +**24. [208. Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/208.%20Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- Trie Structure: + - trace the char to children node: Map + - boolean isEnd to indicate if there is string end with this node + - `TrieNode {boolean isEnd; Map children}`. +- No need to store letter c in TrieNode +- 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。 + + + + + +--- + +**25. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + +**26. [341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, NestedInteger, Stack] + + +#### Method1: Stack holds items from back of the list +- Option1: always set integer on top of the stack everywhere + - if not, poping stack until the top is integer + - code is easy +- Option2: in hasNext(), faltten the list in stack + +#### Method2: DFS preprocessing. +- 用queue to store all items. Kinda hack. Defeat the purpose of the problem. +- Super fast to query next(), however, needs to holds everything in memory +- O(n) + + + +--- + diff --git a/review/Divide and Conquer.md b/review/Divide and Conquer.md new file mode 100644 index 0000000..a67e9b9 --- /dev/null +++ b/review/Divide and Conquer.md @@ -0,0 +1,962 @@ + + + +## Divide and Conquer (38) +**0. [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 + + + +--- + +**1. [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,要乘一下。 + + + + +--- + +**2. [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 + + + +--- + +**3. [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 + + + +--- + +**4. [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 + + + +--- + +**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. [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)) + + + + +--- + +**7. [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 + + + +--- + +**8. [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/ + + + +--- + +**9. [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. + + + +--- + +**10. [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加上去就好. + + + +--- + +**11. [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) + + + +--- + +**12. [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了 + + + +--- + +**13. [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? + + + +--- + +**14. [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 一样 + + + +--- + +**15. [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. + + + + +--- + +**16. [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一下。 + + + +--- + +**17. [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 + + + + +--- + +**18. [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 +- 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 + + + +--- + +**20. [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 + + + +--- + +**21. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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, 自底向上建立起的。 + + + +--- + +**22. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**23. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**24. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**25. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**26. [1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +- time: O(n), visit all nodes +- space: O(logn), height of the tree + +#### Method2: HashMap, DFS. +- traverse tree and create `map ` to fast O(1) removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- This approach requires a dfs build of parentMap + - it is same amount of efforts to do the regular dfs removal. + - not a good solution +- time: O(n) +- space: O(n) + + + +--- + +**27. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**28. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**29. [973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)** Level: Medium Tags: [Divide and Conquer, Heap, Sort] + + +#### PriorityQueue +- Create customized Point{} class +- Sort by distance +- Maintain queue size <= K + +#### Divide and Conquer +- ?, select sort? + + + +--- + +**30. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**31. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**32. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**33. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**34. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**35. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**36. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**37. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high 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..f6d2830 --- /dev/null +++ b/review/Double Sequence DP.md @@ -0,0 +1,149 @@ + + + +## 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. [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), 太慢. + + + +--- + +**2. [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 + + + +--- + +**3. [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] + + + + +--- + +**4. [10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + + + +--- + +**5. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + diff --git a/review/Doubly Linked List.md b/review/Doubly Linked List.md new file mode 100644 index 0000000..66bd104 --- /dev/null +++ b/review/Doubly Linked List.md @@ -0,0 +1,65 @@ + + + +## Doubly Linked List (3) +**0. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**1. [432. All One Data Structure.java](https://github.com/awangdev/LintCode/blob/master/Java/432.%20All%20One%20Data%20Structure.java)** Level: Hard Tags: [Design, Doubly Linked List] + + +#### Doubly Linked List +- IMPORTANT: the problem aims to put keys of same frequency in same node! This affects the design of node +- Main a class `Node {keySet, count, last/next pointers}` +- Each operation: + - 1) finds target node and extract the key + - 2) calculate: count +/- 1 + - 3) find new spot to store the key (prior positions or later positions) +- Be careful when handling the cases in inc() and dec() + + + +--- + +**2. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + diff --git a/review/Edge Case.md b/review/Edge Case.md new file mode 100644 index 0000000..405d36a --- /dev/null +++ b/review/Edge Case.md @@ -0,0 +1,36 @@ + + + +## Edge Case (2) +**0. [686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)** Level: Easy Tags: [Basic Implementation, Edge Case, String] + +Track: 纸上分析edge case. +Validation helps speed it up. + + + +--- + +**1. [41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)** Level: Hard Tags: [Analysis, Array, Edge Case] + + +给一串无序数字, 有负数: 找这个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 +1. 如果nums==null, 其实missing positive integer 自然而然是 1 +1. 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) + - 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +1. 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + diff --git a/review/Enumeration.md b/review/Enumeration.md new file mode 100644 index 0000000..f4c81b6 --- /dev/null +++ b/review/Enumeration.md @@ -0,0 +1,311 @@ + + + +## 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. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**3. [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) + + + +--- + +**4. [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走过的地方 + + + +--- + +**5. [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 + + + +--- + +**6. [36. Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/36.%20Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited row/col/block. + - 在nest for loop里面validate row,col,and block. + - Special: validate block要利用i 和 j 增长的规律 +- i, j are [0~n) can build block boundary in a 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 +- 可能代码稍微复杂一点 + + + +--- + +**7. [158. Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/158.%20Read%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就好了. +- alaternatively: use queue to hold so we do not need to worry about size + + + +--- + +**8. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**9. [246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math, Two Pointers] + + +根据题意枚举, 再根据规则basic implementation + +#### HashTable + Two Pointer +- compare left/right + +#### Alter input +- flip number (6 and 9), and then reverse the string, see if the string is the same. +- takes more + + + + +--- + +**10. [639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%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]) +- Enumeration: + - 具体分析 '*' 出现的位置, 枚举出数字, 基本功. + - 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) + - 枚举好以后, 其实这个题目的写法和思考过程都不难 +- Mode: + 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. + + +#### DFS + memoization +- DFS top-down approach is used to analyze the problem. The logic flow: +- 1) consider the case of 1 letter or 2 letters. +- 2) one letter: + - [*]: + 9 * dfs(s, i + 1) + - [0~9]: + dfs(s, i + 1) +- 3) two letters: + - [_, *]: depends + - [*, _]: depends + - [*, *]: + 15 * dfs(s, i + 2) +- memo[i] records # of ways to decode from [i ~ n] +- space: O(n), Size of recursion tree can go upto n +- time: O(n), `memo array is filled exactly once`!!! + + + +--- + +**11. [68. Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/68.%20Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- greedy approach: line up as many words as possible; once exceed the MaxLength, justify the list of words +- Steps + - 1) Split & group + - 2) Juststify a row of words + - 3) clean up last row +- Calcualte bounded row length = `width + (list.size() - 1)`. `list.size()-1` = Minimum amount of slot/space used. +- Calculate max ave spaces ot insert into each slot = `totalExtraSpace/slot` +- `Juststify a row of words`: + - 1) take a list of words and assume minimum 1 space in-between words + - 2) distribute the remaining spaces evenly to each slot +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**12. [157. Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/157.%20Read%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)` +- Edge Case: + - When there is not enough space to the result buffer, `i + 3 > n`, then only copy what we can: `Math.min(n - i, count)` + - `count < 4` meaning there is not enough content to read, break + + + +--- + +**13. [273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%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来找到结果, 每段的处理方法都是一样的 +- Note: + - StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 + - 注意加 " " 的时候, 如果多余, 要`trim()` + - 注意, `小于20的数字, 有自己的特殊写法, 需要额外handle` + - 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 +- Thinking process: + - `1 ~ 19`: [one, two ... nine, ten, eleven, ...., ninteen] + - `20 ~ x0`: [twenty, thirty, fourty, ... ninety] + - `x00`: hundred: 100 + - thousand: 10^3 + - million: 10^6 + - billion: 10^9 + - trillian: 10^12 way over 2^31, not needed +- plan: + - parse 3 digits at a time + - convert the 3 digit to [xx hundred xx-ty x] + - come up with a string[] + - insert the thousands/million/billion to the string[] + + + +--- + +**14. [65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + diff --git a/review/Expression Tree.md b/review/Expression Tree.md new file mode 100644 index 0000000..9d856eb --- /dev/null +++ b/review/Expression Tree.md @@ -0,0 +1,96 @@ + + + +## Expression Tree (5) +**0. [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, 但是数字可为多位 + + + +--- + +**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. [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. [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` + + + +--- + +**4. [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就可以了. + + + +--- + diff --git a/review/Game Theory.md b/review/Game Theory.md new file mode 100644 index 0000000..58324bc --- /dev/null +++ b/review/Game Theory.md @@ -0,0 +1,138 @@ + + + +## Game Theory (4) +**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. [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. [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 + + + +--- + +**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/Garph.md b/review/Garph.md new file mode 100644 index 0000000..2ac415d --- /dev/null +++ b/review/Garph.md @@ -0,0 +1,21 @@ + + + +## Garph (1) +**0. [785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)** Level: Medium Tags: [BFS, DFS, Garph] + + +#### DFS marking each node with a state +- `bipartite` require each node to be in exact 1 party, which means it only has 1 state +- DFS to mark node with one state; and mark its edges as reversed state + - If any node state has been assigned by different from desired one, return false. + +#### BFS, Queue +- Use `Boolean states[i]` to represent visted & state +- Try all nodes with for loop, and skip visited nodes (similar validation rules as in dfs) +- In `int next : graph[curr]`, test next level first before adding. + + + +--- + diff --git a/review/Geometry.md b/review/Geometry.md new file mode 100644 index 0000000..7cdd058 --- /dev/null +++ b/review/Geometry.md @@ -0,0 +1,39 @@ + + + +## 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. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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..1bf846b --- /dev/null +++ b/review/Graph.md @@ -0,0 +1,430 @@ + + + +## Graph (20) +**0. [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. + + + +--- + +**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. [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 + + + +--- + +**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)** 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 + + + +--- + +**4. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + +--- + +**5. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**6. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**7. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**8. [1153. String Transforms Into Another String.java](https://github.com/awangdev/LintCode/blob/master/Java/1153.%20String%20Transforms%20Into%20Another%20String.java)** Level: Hard Tags: [Graph] + + +#### Graph +- analysis: + - 1) should not have mult-origin cases: 1 char maps to 1 char at maximum + - 2) need a buffer char NOT exist in target to hold inter-media transformation + - check open char (out of 26 lower letter) that is NOT in target chars +- impl the validation rules +- more to read in https://leetcode.com/problems/string-transforms-into-another-string/discuss?currentPage=1&orderBy=most_votes&query= + + + +--- + +**9. [1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph] + + + +#### BFS +- simply calc each level sum with BFS +- top-level is processed first, since we go from top level -> deeper level + - only update result if sum is truly > global MAX. + + + +--- + +**10. [1306. Jump Game III.java](https://github.com/awangdev/LintCode/blob/master/Java/1306.%20Jump%20Game%20III.java)** Level: Medium Tags: [BFS, Graph] + + +### Method1: BFS +- Find possibility to reach certain point, we can BFS: faster to find shortest candidate +- use queue to hold left, right candidates +- use set to record visited + +### Method2: DFS +- attemp all nodes, use set to record visited. +- time: O(n) +- space: O(n) + + + +--- + +**11. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + +**12. [332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)** Level: Medium Tags: [Backtracking, DFS, Graph] + + +#### DFS with backtcking +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +- time: O(n^n). n = # of cities. worst case, each city has (n-1) edges and need to try all combinations +- space: O(n^2), there can at most be n * (n - 1) edges + + + +--- + +**13. [1267. Count Servers that Communicate.java](https://github.com/awangdev/LintCode/blob/master/Java/1267.%20Count%20Servers%20that%20Communicate.java)** Level: Medium Tags: [Array, Graph] + + +#### two axis array, cross-reference +- analyze problem, and realize we want to eliminate `isolated servers` +- count row[], count col[] +- cross-reference row[] and col[]: `row[i]==1 & col[j]==1` indicates a isolated server + +### DFS brutle +- Unfortunately, this problems checks unconnected items, so dfs needs to brutlely check entire row or column +- Only add if `vertical + horizontal count` > 1 +- time: O(mn) * O(m + n) + + + +--- + +**14. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + +**15. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**16. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + +**17. [133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%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: `map` + - 1) make new curr node; + - 2) clone all neibhors and add them +- Use the map to avoid visited nodes +- time: O(n). visit all nodes +- space: O(n). Technically only travels n levels/stacks to circle all nodes (undirected & connected) + +#### 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. + + + +--- + +**18. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**19. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + diff --git a/review/Greedy.md b/review/Greedy.md new file mode 100644 index 0000000..17b3ddb --- /dev/null +++ b/review/Greedy.md @@ -0,0 +1,572 @@ + + + +## Greedy (24) +**0. [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 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 + + + +--- + +**1. [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哪一个。 + + + +--- + +**2. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**3. [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 + + + +--- + +**4. [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. + + + +--- + +**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] + + + + +--- + +**6. [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 + + + +--- + +**7. [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] + + +跟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] + + + +--- + +**8. [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]) + + + +--- + +**9. [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 + + + +--- + +**10. [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. + +可能有简化的余地, 代码有点太长. +比如试一试不用额外空间? + + + +--- + +**11. [1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)** Level: Medium Tags: [Array, Greedy, Permutation] + + +#### Analyze Permutation behavior +- concept similar to `31. Next Permutation` +- 1) first pass: find the one that is in incorrect order +- 2) second pass: find the right spot to swap + + + +--- + +**12. [1007. Minimum Domino Rotations For Equal Row.java](https://github.com/awangdev/LintCode/blob/master/Java/1007.%20Minimum%20Domino%20Rotations%20For%20Equal%20Row.java)** Level: Medium Tags: [Array, Greedy] + + + +#### Method1: Count all occurrance, and count on overlap indexes +- when there is a value that can cover entire row of size n + - it must be: `n = countA[i] + countB[i] - overlap[i]` +- Code easy to write and read +- time: O(n) +- space: O(1) + +#### Method2: Negative count +- Observation: if A[0] works, no need to check B[0]. +- Because if both A[0] and B[0] exist in all dominoes, + - when you swap A[0] in a whole row, + - you will swap B[0] in a whole at the same time. + - The result of trying A[0] and B[0] will be the same. +- time: O(n) +- space: O(1) + +#### Method3: positive count Match +- there should exist 1 numbers, that can appear in (A[i], B[i]). +- failure case: there exist at least 1 index, that does not have the common number +- maximum case: there can be 2 numbers, that both will make it work. +- findCommon2, and count them: + - set.add(A[0], A[B]), + - if any new one does not exist in set, remove it from set + - if set is empty() , return -1 +- use the 2 numbers from set to do a sweep and count in A, O(n), return the less appearance one. +- time: O(n) +- space: O(1) + + + +--- + +**13. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**14. [55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%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 +- 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) +- space: O(1) + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (j + A[j] >= i), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- time: O(n^2) +- space: O(n) + + + + +--- + +**15. [134. Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/134.%20Gas%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 +- 而不是求: 最后点可否走完/最值/计数 + + + +--- + +**16. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + +**17. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**18. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**19. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**20. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**21. [605. Can Place Flowers.java](https://github.com/awangdev/LintCode/blob/master/Java/605.%20Can%20Place%20Flowers.java)** Level: Easy Tags: [Array, Greedy] + + +#### Array +- just check flowerbed[i-1] & flowerbed[i+1] and count + + + +--- + +**22. [402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)** Level: Medium Tags: [Greedy, Monotonous Stack, Stack] + + +#### Monotonous Stack (Increasing) +- Greedy: Remove 1) earlier digits(数位靠前权值大), 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. + + + + +--- + +**23. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + diff --git a/review/Hash Table.md b/review/Hash Table.md new file mode 100644 index 0000000..64d413d --- /dev/null +++ b/review/Hash Table.md @@ -0,0 +1,1855 @@ + + + +## Hash Table (86) +**0. [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 + + + +--- + +**1. [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 + + + +--- + +**2. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + + +给一串点, 找是否有一个所有点中间的, 跟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) + + + +--- + +**3. [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)}` + + + +--- + +**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. [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. + + + +--- + +**6. [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). + + + + +--- + +**7. [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? + + + +--- + +**8. [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 + + + +--- + +**9. [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. + + + +--- + +**10. [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 +- 都是基本操作, 概念实现 + + + +--- + +**11. [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) + + + +--- + +**12. [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? + + + +--- + +**13. [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 +还要做一下那. + + + +--- + +**14. [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' + + + + +--- + +**15. [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的益处。 + + +--- + +**16. [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) + + + +--- + +**17. [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] + + +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 +- + + + +--- + +**18. [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] + + +#### 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. + + + +--- + +**19. [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` + + + +--- + +**20. [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本身. + + + +--- + +**21. [Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)** Level: Medium Tags: [Hash Table] + + +给一面墙, 每一行是一行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 + + + +--- + +**22. [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. + + + +--- + +**23. [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 + + + +--- + +**24. [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一下。 + + + +--- + +**25. [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) + + + +--- + +**26. [[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)** Level: Easy Tags: [Hash Table, Lint, String] + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**27. [[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)** Level: Medium Tags: [Array, Hash Table, Lint] + + +把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. + + + + +--- + +**28. [[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Lint, 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! + + + + +--- + +**29. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**30. [[tool]. Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Hash%20Function.java)** Level: Easy Tags: [Hash Table, Lint] + + +In general, there is no universal recipe to stick to when it comes to implementing hashCode(). +https://www.baeldung.com/java-hashcode + +#### 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会变得太大,所以不能算完和 再 %... + + + +--- + +**31. [36. Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/36.%20Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited row/col/block. + - 在nest for loop里面validate row,col,and block. + - Special: validate block要利用i 和 j 增长的规律 +- i, j are [0~n) can build block boundary in a 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 +- 可能代码稍微复杂一点 + + + +--- + +**32. [359. Logger Rate Limiter.java](https://github.com/awangdev/LintCode/blob/master/Java/359.%20Logger%20Rate%20Limiter.java)** Level: Easy Tags: [Design, Hash Table] + + +#### HashMap +- map: avoid duplicate message, records timestamp for validation +- time: O(1) +- space: O(n) + +#### Queue + Set +- 1) keep a trimmed queue and set (all tasks to be within 10 secs); +- 2) use set to O(1) check if incoming message exists. +- time: O(x), trimQueue() +- space: O(n) + + + +--- + +**33. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**34. [953. Verifying an Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/953.%20Verifying%20an%20Alien%20Dictionary.java)** Level: Easy Tags: [Hash Table] + + +#### Hash Table +- mark the char position +- check adjacent words +- Optimization + - a) If s1 equals s2, just return true, no need to continue. + - b) if s2 (app) is a substring of s1(apple), just return false. + + + + +--- + +**35. [1213. Intersection of Three Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/1213.%20Intersection%20of%20Three%20Sorted%20Arrays.java)** Level: Easy Tags: [Hash Table, Two Pointers] + + +Very similar to 349.Intersection of Two Arrays. + +#### Hash Table +- Use set to check +- Verify duplicates at end rst + +#### Two Pointers +- similar to Intersection of Two Sorted Arrays +- Start from front/back, process 1 item at a time + - if match, move all pointers +- Optoin1: check from back +- Optoin2: check from frotn + + + +--- + +**36. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**37. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**38. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**39. [204. Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/204.%20Count%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个数就好了 + + + +--- + +**40. [496. Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/496.%20Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack +- use stack to hold all elements + - keep poping if `stack.peek() < num` + - use map to record (top, num) +- time O(n), run through base once and sub-sequence once +- space O(n), stack, map + +#### HashMap +- O(n) space, O(n^2) time worst case + + + +--- + +**41. [694. Number of Distinct Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/694.%20Number%20of%20Distinct%20Islands.java)** Level: Medium Tags: [DFS, Hash Table] + + +#### DFS + HashSet +- DFS can find # of island, just like `200.Number of Islands`, aim to count total +- We need to map same-shap land + - One approach: print the **footprint** starting from coordinate (0, 0) + - Another approach: print the actual island in its boundary, like a QR code. (too hard to code, skip) +- Footprint approach: + - 1. always assume a newly found islands starts from (0, 0) + - 2. take 4 direction from init pos and keep printing the footprint + - 3. Since we always visit nodes from top->right->nextrow, we always visit top-left cornor of a new island, and the footprint will be identical + - Otherwises, if needed, we can sort the footprint and output the hash +- time: O(n), visit all +- space: O(n), store footprint, and dfs stacks worst case visit all nodes + + + +--- + +**42. [136. Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/136.%20Single%20Number.java)** Level: Easy Tags: [Bit Manipulation, Hash Table] + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + + + +--- + +**43. [1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)** Level: Medium Tags: [Bucket Sort, DP, Hash Table, Sort] + + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**44. [299. Bulls and Cows.java](https://github.com/awangdev/LintCode/blob/master/Java/299.%20Bulls%20and%20Cows.java)** Level: Medium Tags: [Hash Table] + + +#### Solution1: use int[10] to count frequency +1. check match chars +1. check unmatched chars by counting and offset their frequency + - count++ on secret chars: if secretCount is ever < 0 => `char g` has match, then cows++ + - count-- on guess chars: if guessCount is ever >0 => `char s` has match, then cows++ + +#### Solution2: Use hashmap to count +- Improvement: since all digit, use int[10] to count + + + +--- + +**45. [266. Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/266.%20Palindrome%20Permutation.java)** Level: Easy Tags: [Hash Table] + + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count char occurrance + - 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assume lower case letter. 应该至少是所有ASCII code + + + +--- + +**46. [311. Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/311.%20Sparse%20Matrix%20Multiplication.java)** Level: Medium Tags: [Hash Table] + + +给两个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 + + + +--- + +**47. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**48. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**49. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**50. [771. Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/771.%20Jewels%20and%20Stones.java)** Level: Easy Tags: [Hash Table] + + +- 给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 +- Basic HashSet + + + +--- + +**51. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**52. [387. First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/387.%20First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +#### Count appearance with int[256] +- 按照题意, 找到第一个 first index == last index的字母. + +#### Count appearance with hashmap (more scalable) +- 用hashmap存字母的index, 有些重复字母的index就会是个list. +- 找到单一index, 结合成list, sort, return list.get(0) +- slow due + + + +--- + +**53. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**54. [246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math, Two Pointers] + + +根据题意枚举, 再根据规则basic implementation + +#### HashTable + Two Pointer +- compare left/right + +#### Alter input +- flip number (6 and 9), and then reverse the string, see if the string is the same. +- takes more + + + + +--- + +**55. [463. Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/463.%20Island%20Perimeter.java)** Level: Easy Tags: [Hash Table] + +#### Brutle, Count Blocks and Walls +- 每个格子 +4 个墙; +- 每个shared的墙要减去: 从每个island走去另外一个, 都-1 (最终没面墙, -2) + +#### Hash Table +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么就可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不大,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + + + +--- + +**56. [170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)** Level: Easy Tags: [Design, Hash Table, Memoization] + + +#### Hash Table, Memo +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +- time: O(n), loop over all elements in map +- space: O(n), store all elements in map & memoization set + + + +--- + +**57. [981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)** Level: Medium Tags: [Binary Search, Hash Table, TreeMap] + + +#### Method1: Binary Search +- use hash to store +- binary serach on list of values + +#### Method2: TreeMap +- use hash to store > +- treemap.floorKey(timestamp) finds the top item below certain timestamp + + + +--- + +**58. [202. Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/202.%20Happy%20Number.java)** Level: Easy Tags: [Hash Table, Math] + + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + + + +--- + +**59. [380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + + +#### Hash Table, Swap when removing +- Map: `map, Lis: tracks `index->value` +- list maintain 用来 insert/remove/random operations. +- Remove: swap input valueIndex & tialIndex = list.size() -1. + - list.remove(object) 应该是要O(logn) 做一个search的. + - list.remove(list.size() - 1) is cheapter + + + +--- + +**60. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**61. [219. Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/219.%20Contains%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 +- Time O(nm), m = # of duplicates. 太慢 +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k + + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**62. [205. Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/205.%20Isomorphic%20Strings.java)** Level: Easy Tags: [Hash Table] + + +#### HashMap +- check 2 failture cases: + - same key, value not matching + - two key maps to same value + + + +--- + +**63. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**64. [242. Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/242.%20Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +#### int[26] + +#### HashMap + + + +--- + +**65. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**66. [217. Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/217.%20Contains%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 + + + +--- + +**67. [1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)** Level: Medium Tags: [Array, Hash Table, TreeMap] + + + +#### Hash Table, TreeMap; atomic save +- store efficiently: use List>. only preserve changed itemd +- if no match, find last modifed item based on snapId, use TreeMap.floorEntry + - map.floorEntry(id) return the item.key lower or equal to id +- Utilize a `buffer: Map` and perform atomic save + + + +--- + +**68. [1. Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1.%20Two%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. + + + + +--- + +**69. [139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- Bottom-up, test simply case. Sequence DP. +- true/false problem, think about dp + - 子问题: 前i个字母, 是否可以有valid break + - check: 1) dp[j] && 2) `if substring(j, i) valid`, for all j = [0 ~ i] + - dp = new boolean[n + 1]; dp[0] = true; + - test: `dp[i] |= dp[j] == true && word[j, n] in dict`. + - Need iterate over i = [0 ~ n], also j = [0, i] + - When there is a way to make dp[i] == true, then break the [j ~ i] loop, move on to test dp[i++] +- Use set dict: `dict.contains()` +- Improvement: O(n) to figure out max length, so we can skip some substring[j~i] dict.contains() +- overall O(n^2) time since the double for loop + +#### DFS +- Top-Down, break into small problems: Check front subString, and put the rest substring into dfs to test +- Memoization: for tested failed substring, record and do NOT test them again. +- Same Improvement as in DP: use max/min length of dict words as boundary + + + +--- + +**70. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**71. [274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.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. + +#### Couting Sort / 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 + +#### More about Counting Sort +1. Application: for number/character range +1. Steps: + - Find range, define countArray + - Count element and record in the array + - PreSum the countArray + - Start from beginning of the array, `print & decrese count` to produce the sorted elements + + + + +--- + +**72. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**73. [49. Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/49.%20Group%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 (too slow) +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**74. [720. Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/720.%20Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序(lexicographically), 排序以后才能逐个看是否partial string已经存在 + - 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 + - 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: + - 长 string 排在前; + - 相等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) + +#### 从最长的word开始做 +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**75. [438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)** Level: Medium Tags: [Hash Table, Sliding Window, Two Pointers] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### Method1: Sliding Window, Hash Table. +- A creative way of using anagram char count `hash[c] >= 0` to determine if the curr c is a target char of the deesired anagram. + - because we always reduce hash[c]-- for all characters + - so only the anagram chars would be `hash[c] >= 0` after reducing. +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +- Slinding window always has left/right pointer: + - 1) at any given time move 1 index at a time: expand right window, process rsult, shrink left window + - 2) one of the basic goal is to maintain fixed window size +- algo: + - calc char freq of the target p, and store in a hash[256]; it will be used to distinguish anagram chars: `hash[c] >= 0` indicates a anagram char + - expand right window: move right to expand the window; ONLY when meeting a anagram char, count-- + - process result: if count reduces to 0, one anagram is found + - shrink left window: if (right - left) == p.length(), drop curr left char, and move forward +- how could we rely on only just `count == 0`? + - the hidden pre-condition is `right - left must already be p.length()`, which is validaterd in prev iteration +- time: O(n) +- space: O(1) + +#### Method2: HashTable +- count character apperance -> hash table, here just a int[26] + - use a window to record count++ and count--, in order to compare with countP +- prep the countP takes O(m) time +- time: O(n) + O(m) +- space: O(n) + + + + + +--- + +**76. [632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)** Level: Hard Tags: [Hash Table, Sliding Window, Two Pointers] + + +#### Method1: Sliding Window +- First sort all of the items together by actual val using `Node {int val, int row}` +- Slinding window goal: + - 1) use right to find range that touches all rows, + - 2) use left to shrink the range +- Sliding Window Template + - move right pointer + - Counts[i] = # of elements used in left/right range + - when counts[i] == 0, countUnique++; the number of row/list being included + - when count == row size: + - processing & save shorter range by using left/right Pointers + - move left pointer; when counts[i] == 0, countUnique-- +- time: O(nlogn) for initial sort and then O(n) to process +- space: O(n) +- What is hard here? To think of the idea of counting one usage of each row: + - when each all rows are used at least 1 time + - calculate the min dist + +#### Method2 PQ?, Similar to merging k array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104893/Java-Code-using-PriorityQueue.-similar-to-merge-k-array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/solution/ + + + +--- + +**77. [138. Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/138.%20Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List, time, space: O(n) +- Basic Implementation of copy linked list: + - use a iterator node to iterate over the list: 遍历head.next .... null. + - use a dummy node to hold reference to the iterator node. +- Map: 1. avoid creating same node; 2. return the new node if existing + - 每一步都check map里面有没有head. 没有? 加上 + - 每一步都check map里面有没有head.random. 没有? 加上 +- Note, there is a way to skip the extra map O(n): https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43491/A-solution-with-constant-space-complexity-O(1)-and-linear-time-complexity-O(N) + - However, creating a deep clone of the list is already O(n) extra space, so it is NOT effectively O(1) w/o map + - It may be beneficial, if we can not hold all nodes in memory, then the approach w/o map is more applicable. + + + +--- + +**78. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + +**79. [760. Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/760.%20Find%20Anagram%20Mappings.java)** Level: Easy Tags: [Hash Table] + + +- HashMap 存index list +- 遍历一遍数组A, 列举出所有元素 + + + +--- + +**80. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**81. [94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Method1: DFS +- option1: dfs + rst list to carry results +- option2: Divide and Conquer, 在自己的基础上recursive, 不用helper function +- O(n) time + +#### Method2: Iterative, Stack inorder traversal +- 1) Add root.leftPath all the way to leaf, 2) process curr 3) Move to right if applicable 4) add all right.leftPath +- O(n) time, O(h) space + + + + +--- + +**82. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**83. [721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Union Find] + +- time O(mn) + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Method1: 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, 最终用来输出. + +#### Method2: 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) + +### DFS +- TODO + + + +--- + +**84. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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" + + + +--- + +**85. [739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)** Level: Medium Tags: [Hash Table, Monotonous Stack, Stack] + + +#### Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) + +#### Method2: `Map `, kinda of like bucket sort +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: + - 1) record tempIndex[currTemp] = i; + - 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result + + +--- + diff --git a/review/HashHeap.md b/review/HashHeap.md new file mode 100644 index 0000000..2769da8 --- /dev/null +++ b/review/HashHeap.md @@ -0,0 +1,76 @@ + + + +## HashHeap (3) +**0. [[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)** Level: Hard Tags: [HashHeap, Heap, Lint] + +非题.是从九章找来的HashHeap implementation. + +#### HashHeap +- An efficient implementation of a priority queue. +- The linear hash function monotonically maps keys to buckets, and each bucket is a heap +- https://xlinux.nist.gov/dads/HTML/hashheap.html + + + +--- + +**1. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**2. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + diff --git a/review/Heap.md b/review/Heap.md new file mode 100644 index 0000000..66d030a --- /dev/null +++ b/review/Heap.md @@ -0,0 +1,559 @@ + + + +## Heap (22) +**0. [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个数字 + + + +--- + +**1. [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 + + + +--- + +**2. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**3. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**4. [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 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) + + + + +--- + +**6. [[lint]. Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Merge%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 + + + +--- + +**7. [[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)** Level: Hard Tags: [HashHeap, Heap, Lint] + +非题.是从九章找来的HashHeap implementation. + +#### HashHeap +- An efficient implementation of a priority queue. +- The linear hash function monotonically maps keys to buckets, and each bucket is a heap +- https://xlinux.nist.gov/dads/HTML/hashheap.html + + + +--- + +**8. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**9. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**10. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**11. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**12. [973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)** Level: Medium Tags: [Divide and Conquer, Heap, Sort] + + +#### PriorityQueue +- Create customized Point{} class +- Sort by distance +- Maintain queue size <= K + +#### Divide and Conquer +- ?, select sort? + + + +--- + +**13. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**14. [239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Method1: Deque, Monotonous queue +- 维持monotonuous queue: `front is always at max` and the `tail end is min`. Always need to return the max end of queue. +- when adding new elements x: + - 1) start from small-end of the queue + - 2) drop all smaller elements + - 3) append to the ending element that is larger than x. + - This is to maintain a front->tail decreasing queue +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`: better than using arraylist, since DeQueue(linked list) removes at O(1) cost +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +- Option1: sliding window template using right/left + while loop + - 1) tailing the new number to max queue, if applicable + - 2) process: record max + - 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] +- Option2: same concept, but use index `i` to mark right, and `i - k + 1` to mark left. +- time: O(n), one pass +- space: O(k), store the deque + + +#### Method2: Heap +- can always build a `class Node{index, val}`; and sort them with PQ of size k +- time: O(nlogK) +- space: O(k) +- this is not linear time, not as good as method1 + + + +--- + +**15. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**16. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**17. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**18. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**19. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**20. [373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)** Level: Medium Tags: [Heap, MaxHeap, MinHeap] + + +#### Method1: MinHeap wiht k size +- This approach follows the pattern of finding min pair: + - 1) only need to store k pairs + - 2) always start from min of A list and min of B list + - 3) pre-build k pairs honoring A list, and then pick the min pair, and start swapping with min of list B +- First attemp all first k pairs from nums1[i] against nums2[0] <=k : O(k) +- Use queue to pull min node and save results +- Use the nums1 val from the min node, pair up with nums2[j], add back to queue to sort +- overall runtime: O(klogk) +- space: O(k) + +#### Method2: MaxHeap with k size +- Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +- overall time: O(mnLogK) +- space: O(k) + + + +--- + +**21. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high= 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. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: 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..3c494c4 --- /dev/null +++ b/review/Interval.md @@ -0,0 +1,21 @@ + + + +## 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..de371f4 --- /dev/null +++ b/review/KMP.md @@ -0,0 +1,20 @@ + + + +## 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..225aec0 --- /dev/null +++ b/review/Linked List.md @@ -0,0 +1,578 @@ + + + +## Linked List (34) +**0. [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 + + + + +--- + +**1. [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 + + + +--- + +**2. [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, 换位子 + + + +--- + +**3. [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. + + + +--- + +**4. [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/ + + + +--- + +**5. [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]。 然后把三段链接在一起。 + + + + +--- + +**6. [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会出问题 + + + +--- + +**7. [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) + + + +--- + +**8. [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链接起来。 + + + +--- + +**9. [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) + + +--- + +**10. [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 + + + + +--- + +**11. [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; + + + +--- + +**12. [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在某个点小了,加进去当下这个空隙。 + + + +--- + +**13. [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一下。 + + + +--- + +**14. [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] + + +给一个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, 永远在左边上下上下。 + + + +--- + +**15. [[lint]. Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Nth%20to%20Last%20Node%20in%20List.java)** Level: Easy Tags: [Linked List, Lint] + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth + + + +--- + +**16. [21. Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/21.%20Merge%20Two%20Sorted%20Lists.java)** Level: Easy Tags: [Linked List] + + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + + + +--- + +**17. [237. Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/237.%20Delete%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 + + + +--- + +**18. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**19. [83. Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/83.%20Remove%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会非常清晰 + + + +--- + +**20. [203. Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/203.%20Remove%20Linked%20List%20Elements.java)** Level: Easy Tags: [Linked List] + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + + + +--- + +**21. [19. Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/19.%20Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Two Pointer +- 1 end pointer to define the window based n steps +- 1 pre pointer to track the node before the targeting node +- when end reaches null, remove nth node: link pre and head.next + + + +--- + +**22. [206. Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/206.%20Reverse%20Linked%20List.java)** Level: Easy Tags: [Linked List] + +#### Iterative +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + +#### Recursive with a helper function +- source node: head +- target node: new head + + + +--- + +**23. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**24. [369. Plus One Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/369.%20Plus%20One%20Linked%20List.java)** Level: Medium Tags: [Linked List] + + +#### Reverse to make significant digit at tail +- Need add from the back and calculate carry +- Reverse list, so insignificant digit at head; calculate carry +- Reverse back when output + + + +--- + +**25. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**26. [234. Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/234.%20Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单: 两边回溯相等. However: + - 1) cannot random access index on linkded list + - 2) cannot reverse iterating linked list +- solution: reverse linked list: 遍历接开头 + - 1) 用快慢指正找到mid point + - 2) reverse 2nd half + - 3) compare leftList and rightList +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + + + +--- + +**27. [876. Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/876.%20Middle%20of%20Linked%20List.java)** Level: Easy Tags: [Linked List] + +找Linked List的中间node + +#### 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + + + +--- + +**28. [2. Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/2.%20Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. 跟Add Binary的理解方式一模一样. + +#### Math, Linked List +- reverse order helps calculation + - add additional carry to end + - not same length: align on left +- traverse till both ends +- 遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + + + + +--- + +**29. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**30. [430. Flatten a Multilevel Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/430.%20Flatten%20a%20Multilevel%20Doubly%20Linked%20List.java)** Level: Medium Tags: [DFS, Linked List] + + +#### DFS +- Depth-first: + - 1) process curr.child, return tailChild + - 2) connect tailChild.next = curr.next +- function: link(Node a, Node b); + + + +--- + +**31. [160. Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/160.%20Intersection%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 + + + +--- + +**32. [138. Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/138.%20Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List, time, space: O(n) +- Basic Implementation of copy linked list: + - use a iterator node to iterate over the list: 遍历head.next .... null. + - use a dummy node to hold reference to the iterator node. +- Map: 1. avoid creating same node; 2. return the new node if existing + - 每一步都check map里面有没有head. 没有? 加上 + - 每一步都check map里面有没有head.random. 没有? 加上 +- Note, there is a way to skip the extra map O(n): https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43491/A-solution-with-constant-space-complexity-O(1)-and-linear-time-complexity-O(N) + - However, creating a deep clone of the list is already O(n) extra space, so it is NOT effectively O(1) w/o map + - It may be beneficial, if we can not hold all nodes in memory, then the approach w/o map is more applicable. + + + +--- + +**33. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + diff --git a/review/LinkedHashMap.md b/review/LinkedHashMap.md new file mode 100644 index 0000000..87eba09 --- /dev/null +++ b/review/LinkedHashMap.md @@ -0,0 +1,48 @@ + + + +## LinkedHashMap (1) +**0. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + diff --git a/review/Lint.md b/review/Lint.md new file mode 100644 index 0000000..6bad5c7 --- /dev/null +++ b/review/Lint.md @@ -0,0 +1,470 @@ + + + +## Lint (27) +**0. [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 + + + +--- + +**1. [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 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**2. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**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 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. + + + +--- + +**6. [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. + + + +--- + +**7. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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, 自底向上建立起的。 + + + +--- + +**8. [[lint]. Nth to Last Node in List.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Nth%20to%20Last%20Node%20in%20List.java)** Level: Easy Tags: [Linked List, Lint] + +#### Linked List +- 先找到nth node +- 然后head开始跑 +- node 到底,而head ~ node刚好是 n 距离。所以head就是要找的last nth + + + +--- + +**9. [[lint]. Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array, Lint] + + + + +--- + +**10. [[lint]. Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Compare%20Strings.java)** Level: Easy Tags: [Lint, String] + +看StringA是不是包括所有 StringB的字符. Anagram + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + + + +--- + +**11. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**12. [[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)** Level: Hard Tags: [HashHeap, Heap, Lint] + +非题.是从九章找来的HashHeap implementation. + +#### HashHeap +- An efficient implementation of a priority queue. +- The linear hash function monotonically maps keys to buckets, and each bucket is a heap +- https://xlinux.nist.gov/dads/HTML/hashheap.html + + + +--- + +**13. [[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)** Level: Easy Tags: [Hash Table, Lint, String] + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**14. [[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)** Level: Medium Tags: [Array, Hash Table, Lint] + + +把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. + + + + +--- + +**15. [[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)** Level: Medium Tags: [Array, Lint, Two Pointers] + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**16. [[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)** Level: Easy Tags: [Array, Lint, 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) + + + + +--- + +**17. [[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Lint, 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! + + + + +--- + +**18. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**19. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**20. [[lint]. Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array, Lint] + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**21. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**22. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**23. [[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)** Level: Medium Tags: [Lint, 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) + + + +--- + +**24. [[tool]. Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Hash%20Function.java)** Level: Easy Tags: [Hash Table, Lint] + + +In general, there is no universal recipe to stick to when it comes to implementing hashCode(). +https://www.baeldung.com/java-hashcode + +#### 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会变得太大,所以不能算完和 再 %... + + + +--- + +**25. [[tool]. UnionFind.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20UnionFind.java)** Level: Medium Tags: [Lint, Union Find] + + +#### Method1: Union Find with Array +- union(), find() +- Path Compresion: store skip father after found, which makes find O(1) + +#### Method2: Union Find with HashMap + + + + + +--- + +**26. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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 + + + +--- + diff --git a/review/List.md b/review/List.md new file mode 100644 index 0000000..6ec2c3f --- /dev/null +++ b/review/List.md @@ -0,0 +1,11 @@ + + + +## List (1) +**0. [118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)** Level: Easy Tags: [Array, Basic Implementation, List] + + + + +--- + diff --git a/review/Lock.md b/review/Lock.md new file mode 100644 index 0000000..a657c1f --- /dev/null +++ b/review/Lock.md @@ -0,0 +1,25 @@ + + + +## Lock (1) +**0. [1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)** Level: Medium Tags: [Lock, Semaphore, Thread] + + +#### Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 + +#### Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +- semaphore: https://www.geeksforgeeks.org/semaphore-in-java/ + + + +--- + diff --git a/review/Math.md b/review/Math.md new file mode 100644 index 0000000..799333d --- /dev/null +++ b/review/Math.md @@ -0,0 +1,789 @@ + + + +## Math (45) +**0. [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) 太慢, 不合题意 + + + +--- + +**1. [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 + + + + +--- + +**2. [Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)** Level: Easy Tags: [Math] + + + +--- + +**3. [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. + + +--- + +**4. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + + +给一串点, 找是否有一个所有点中间的, 跟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) + + + +--- + +**5. [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了。 + + + +--- + +**6. [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许多。 + + + + +--- + +**7. [Orderly Queue.java](https://github.com/awangdev/LintCode/blob/master/Java/Orderly%20Queue.java)** Level: Hard Tags: [Math, String] + + + + +--- + +**8. [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. + + + +--- + +**9. [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. + + + + +--- + +**10. [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的益处。 + + +--- + +**11. [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. + + + +--- + +**12. [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找合适的数字. + + + +--- + +**13. [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 + + + +--- + +**14. [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 + + + +--- + +**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] + + +#### 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. [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整除. 一步到位. + + + +--- + +**17. [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` + + + +--- + +**18. [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) + + + +--- + +**19. [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) + + + +--- + +**20. [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 的情况. + + +--- + +**21. [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 + + + +--- + +**22. [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, 数位号) + + + + +--- + +**23. [360. Sort Transformed Array.java](https://github.com/awangdev/LintCode/blob/master/Java/360.%20Sort%20Transformed%20Array.java)** Level: Medium Tags: [Math, Two Pointers] + + +#### Two Pointers, Math +- Being able to analys the a*x^2 + b*x graph and find the `peak` or `valley` +- Math basics: x^2 dominates the overall curve so it is up to a to determine: + - `valley`: if a < 0, both sides will be small and center will be large. Prioritize larger value. + - `peak`: if a > 0, center will be small and both sides will be large. Prioritize smaller value. + - starting index being 0 or n-1, is driven by `a` + + + +--- + +**24. [415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)** Level: Easy Tags: [Basic Implementation, Math, String] + + +#### Two Pointer +- Use i, j to process from end of 2 strings +- handle edge case for i, j + - if i < 0, its num = 0 (since we are doing sum, blindly setting 0 is okay) +- Note: `sb.insert(0, x)` is much slower than doing a final `sb.reverse()` + +#### If manually convertin to int[] +1. when converting to int[], remember to reverse string. +1. when converting to int[], remember to reserve extra space for carry + + + +--- + +**25. [7. Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/7.%20Reverse%20Integer.java)** Level: Easy Tags: [Math] + + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + + + +--- + +**26. [204. Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/204.%20Count%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个数就好了 + + + +--- + +**27. [509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.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. + + + + +--- + +**28. [67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%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 + +#### reverse +- Reverse string -> Convert to Integer List, add up -> Convert back to string +- pointer 从前向后, 所以只需要 1个pointer. +- 操作复杂, 如上, 证明可以解决. 没必要reverse. + +#### Incorrect: convert to Integer +把binary换成数字作加法. 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**29. [168. Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/168.%20Excel%20Sheet%20Column%20Title.java)** Level: Easy Tags: [Math] + + +#### 基本换算 +- 26位, 像10位一样去思考 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + + + +--- + +**30. [9. Palindrome Number.java](https://github.com/awangdev/LintCode/blob/master/Java/9.%20Palindrome%20Number.java)** Level: Easy Tags: [Math] + +#### Reverse half of the number +- build reversed integer 直到midpoint, where x <= reverse +- 如果input双数: x == reverse +- 如果input单数 (而且x>reverse): x == reverse/10 + +#### Consider palindrome +- optionA: compare digit by digit +- optionB: reverse half of the string/int, and compare with other half. + + + + + + +--- + +**31. [43. Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/43.%20Multiply%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]. + - Loop over num1, each row num1[x] * num2, save to correct index (i + j + 1) + - Note: skip leading '0' during output, but do not delete string "0" + - time,space O(mn) +- Option1: Calculate carry on the fly + - index `curr = i + j + 1`, left index `left = curr - 1`, since we start calculation from end of the array. + - **we only touch right side of the array once**, so we can move the carry off from it, and carry to left index + - code is concise +- Option2: save product first without calculating carry + - save product in each int index + - calculate carry on rst[] and `sb.insert(0, c)` (since we start from end of rst) + - this is actaully faster than Option1, somehow. + +#### Reverse, calculate from index 0, and reverse back +- 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'; + + + +--- + +**32. [367. Valid Perfect Square.java](https://github.com/awangdev/LintCode/blob/master/Java/367.%20Valid%20Perfect%20Square.java)** Level: Easy Tags: [Binary Search, Math] + + +#### Binary找sqrt +- binary search template: mid+1, mid-1, `start <= end` +- define index as long. + + + +--- + +**33. [246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math, Two Pointers] + + +根据题意枚举, 再根据规则basic implementation + +#### HashTable + Two Pointer +- compare left/right + +#### Alter input +- flip number (6 and 9), and then reverse the string, see if the string is the same. +- takes more + + + + +--- + +**34. [319. Bulb Switcher.java](https://github.com/awangdev/LintCode/blob/master/Java/319.%20Bulb%20Switcher.java)** Level: Medium Tags: [Brainteaser, Math] + + +#### Brainteaser +- https://leetcode.com/problems/bulb-switcher/discuss/77104/Math-solution.. + +#### Brutle: +- if just impl, it take O(n^2): +- repating: some pos are toggled mutiple times: if we know total times, easy to determin each pos. +- loop over [2, n], count times on each index + + + +--- + +**35. [12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)** Level: Medium Tags: [Basic Implementation, Math, String] + + +#### String, Basic implementation +- Parse each digit based on rules +- 1) parse: analyze the situations + + +--- + +**36. [202. Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/202.%20Happy%20Number.java)** Level: Easy Tags: [Hash Table, Math] + + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + + + +--- + +**37. [69. Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/69.%20Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + +#### sqrt(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 + + + +--- + +**38. [2. Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/2.%20Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. 跟Add Binary的理解方式一模一样. + +#### Math, Linked List +- reverse order helps calculation + - add additional carry to end + - not same length: align on left +- traverse till both ends +- 遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + + + + +--- + +**39. [273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%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来找到结果, 每段的处理方法都是一样的 +- Note: + - StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 + - 注意加 " " 的时候, 如果多余, 要`trim()` + - 注意, `小于20的数字, 有自己的特殊写法, 需要额外handle` + - 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 +- Thinking process: + - `1 ~ 19`: [one, two ... nine, ten, eleven, ...., ninteen] + - `20 ~ x0`: [twenty, thirty, fourty, ... ninety] + - `x00`: hundred: 100 + - thousand: 10^3 + - million: 10^6 + - billion: 10^9 + - trillian: 10^12 way over 2^31, not needed +- plan: + - parse 3 digits at a time + - convert the 3 digit to [xx hundred xx-ty x] + - come up with a string[] + - insert the thousands/million/billion to the string[] + + + +--- + +**40. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + +**41. [65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**42. [8. String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/8.%20String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- Handling use cases +- Parse steps: + - 0. trim space + - 1 parse operator + - 2 trim leading zero + - 3. get number string +- Validation: + - 1. max length over max integer length + - 2. exceed min/max value +- Alternatively, regular expression, but not applicable in interview: if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**43. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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" + + + +--- + +**44. [13. Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/13.%20Roman%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 + +#### use map to store combinations + + + + +--- + diff --git a/review/Matrix DFS.md b/review/Matrix DFS.md new file mode 100644 index 0000000..74a1d14 --- /dev/null +++ b/review/Matrix DFS.md @@ -0,0 +1,67 @@ + + + +## Matrix DFS (2) +**0. [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 + + + +--- + +**1. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + diff --git a/review/MaxHeap.md b/review/MaxHeap.md new file mode 100644 index 0000000..747be5d --- /dev/null +++ b/review/MaxHeap.md @@ -0,0 +1,134 @@ + + + +## MaxHeap (5) +**0. [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个数字 + + + +--- + +**1. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**2. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**3. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**4. [373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)** Level: Medium Tags: [Heap, MaxHeap, MinHeap] + + +#### Method1: MinHeap wiht k size +- This approach follows the pattern of finding min pair: + - 1) only need to store k pairs + - 2) always start from min of A list and min of B list + - 3) pre-build k pairs honoring A list, and then pick the min pair, and start swapping with min of list B +- First attemp all first k pairs from nums1[i] against nums2[0] <=k : O(k) +- Use queue to pull min node and save results +- Use the nums1 val from the min node, pair up with nums2[j], add back to queue to sort +- overall runtime: O(klogk) +- space: O(k) + +#### Method2: MaxHeap with k size +- Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +- overall time: O(mnLogK) +- space: O(k) + + + +--- + diff --git a/review/Memoization.md b/review/Memoization.md new file mode 100644 index 0000000..217cc83 --- /dev/null +++ b/review/Memoization.md @@ -0,0 +1,445 @@ + + + +## Memoization (15) +**0. [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);` + + + +--- + +**1. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**2. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**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. [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. [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. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**8. [509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.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. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + +**10. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**11. [70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%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]往往是有特殊状态的. 这里, dp[0]: 1种方式可以原地不动 + - dp[1]=1, 1种方式到达index=1, + - 之后的`dp[2] = dp[0]+dp[1]`: 就是dp[0]的走法 or dp[1]的走法 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**12. [170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)** Level: Easy Tags: [Design, Hash Table, Memoization] + + +#### Hash Table, Memo +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +- time: O(n), loop over all elements in map +- space: O(n), store all elements in map & memoization set + + + +--- + +**13. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + + +--- + +**14. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + diff --git a/review/Merge Sort.md b/review/Merge Sort.md new file mode 100644 index 0000000..dc63d2e --- /dev/null +++ b/review/Merge Sort.md @@ -0,0 +1,159 @@ + + + +## Merge Sort (5) +**0. [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/ + + + +--- + +**1. [[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)** Level: Medium Tags: [Lint, 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) + + + +--- + +**2. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**3. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**4. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + diff --git a/review/MinHeap.md b/review/MinHeap.md new file mode 100644 index 0000000..678a502 --- /dev/null +++ b/review/MinHeap.md @@ -0,0 +1,300 @@ + + + +## MinHeap (11) +**0. [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个数字 + + + +--- + +**1. [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] + + +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 +- + + + +--- + +**2. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**3. [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 + + + +--- + +**5. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**6. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**7. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**8. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**9. [373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)** Level: Medium Tags: [Heap, MaxHeap, MinHeap] + + +#### Method1: MinHeap wiht k size +- This approach follows the pattern of finding min pair: + - 1) only need to store k pairs + - 2) always start from min of A list and min of B list + - 3) pre-build k pairs honoring A list, and then pick the min pair, and start swapping with min of list B +- First attemp all first k pairs from nums1[i] against nums2[0] <=k : O(k) +- Use queue to pull min node and save results +- Use the nums1 val from the min node, pair up with nums2[j], add back to queue to sort +- overall runtime: O(klogk) +- space: O(k) + +#### Method2: MaxHeap with k size +- Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +- overall time: O(mnLogK) +- space: O(k) + + + +--- + +**10. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high: find candidates that has 0 overlap with others + - pick `min-count candidate A`: it is a candidate that has overlaps with most strings (since 0-match-count is lowest) + - the above candidate will help to **eliminate** a largerset of overlapped candidates + - guess A, return matchCount. +- filter set with matchCount: eliminateCandidate + + + +--- + diff --git a/review/Minimum Binary Tree.md b/review/Minimum Binary Tree.md new file mode 100644 index 0000000..da98207 --- /dev/null +++ b/review/Minimum Binary Tree.md @@ -0,0 +1,69 @@ + + + +## Minimum Binary Tree (3) +**0. [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. + + + +--- + +**1. [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 + + + + +--- + +**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..c552a01 --- /dev/null +++ b/review/Monotonous Stack.md @@ -0,0 +1,64 @@ + + + +## Monotonous Stack (3) +**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)的巧妙用法. + + + + +--- + +**1. [402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)** Level: Medium Tags: [Greedy, Monotonous Stack, Stack] + + +#### Monotonous Stack (Increasing) +- Greedy: Remove 1) earlier digits(数位靠前权值大), 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. + + + + +--- + +**2. [739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)** Level: Medium Tags: [Hash Table, Monotonous Stack, Stack] + + +#### Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) + +#### Method2: `Map `, kinda of like bucket sort +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: + - 1) record tempIndex[currTemp] = i; + - 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result + + +--- + diff --git a/review/Moore Voting.md b/review/Moore Voting.md new file mode 100644 index 0000000..990463c --- /dev/null +++ b/review/Moore Voting.md @@ -0,0 +1,65 @@ + + + +## Moore Voting (2) +**0. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**1. [229. Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/229.%20Majority%20Element%20II.java)** Level: Medium Tags: [Array, Moore Voting] + + +#### Two counters, Moore Voting +1. Moore voting: vote可以加减, 一旦为零, 换下一个candidate, 之前抵消掉的算作清零. +1. 一个array里面, 最多也只有2个数字 出现次数大于2次, 所以用A/B表示. +1. count overall apperance at the end for the two items: countA, countB +1. save to result as valA, valB +1. 有点 moore voting的意思: + - 当count == 0的时候, reset + - 两个candidate A/B都不等, 那么countA--, countB-- +1. 最终重新计数, 然后比较出结局. +1. 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +#### Sort + count +- O(nlogN) + + + +--- + diff --git a/review/NestedInteger.md b/review/NestedInteger.md new file mode 100644 index 0000000..d2d0b53 --- /dev/null +++ b/review/NestedInteger.md @@ -0,0 +1,64 @@ + + + +## NestedInteger (3) +**0. [339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS, NestedInteger] + + +给一串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. + - 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) + +#### BFS +- bfs, queue, 处理queue.size() for a level +- use a level variable to track levels +- slower since it uses extra space, worst case O(n) of all items + + + +--- + +**1. [364. Nested List Weight Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/364.%20Nested%20List%20Weight%20Sum%20II.java)** Level: Medium Tags: [DFS, NestedInteger] + + +#### Method1: DFS +- Build a list of NestedInt +- DFS: + - sum up integers in the list are integers + - dfs on nested list + - overallSum = sum * (depth+1) + - End state: if no nested list (no more child dfs), return depth 1 +- Parent level: sum up all ints and times the (depth+1) + + +#### Method2: BFS +- Using stack to flatten all nestedList, and process in the end +- Can actually use list, does not need to be stack. +- uses more memory + + + +--- + +**2. [341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, NestedInteger, Stack] + + +#### Method1: Stack holds items from back of the list +- Option1: always set integer on top of the stack everywhere + - if not, poping stack until the top is integer + - code is easy +- Option2: in hasNext(), faltten the list in stack + +#### Method2: DFS preprocessing. +- 用queue to store all items. Kinda hack. Defeat the purpose of the problem. +- Super fast to query next(), however, needs to holds everything in memory +- O(n) + + + +--- + diff --git a/review/PQ.md b/review/PQ.md new file mode 100644 index 0000000..3a64ea2 --- /dev/null +++ b/review/PQ.md @@ -0,0 +1,24 @@ + + + +## PQ (1) +**0. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + diff --git a/review/Partition DP.md b/review/Partition DP.md new file mode 100644 index 0000000..f018812 --- /dev/null +++ b/review/Partition DP.md @@ -0,0 +1,201 @@ + + + +## 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. [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的理所应当。 + + + + +--- + +**3. [91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Method1: DP, Bottom-Up by calculating base case first +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- there can be 2 final states at dp[i]: single digit or double digit. + - validate if `single digit`, dp[i] += dp[i - 1]. Last 1 digit can be decoded. + - validate if `double digit`, dp[i] += dp[i - 2]. Last 2 digits can be decoded. +- note: + - get number from char: `c - '0'` + - validatae single digit != '0', 因为'0' 不在条件之中(A-Z) +- Option1: dp[i] as # ways to decode at index i, including letter i + - The validation is done on: 1) single digit i, or 2) double digit [i-1, i] +- Option2: Partition DP, dp[i] as # ways to decode for first i letters (excluding letter i) + - 定义`dp[i] = 前i个digits最多有多少种decode的方法`: new dp[n + 1]. + - dp[i] += dp[i - x], where x = 1, 2 + - The validation is done on: 1) single digit [i-1], or 2) double digit [i-2, i-1] + - Option2 is better in terms of clean coding. We assume `dp[0]=1` as 1 way to decode 0 digits. + - No need to specially handle length == 1, because it is covered later + - No need to manualy init the first 2-digit case as in Option1 + - Return of `dp[n]` is clean +- 引申: 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +#### Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 + - especially when i = n - 2, handle 2-digt edge case carefully: + - 1) check if two-digit range [i, i+1] is valid + - 2) check if single-digit [i] is valid; if so, += dfs(s, i + 1) +- memo[i]: # ways to decode from [i, n). init with `memo[i]=-1` + + + +--- + +**4. [639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%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]) +- Enumeration: + - 具体分析 '*' 出现的位置, 枚举出数字, 基本功. + - 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) + - 枚举好以后, 其实这个题目的写法和思考过程都不难 +- Mode: + 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. + + +#### DFS + memoization +- DFS top-down approach is used to analyze the problem. The logic flow: +- 1) consider the case of 1 letter or 2 letters. +- 2) one letter: + - [*]: + 9 * dfs(s, i + 1) + - [0~9]: + dfs(s, i + 1) +- 3) two letters: + - [_, *]: depends + - [*, _]: depends + - [*, *]: + 15 * dfs(s, i + 2) +- memo[i] records # of ways to decode from [i ~ n] +- space: O(n), Size of recursion tree can go upto n +- time: O(n), `memo array is filled exactly once`!!! + + + +--- + diff --git a/review/Partition.md b/review/Partition.md new file mode 100644 index 0000000..0858f5f --- /dev/null +++ b/review/Partition.md @@ -0,0 +1,72 @@ + + + +## Partition (3) +**0. [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) + + + +--- + +**1. [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看起来更容易理解. + + + +--- + +**2. [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 + + + +--- + diff --git a/review/Permutation.md b/review/Permutation.md new file mode 100644 index 0000000..81e15d8 --- /dev/null +++ b/review/Permutation.md @@ -0,0 +1,118 @@ + + + +## Permutation (5) +**0. [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 太慢, 不可行. + + + +--- + +**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. [1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)** Level: Medium Tags: [Array, Greedy, Permutation] + + +#### Analyze Permutation behavior +- concept similar to `31. Next Permutation` +- 1) first pass: find the one that is in incorrect order +- 2) second pass: find the right spot to swap + + + +--- + +**3. [31. Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/31.%20Next%20Permutation.java)** Level: Medium Tags: [Array, Permutation] + + +#### Permutation Behavior +- Great write up: https://leetcode.com/problems/next-permutation/solution/ +- next lexicographically permutation: `smallest` but `larger than curr` permutation: + - find first low point from right [low] + - find the slight larger [high] to swap with [low] + - make sure right side of low is eventually the smallest +- Analyze the use cases, to find next low permutation, 2 major steps: + - 1) Find `first low/drop candidate` from right + - 2) Find `first high where nums[high] > nums[low]` from right + - 3) swap(low, high). + - By now, [low, n-1] forms a greater permutation + - but it is not the smallest, because right side [low + 1, n - 1] is descending + - 4) reverse(low + 1, n-1) to create ascending slopt on right of low (smallest next lexicographically permutation) +- Corner case: if input array is decending (1st low not found), reverse it all together O(n) +- time: O(n) visit all indexes +- space: O(1) not using additional +- Similar question: `1053. Previous Permutation With One Swap` + + + + +--- + +**4. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + diff --git a/review/PreProduct.md b/review/PreProduct.md new file mode 100644 index 0000000..e29db56 --- /dev/null +++ b/review/PreProduct.md @@ -0,0 +1,58 @@ + + + +## PreProduct (2) +**0. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**1. [238. Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/238.%20Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 第一轮:PreProduct (跟preSum的感觉有点像) + - PreProduct[i] stores product from num[0] -> num[i-1] (skipping current num[i]) + - init preProduct[i] = 1, as base for product + - 错过一位操作: always `preProduct[i] *= carry;` and `carry *= nums[i]` +- 第二轮: 从右边乘起, 每次在index i, 收到的carry都是 `nums[i+1] *....* nums[end]` + - 第一轮的结果 * 第二轮的结果, 刚好在index i 缺少掉 nums[i]. 如题所愿. +- Time: O(n) + + + +--- + diff --git a/review/PreSum.md b/review/PreSum.md new file mode 100644 index 0000000..9e4170e --- /dev/null +++ b/review/PreSum.md @@ -0,0 +1,309 @@ + + + +## PreSum (13) +**0. [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] + + +#### 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. + + + +--- + +**1. [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` + + + +--- + +**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. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**4. [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]) + + + +--- + +**5. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**6. [303. Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/303.%20Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**7. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**8. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**9. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**10. [304. Range Sum Query 2D - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/304.%20Range%20Sum%20Query%202D%20-%20Immutable.java)** Level: Medium Tags: [DP, PreSum] + + +#### Method1: rangeSum/caching +- build rangeSum[i][j]: square range sum from (0,0) to (i,j), O(mn) to init +- query: time O(1) + +#### Method2: presum/caching +- build rowPreSum[i][j]: row i sum from [0 ~ j], O(mn) to init +- callign takes O(m); space O(mn) + + + + +--- + +**11. [724. Find Pivot Index.java](https://github.com/awangdev/LintCode/blob/master/Java/724.%20Find%20Pivot%20Index.java)** Level: Easy Tags: [Array, PreSum] + + +#### PreSum +- want to find `nums[i - 1] == nums[n - 1] - nums[i]`, given: + - preSum[i], sum from [0, i] inclusive + - preSum[j] - preSum[i] = [i+1, j] inclusive +- O(n) to build preSum +- O(n) to find pivot + + + +--- + +**12. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + diff --git a/review/PriorityQueue.md b/review/PriorityQueue.md new file mode 100644 index 0000000..77a09c2 --- /dev/null +++ b/review/PriorityQueue.md @@ -0,0 +1,608 @@ + + + +## PriorityQueue (23) +**0. [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] + + +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 +- + + + +--- + +**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. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**3. [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 + + + +--- + +**4. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**5. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**6. [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 + + + +--- + +**9. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**10. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**11. [252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### Method1: sort input and compare if curr.end & next.start overlaps +- sort: `Arrays.sort(intervals, Comparator.comparing(i -> i[0]))` +- time: O(nlogn), space: O(1) + +#### Method2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 +- time: O(nlogn), space O(n) +- Not necessary for this problem, since it requires extra space with pq. + + + +--- + +**12. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**13. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**14. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**15. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**16. [414. Third Maximum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/414.%20Third%20Maximum%20Number.java)** Level: Easy Tags: [Array, PriorityQueue] + +#### pq +- 注意special case: `when less than 3 items, return maximum` +- PQ是natural order: remain peek() will be the 3rd maximum + + + + +--- + +**17. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**18. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**19. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**20. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**21. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**22. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + diff --git a/review/Queue.md b/review/Queue.md new file mode 100644 index 0000000..0295ac4 --- /dev/null +++ b/review/Queue.md @@ -0,0 +1,80 @@ + + + +## 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. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**2. [346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison +- Note: if we it is calculate moving-window-product, better to use deque :) +- Sliding window? + - It has the spirit of slinding window: 1) maintain a range; 2) check range size `if (queue.size() > size)` + - Though, the solution must use a data structure to store data; it is not the traditional sliding window type of `left/right` pointer problem + + + +--- + diff --git a/review/Quick Select.md b/review/Quick Select.md new file mode 100644 index 0000000..a106890 --- /dev/null +++ b/review/Quick Select.md @@ -0,0 +1,54 @@ + + + +## Quick Select (2) +**0. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**1. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, highpivot, 如果写成了 <= 会 stack overflow. +- Time O(nlogn), Space: O(1) + + + +--- + +**1. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**2. [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) + + + +--- + +**3. [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 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 + + + +--- + +**5. [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 原题有一点点不一样. + + + + +--- + +**6. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high0)` from skipping last item at index 0 + + +#### Method2: DP +- the problem aims to find true/false capability +- bit-masking. TODO. The DP approach is kinda hard-level +- https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/335668/DP-with-Bit-Masking-Solution-%3A-Best-for-Interviews + + + +--- + diff --git a/review/Reservior Sampling.md b/review/Reservior Sampling.md new file mode 100644 index 0000000..182e44a --- /dev/null +++ b/review/Reservior Sampling.md @@ -0,0 +1,25 @@ + + + +## Reservior Sampling (1) +**0. [398. Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/398.%20Random%20Pick%20Index.java)** Level: Medium Tags: [Reservior Sampling] + + +#### 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. +- probability theory: + - 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 + + + + +--- + diff --git a/review/Rotation.md b/review/Rotation.md new file mode 100644 index 0000000..4f23f15 --- /dev/null +++ b/review/Rotation.md @@ -0,0 +1,17 @@ + + + +## Rotation (1) +**0. [189. Rotate Array.java](https://github.com/awangdev/LintCode/blob/master/Java/189.%20Rotate%20Array.java)** Level: Easy Tags: [Array, Rotation] + +#### Rotate array in place +- rotate all +- rotate 2 sides: < k or >= + + +#### Rotate by buffer the k array + + + +--- + diff --git a/review/Segment Tree.md b/review/Segment Tree.md new file mode 100644 index 0000000..9040710 --- /dev/null +++ b/review/Segment Tree.md @@ -0,0 +1,422 @@ + + + +## Segment Tree (17) +**0. [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 + + + +--- + +**1. [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 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**2. [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了 + + + +--- + +**3. [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. + + + + +--- + +**4. [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. + + + +--- + +**5. [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. + + + +--- + +**6. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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, 自底向上建立起的。 + + + +--- + +**7. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**8. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**9. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**10. [308. Range Sum Query 2D - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/308.%20Range%20Sum%20Query%202D%20-%20Mutable.java)** Level: Hard Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree +- Same concept as turning an array into a binary segment tree, + - HOWEVER, this is a 4-nary segmenet tree +- Reference. 307 Range Sum Query +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. +- Handling end stage, there are two approaches: + - ApproachA: check at beginning of recursive call (i.e in `build()`, `updateNode()`, `rangeQuery()`). + - pro: calling recursive function blindly; code is easy. + - con: be really clear about termination state, and catch it. + - ApproachB: check & come up with correct query condition before recursive call + - pro: input to recursive function is assumed to be correct + - con: sometimes really hard to write the conditions before recursive call; code is hard. + + + +--- + +**11. [850. Rectangle Area II.java](https://github.com/awangdev/LintCode/blob/master/Java/850.%20Rectangle%20Area%20II.java)** Level: Hard Tags: [Segment Tree, Sweep Line] + + +#### Sweep Line + Merge Interval concept +- Inspired by: https://leetcode.com/problems/rectangle-area-ii/discuss/137941/Java-TreeMap-solution-inspired-by-Skyline-and-Meeting-Room +- First consider regular sweep line and realize problem: each vertical line has multiple block segments + - Easy: take a list of vertical dots, and calculate the height diff + - We can use a TreeMap with y-coordinate as key, so to `natural sort by y-coordinate` +- Trick: can NOT remove used y coordinate from map, because the rectangle may continue to expand to right side. +- apply simple equation to calc area: `(long)preY * (p.x - preX)` +- time: + - sort initial queue: O(nlogn) + - process queue: O(n) + - TreeMap insertion: O(logn) + - TreeMap traversal: O(n) + - overall, process queue can be O(n^2) +- space: O(n) + +#### Sweep Line concept, bottom->top sweep +- https://leetcode.com/problems/rectangle-area-ii/discuss/137914/C%2B%2BPython-Discretization-and-O(NlogN) + +#### Segment Tree +- TODO lol + + + +--- + +**12. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**13. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**14. [307. Range Sum Query - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/307.%20Range%20Sum%20Query%20-%20Mutable.java)** Level: Medium Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree, devide and conquer +- sample problem for segment tree +- build(), update(), rangeQuery() + - build and update are standard + - rangeQuery: handle the range split check +- Null leaf node handling: NO, ideally it will not encounter null leaf. + - in update/rangeQuery: when final state (`start==end`) is reached, the recursive call ends + - there is no way for any node to dive futher into null child. +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. + + + +--- + +**15. [715. Range Module.java](https://github.com/awangdev/LintCode/blob/master/Java/715.%20Range%20Module.java)** Level: Hard Tags: [Segment Tree, TreeSet] + + +#### TreeSet +- start with considering array structure but operation are all O(n) + - what if we can easily find range, and update +- TreeSet: + - build a class `Interval {int start, end;}` + - build a customized `compareTo` that sorts the interval by start at default, but sort by end if a.start==b.start + - Query: TreeSet allow us to find element in O(logn) + - Add Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + - Remove Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + + + +--- + +**16. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + diff --git a/review/Semaphore.md b/review/Semaphore.md new file mode 100644 index 0000000..3bb2051 --- /dev/null +++ b/review/Semaphore.md @@ -0,0 +1,25 @@ + + + +## Semaphore (1) +**0. [1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)** Level: Medium Tags: [Lock, Semaphore, Thread] + + +#### Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 + +#### Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +- semaphore: https://www.geeksforgeeks.org/semaphore-in-java/ + + + +--- + diff --git a/review/Sequence DFS.md b/review/Sequence DFS.md new file mode 100644 index 0000000..c5f2a7e --- /dev/null +++ b/review/Sequence DFS.md @@ -0,0 +1,47 @@ + + + +## Sequence DFS (2) +**0. [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) + + + +--- + +**1. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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 + + + +--- + diff --git a/review/Sequence DP.md b/review/Sequence DP.md new file mode 100644 index 0000000..9e5a91c --- /dev/null +++ b/review/Sequence DP.md @@ -0,0 +1,593 @@ + + + +## Sequence DP (21) +**0. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + + +#### 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题,想不到,就是搞不出。 + + + + +--- + +**1. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**2. [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) + + + +--- + +**3. [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. + + + +--- + +**4. [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), 太慢. + + + +--- + +**5. [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 + + + +--- + +**6. [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) + + + +--- + +**7. [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] + + + + +--- + +**8. [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]。 + + + + + +--- + +**9. [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] + + +跟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] + + + +--- + +**10. [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]) + + + +--- + +**11. [198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +搜刮房子, 相邻的不能碰. 每个房子里有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虽然抽象, 但是更加实用. + +#### Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- calculation + - dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]). The prior house can be either state. + - dp[i][1] = dp[i - 1][0] + nums[i]. The prior house must be `NOT ROBBED`. +- return `Math.max(dp[n - 1][0], dp[n - 1][1])` + + + +--- + +**12. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**13. [70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%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]往往是有特殊状态的. 这里, dp[0]: 1种方式可以原地不动 + - dp[1]=1, 1种方式到达index=1, + - 之后的`dp[2] = dp[0]+dp[1]`: 就是dp[0]的走法 or dp[1]的走法 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**14. [10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + + + +--- + +**15. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**16. [121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### min[n] +- 每天都就交易价格,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 dont need to buyin at 5, 3, 6, 4 later on; + - only need to sell on higher prices. + + + +--- + +**17. [139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- Bottom-up, test simply case. Sequence DP. +- true/false problem, think about dp + - 子问题: 前i个字母, 是否可以有valid break + - check: 1) dp[j] && 2) `if substring(j, i) valid`, for all j = [0 ~ i] + - dp = new boolean[n + 1]; dp[0] = true; + - test: `dp[i] |= dp[j] == true && word[j, n] in dict`. + - Need iterate over i = [0 ~ n], also j = [0, i] + - When there is a way to make dp[i] == true, then break the [j ~ i] loop, move on to test dp[i++] +- Use set dict: `dict.contains()` +- Improvement: O(n) to figure out max length, so we can skip some substring[j~i] dict.contains() +- overall O(n^2) time since the double for loop + +#### DFS +- Top-Down, break into small problems: Check front subString, and put the rest substring into dfs to test +- Memoization: for tested failed substring, record and do NOT test them again. +- Same Improvement as in DP: use max/min length of dict words as boundary + + + +--- + +**18. [256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, depends on the color of dp[n-1] + - 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- Need to have status with dp array: int[index][color status] + - dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. + - dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- time: O(nm), m = # of colors +- space: O(nm) + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 +- space:O(1) + + + +--- + +**19. [265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + + +一排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) + + + +--- + +**20. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + diff --git a/review/Sliding Window.md b/review/Sliding Window.md new file mode 100644 index 0000000..067cf53 --- /dev/null +++ b/review/Sliding Window.md @@ -0,0 +1,401 @@ + + + +## Sliding Window (13) +**0. [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个数字 + + + +--- + +**1. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**2. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**3. [567. Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/567.%20Permutation%20in%20String.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + +#### Method1: Sliding window with left/right Pointers +- Sliding window template: + - 1) Check right pointer and move right + - 2) Move left when necessary + - 3) Verify count == 0 & end state + - Note: normally 2) and 3) are in reversed order; this problem is a bit different +- This is efficient when the number of characters is not limited to 26, the runtime is still O(m + n) +- time: O(m + n), m = s1 length, n = s2 length +- space: O(k), k = # of possible chars, 26 in this case + +#### Method2: Two Pointer, but brutle verify freq count +- 如果做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) 这一步 +- time: (m + n) + - However, if # of possible chars is more than 26 + - For example, `k unique characters`, then the runtime will become: O(m + nk) + +- space: O(k), k = # of possible chars, 26 in this case + + + +--- + +**4. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**5. [239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Method1: Deque, Monotonous queue +- 维持monotonuous queue: `front is always at max` and the `tail end is min`. Always need to return the max end of queue. +- when adding new elements x: + - 1) start from small-end of the queue + - 2) drop all smaller elements + - 3) append to the ending element that is larger than x. + - This is to maintain a front->tail decreasing queue +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`: better than using arraylist, since DeQueue(linked list) removes at O(1) cost +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +- Option1: sliding window template using right/left + while loop + - 1) tailing the new number to max queue, if applicable + - 2) process: record max + - 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] +- Option2: same concept, but use index `i` to mark right, and `i - k + 1` to mark left. +- time: O(n), one pass +- space: O(k), store the deque + + +#### Method2: Heap +- can always build a `class Node{index, val}`; and sort them with PQ of size k +- time: O(nlogK) +- space: O(k) +- this is not linear time, not as good as method1 + + + +--- + +**6. [1040. Moving Stones Until Consecutive II.java](https://github.com/awangdev/LintCode/blob/master/Java/1040.%20Moving%20Stones%20Until%20Consecutive%20II.java)** Level: Medium Tags: [Array, Sliding Window] + + + +#### Analyze to understand +- Make sure to sort array: we need to use the actual number range `A[j] - A[i]`, which requires the array to be sorted +- we want to form a new array where A[n-1] - A[0] + 1 == n; order does not matter but all slots need to be filled consecutivly +- max moves: https://leetcode.com/problems/moving-stones-until-consecutive-ii/discuss/289357/c%2B%2B-with-picture + - A interval will be automatically dropped between A[0] and A[1], if moving A[0] first + - Same, a interval between A[n-2] and A[n-1] will be dropped when moving A[n-1] first + - so largest possible move = firstItem + remaining range size - n items = 1 + (A[n-1] - A[1] + 1) - n = A[n-1] - A[1] -n + 2 + - or A[n-2] - A[0] - n + 2 +- min moves: `Sliding Window` + - use slinding window to assume a right pointer, to make sure A[right] - A[left] + 1 < n; otherwise, move left++ + - check # of included stones + - calculate remaining, which is remaining moves +- Handle min move edge case: + - Consecutive Array up to right = n - 1; need 2 moves to finish + + + +--- + +**7. [346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison +- Note: if we it is calculate moving-window-product, better to use deque :) +- Sliding window? + - It has the spirit of slinding window: 1) maintain a range; 2) check range size `if (queue.size() > size)` + - Though, the solution must use a data structure to store data; it is not the traditional sliding window type of `left/right` pointer problem + + + +--- + +**8. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**9. [1004. Max Consecutive Ones III.java](https://github.com/awangdev/LintCode/blob/master/Java/1004.%20Max%20Consecutive%20Ones%20III.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + + +#### Sliding window + Left/Right Two Pointers +- https://leetcode.com/problems/max-consecutive-ones-iii/solution/ +- Start with DFS thought, but realize redundant calculations: + - we never need to flip 2 indexes [A], [C] from 0 -> 1, if there is a [B] in middle that is 0 too + - the flipped k zeroes must be consecutive too +- we can utilize two pointers to establish a window that captures k zeroes + - always expend right pointer; if seeing an zero, k-- + - note: `len = right - left + 1` is the ongoing max length + - when k < 0 (too many zeros), we need to slide the left side of the window to make sure: + - keep window len + - potentially do k++ when A[left]==0 +- goal: matain a max size of the window, until right == n +- return (right - left). at this moment, right == n, so no need to (right - left + 1) + + + +--- + +**10. [438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)** Level: Medium Tags: [Hash Table, Sliding Window, Two Pointers] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### Method1: Sliding Window, Hash Table. +- A creative way of using anagram char count `hash[c] >= 0` to determine if the curr c is a target char of the deesired anagram. + - because we always reduce hash[c]-- for all characters + - so only the anagram chars would be `hash[c] >= 0` after reducing. +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +- Slinding window always has left/right pointer: + - 1) at any given time move 1 index at a time: expand right window, process rsult, shrink left window + - 2) one of the basic goal is to maintain fixed window size +- algo: + - calc char freq of the target p, and store in a hash[256]; it will be used to distinguish anagram chars: `hash[c] >= 0` indicates a anagram char + - expand right window: move right to expand the window; ONLY when meeting a anagram char, count-- + - process result: if count reduces to 0, one anagram is found + - shrink left window: if (right - left) == p.length(), drop curr left char, and move forward +- how could we rely on only just `count == 0`? + - the hidden pre-condition is `right - left must already be p.length()`, which is validaterd in prev iteration +- time: O(n) +- space: O(1) + +#### Method2: HashTable +- count character apperance -> hash table, here just a int[26] + - use a window to record count++ and count--, in order to compare with countP +- prep the countP takes O(m) time +- time: O(n) + O(m) +- space: O(n) + + + + + +--- + +**11. [632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)** Level: Hard Tags: [Hash Table, Sliding Window, Two Pointers] + + +#### Method1: Sliding Window +- First sort all of the items together by actual val using `Node {int val, int row}` +- Slinding window goal: + - 1) use right to find range that touches all rows, + - 2) use left to shrink the range +- Sliding Window Template + - move right pointer + - Counts[i] = # of elements used in left/right range + - when counts[i] == 0, countUnique++; the number of row/list being included + - when count == row size: + - processing & save shorter range by using left/right Pointers + - move left pointer; when counts[i] == 0, countUnique-- +- time: O(nlogn) for initial sort and then O(n) to process +- space: O(n) +- What is hard here? To think of the idea of counting one usage of each row: + - when each all rows are used at least 1 time + - calculate the min dist + +#### Method2 PQ?, Similar to merging k array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104893/Java-Code-using-PriorityQueue.-similar-to-merge-k-array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/solution/ + + + +--- + +**12. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + diff --git a/review/Slow Fast Pointer.md b/review/Slow Fast Pointer.md new file mode 100644 index 0000000..df24453 --- /dev/null +++ b/review/Slow Fast Pointer.md @@ -0,0 +1,58 @@ + + + +## Slow Fast Pointer (3) +**0. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**1. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**2. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + diff --git a/review/Sort.md b/review/Sort.md new file mode 100644 index 0000000..d30ffdd --- /dev/null +++ b/review/Sort.md @@ -0,0 +1,685 @@ + + + +## Sort (31) +**0. [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), 排序 + + + +--- + +**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. [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/ + + + +--- + +**3. [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来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**4. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + +--- + +**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. [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) + + + +--- + +**7. [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看起来更容易理解. + + + +--- + +**8. [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在某个点小了,加进去当下这个空隙。 + + + +--- + +**9. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**10. [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 + + + +--- + +**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. [[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)** Level: Medium Tags: [Lint, 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) + + + +--- + +**13. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**14. [252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### Method1: sort input and compare if curr.end & next.start overlaps +- sort: `Arrays.sort(intervals, Comparator.comparing(i -> i[0]))` +- time: O(nlogn), space: O(1) + +#### Method2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 +- time: O(nlogn), space O(n) +- Not necessary for this problem, since it requires extra space with pq. + + + +--- + +**15. [259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +1. Similar to 15. 3Sum, but simpler. +1. 只需要count triplet, 但是不需要save triplet, 而且还不需要handle duplicated triplets +1. 发现start, end满足条件时候,(end - start)就是所有 sum target, 那么就end-- +1. 两层循环, O(n2) + + + +--- + +**16. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**17. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**18. [1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)** Level: Medium Tags: [Bucket Sort, DP, Hash Table, Sort] + + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**19. [15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +#### sort array, for loop + two pointer +- 处理duplicate wthin triplets: + - 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. + - 如果是triplet内有重复, 用完start point, 移动到结尾. +- Note: + - 1. 找 value triplets, 多个结果。注意,并非找index。 + - 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + - 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 +- 时间 O(n^2), 两个nested loop + +#### For loop + 2Sum +- HashMap 2Sum. Remember to handle duplicates + - 1. For loop 挑个数字A + - 2. 2Sum 出一堆2个数字的结果 + - 3. Cross match 步骤1里面的A + + + +--- + +**20. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**21. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**22. [973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)** Level: Medium Tags: [Divide and Conquer, Heap, Sort] + + +#### PriorityQueue +- Create customized Point{} class +- Sort by distance +- Maintain queue size <= K + +#### Divide and Conquer +- ?, select sort? + + + +--- + +**23. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**24. [242. Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/242.%20Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +#### int[26] + +#### HashMap + + + +--- + +**25. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**26. [1033. Moving Stones Until Consecutive.java](https://github.com/awangdev/LintCode/blob/master/Java/1033.%20Moving%20Stones%20Until%20Consecutive.java)** Level: Easy Tags: [Basic Implementation, Sort] + + +#### Analyze to understand +- put 3 elements into array, sort and follow below rules: +- min: + - if 3 elements consecutive, 0 move. + - if only 1 pair of the two elemnets consecutive or if they have 1 slot in between, it needs exactly 1 move + - otherwise, at most 2 moves +- max: # of open slots between them (high - low + 1) - n, where n = 3 +- Follow up: `1040. Moving Stones Until Consecutive` is more interesting with special rulese (cannot move to `ending spot`), and it uses sliding window concept + + + +--- + +**27. [274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.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. + +#### Couting Sort / 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 + +#### More about Counting Sort +1. Application: for number/character range +1. Steps: + - Find range, define countArray + - Count element and record in the array + - PreSum the countArray + - Start from beginning of the array, `print & decrese count` to produce the sorted elements + + + + +--- + +**28. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**29. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**30. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + diff --git a/review/Stack.md b/review/Stack.md new file mode 100644 index 0000000..635de43 --- /dev/null +++ b/review/Stack.md @@ -0,0 +1,733 @@ + + + +## Stack (38) +**0. [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, 但是数字可为多位 + + + +--- + +**1. [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 + + + + +--- + +**2. [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. + + + +--- + +**3. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + +--- + +**4. [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()里, 每次换水,查看末尾项. + + + + +--- + +**5. [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的头。 + + + + + +--- + +**6. [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? + + + +--- + +**7. [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. + + + +--- + +**8. [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,也是类似做法 + + + +--- + +**9. [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 + + + +--- + +**10. [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 + + + +--- + +**11. [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) + + + + +--- + +**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. [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) + + + + +--- + +**14. [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了. + + + + +--- + +**15. [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` + + + +--- + +**16. [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] + + +给一个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, 永远在左边上下上下。 + + + +--- + +**17. [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就可以了. + + + +--- + +**18. [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)的巧妙用法. + + + + +--- + +**19. [42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. + +#### Method1: Max wall from both sides +- Array: Left Max Wall vs Right Max Wall. +- 对于每个index而言, vertically 能存放的最大水柱, 就是靠 左 右 最高墙决定的: + - min(leftHighestWall, rightHighestWall) - currHeight. +- time: O(n) +- space: O(n) + +#### Method2: Two Pointers +- Optimization from Method1: two pointer, 还是找左边最高和右边最高. O(1) space. +- 利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +- always limited by the shorter wall: 左边按照maxLeft来计算, 右边按照maxRight来计算. +- time: O(n) +- space: O(1) + +#### Method3: 2 Pointers, start from 2 sides +- 1. 找中间最高bar的index +- 2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条 +- 3. 每次还要减去block自身的height +- time: O(n) +- space: O(1) + +#### Method4: Stack +- 主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +- 最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +- 用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. +- time: O(n) +- space: O(n) + + + + +--- + +**20. [1021. Remove Outermost Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1021.%20Remove%20Outermost%20Parentheses.java)** Level: Easy Tags: [Stack] + +#### Stack +- use stack to hold potential pair +- when stack is empty: detect outtermost element, dont add to final result +- time: O(n), space O(n) + +#### Count occurance +- solution from discussion, time O(n), space O(1) +- save space, but less scalable: think about if there are 100 different pairs, then the couting will be a bit complex to handle. + + + +--- + +**21. [496. Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/496.%20Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack +- use stack to hold all elements + - keep poping if `stack.peek() < num` + - use map to record (top, num) +- time O(n), run through base once and sub-sequence once +- space O(n), stack, map + +#### HashMap +- O(n) space, O(n^2) time worst case + + + +--- + +**22. [1249. Minimum Remove to Make Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1249.%20Minimum%20Remove%20to%20Make%20Valid%20Parentheses.java)** Level: Medium Tags: [Stack, String] + + +#### Stack +- Goal: remove extra '(' or ')' so it is valid. +- Forward thinking: use stack to track '(' and ')', then keep appending partial string to output +- Backward thinking: use stack to filter out false indexes, and remove them in the end + + + + +--- + +**23. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**24. [844. Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/844.%20Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + +#### Method1: Two pointers to backtack from end of string +- time: O(n) +- space: O(1) + +#### Method2: Stack +- need to remove entity just added +- use stack to hold array content; pop if # is found + + + +--- + +**25. [1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)** Level: Hard Tags: [DFS, Stack, String] + +#### Parse exp as sub problem +- Analyze the pattern: 1) single char, 2) with !, 3) with &, | +- Identify sub problem + - Use stack to parse the data in "()", which is a sub problem to solve with recursive call + - Handle &, | case: need to parse multiple +- Be comfortable with string parsing +- Slight improve: + - If see obvious result, directly return evaluation w/o further parsing + - use memo to store evaluated exp + +#### Evaluate inner exp and save back to Stack +- Use '(' and ')' to mark inner exp +- Evaluate the inner exp and save result back to Stack: the result will be 'f' or 't' +- This is slightly slow because: + - It requires all stack items on top to be processed before reaching the operator + - There is no room to optimize even there is simplification for specific operator + + + +--- + +**26. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**27. [20. Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/20.%20Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +#### Stack +- 剥皮过程。解铃还须系铃人 +- 左边的外皮'{['在stack底部 +- 右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**28. [145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Method1: DFS, Recursive +- trivial, 先加left recursively, 再加right recursively, 然后组成头部. +- Option1 w/o helper; option2 with dfs helper. + +#### Method2, Iterative, Stack +- Option1: reversely add to list +- 双stack的思想, 需要在图纸上画一画 + - 原本需要的顺序是: 先leftChild, rightChild, currNode. + - 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild + - 这样出来的结果是reverse的, 那么翻转一下就可以了. +- reverse add: `list.add(0, x)`; +- 利用stack的特点 + - 每次加element进stack的时候, 想要在 bottom/后process的, 先加 + - 想要下一轮立刻process的, 最后push进stack. +- Option2: regular sequence add to stack: add curr, right, left + - Use set to contain the processed children + - only process curr if its children is processed + + + + +--- + +**29. [103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + + +#### 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 + + + +--- + +**30. [636. Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/636.%20Exclusive%20Time%20of%20Functions.java)** Level: Medium Tags: [Stack] + + +#### Stack +- Task time range: + - start range = next task timestamp - start.timestamp + - end range = curr task timestamp - last task timestamp + 1; because end node is inclusive. +- How to think of using stack: a task cannot finish until end is met; a early task cannot stop until a later task ends + - Alternatively, we can use a hashmap to track as well +- Keep track of the timestamp +- make sure to +1 when end node is met because end task is inclusive to this finishing task + + + + +--- + +**31. [94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Method1: DFS +- option1: dfs + rst list to carry results +- option2: Divide and Conquer, 在自己的基础上recursive, 不用helper function +- O(n) time + +#### Method2: Iterative, Stack inorder traversal +- 1) Add root.leftPath all the way to leaf, 2) process curr 3) Move to right if applicable 4) add all right.leftPath +- O(n) time, O(h) space + + + + +--- + +**32. [402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)** Level: Medium Tags: [Greedy, Monotonous Stack, Stack] + + +#### Monotonous Stack (Increasing) +- Greedy: Remove 1) earlier digits(数位靠前权值大), 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. + + + + +--- + +**33. [71. Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/71.%20Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path: + - 1. `.` 代表current directory, 可以忽略. + - 2. `../` 表示previous level. + - 3. double slash 可以忽略. + - 4. empty string 要output `/` +- parse by '/', and go over using stack + - put [folder] in stack + - ".." pop() 1 element of the stack, if anything + - "." stays the same +- output stack reversely: connect with '/', skip tail + + + +--- + +**34. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + +**35. [341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, NestedInteger, Stack] + + +#### Method1: Stack holds items from back of the list +- Option1: always set integer on top of the stack everywhere + - if not, poping stack until the top is integer + - code is easy +- Option2: in hasNext(), faltten the list in stack + +#### Method2: DFS preprocessing. +- 用queue to store all items. Kinda hack. Defeat the purpose of the problem. +- Super fast to query next(), however, needs to holds everything in memory +- O(n) + + + +--- + +**36. [739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)** Level: Medium Tags: [Hash Table, Monotonous Stack, Stack] + + +#### Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) + +#### Method2: `Map `, kinda of like bucket sort +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: + - 1) record tempIndex[currTemp] = i; + - 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result + + +--- + +**37. [272. Closest Binary Search Tree Value II.java](https://github.com/awangdev/LintCode/blob/master/Java/272.%20Closest%20Binary%20Search%20Tree%20Value%20II.java)** Level: Hard Tags: [Stack, Tree] + + +#### Method1: Stack, DFS, Inorder Traversal +- find successors and predecessors using BST (both list will be sorted); in the end, we can easily get top k from the two sorted list + - with BST: **inorder traversal gives us sorted predecessors + - with BST: **reversed-inorder traversal gives us sorted successors + - smallest on top of the stack +- time: O(n) visit all nodes, O(k) to output +- space overall: O(n) to store all nodes + +#### Method2: BFS, brutle force +- Itereate over all nodes and maintain pq (improvemenet point: how to avoid traversing entire tree?) +- prioritize nodes that are closer to target, so we may stop early when result reaches k candidates +- time: O(n*logn) +- kinds slow and not utilizing BST + + + +--- + diff --git a/review/Status DP.md b/review/Status DP.md new file mode 100644 index 0000000..db6b398 --- /dev/null +++ b/review/Status DP.md @@ -0,0 +1,233 @@ + + + +## Status DP (8) +**0. [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. + + + +--- + +**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. [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] + + +跟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] + + + +--- + +**3. [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; + + + +--- + +**4. [198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +搜刮房子, 相邻的不能碰. 每个房子里有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虽然抽象, 但是更加实用. + +#### Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- calculation + - dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]). The prior house can be either state. + - dp[i][1] = dp[i - 1][0] + nums[i]. The prior house must be `NOT ROBBED`. +- return `Math.max(dp[n - 1][0], dp[n - 1][1])` + + + +--- + +**5. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**6. [256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, depends on the color of dp[n-1] + - 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- Need to have status with dp array: int[index][color status] + - dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. + - dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- time: O(nm), m = # of colors +- space: O(nm) + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 +- space:O(1) + + + +--- + +**7. [265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + + +一排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) + + + +--- + diff --git a/review/String.md b/review/String.md new file mode 100644 index 0000000..6870f33 --- /dev/null +++ b/review/String.md @@ -0,0 +1,1481 @@ + + + +## String (78) +**0. [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了 + + + +--- + +**1. [Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)** Level: Easy Tags: [String, Two Pointers] + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + + + +--- + +**2. [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的要求! + + + +--- + +**3. [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 + + + +--- + +**4. [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 + + + +--- + +**5. [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 + + + +--- + +**6. [Orderly Queue.java](https://github.com/awangdev/LintCode/blob/master/Java/Orderly%20Queue.java)** Level: Hard Tags: [Math, String] + + + + +--- + +**7. [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, 看解答 + + + +--- + +**8. [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 + + + +--- + +**9. [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 + + + +--- + +**10. [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 + + + +--- + +**11. [Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)** Level: Medium Tags: [String] + + + +--- + +**12. [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 +还要做一下那. + + + +--- + +**13. [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' + + + + +--- + +**14. [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一下最后一个词 + + + + +--- + +**15. [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) + + + +--- + +**16. [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 + + + +--- + +**17. [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. + + + + +--- + +**18. [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] + + + + +--- + +**19. [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 一样 + + + +--- + +**20. [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) + + + + +--- + +**21. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**22. [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看起来更容易理解. + + + +--- + +**23. [[HackerRank]. Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/[HackerRank].%20Change%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. + + + +--- + +**24. [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 + + + +--- + +**25. [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 + + + +--- + +**26. [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一下。 + + + +--- + +**27. [[lint]. Compare Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Compare%20Strings.java)** Level: Easy Tags: [Lint, String] + +看StringA是不是包括所有 StringB的字符. Anagram + +#### Basic Implementation +- 比较一下大小, null. +- 然后用int[]来count chars from A, count[x]++. 再对照chars in B, count[x]-- +- 如果 count[c] < 0, 就 false. +- O(n) + + + +--- + +**28. [[lint]. Longest Words.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Longest%20Words.java)** Level: Easy Tags: [Hash Table, Lint, String] + +给一串String, 找到最长的长度, 把最长的String全都return + +#### Hash Table +- > +- 存最长值, 最后map.get(max) + + + +--- + +**29. [[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)** Level: Easy Tags: [Array, Lint, 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) + + + + +--- + +**30. [788. Rotated Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/788.%20Rotated%20Digits.java)** Level: Easy Tags: [Basic Implementation, String] + + +#### Basic Implementation of the rules +- [3,4,7] -> cannot rotate, failures. Must NOT have. set1 +- 2,5,6,9 -> good candidates. Must have 1. set2 +- [0,1,8] -> goes back to itself. can have +- loop over [1, N], count=int[10] appearance. + - set1 meet 0 + - set2 meet at least 1 + + + +--- + +**31. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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 + + + +--- + +**32. [408. Valid Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/408.%20Valid%20Word%20Abbreviation.java)** Level: Easy Tags: [Basic Implementation, String] + +tricky: find integer within a string +edge case: leading '0' should not be allow in such abbr. + + + +--- + +**33. [415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)** Level: Easy Tags: [Basic Implementation, Math, String] + + +#### Two Pointer +- Use i, j to process from end of 2 strings +- handle edge case for i, j + - if i < 0, its num = 0 (since we are doing sum, blindly setting 0 is okay) +- Note: `sb.insert(0, x)` is much slower than doing a final `sb.reverse()` + +#### If manually convertin to int[] +1. when converting to int[], remember to reverse string. +1. when converting to int[], remember to reserve extra space for carry + + + +--- + +**34. [1108. Defanging an IP Address.java](https://github.com/awangdev/LintCode/blob/master/Java/1108.%20Defanging%20an%20IP%20Address.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**35. [383. Ransom Note.java](https://github.com/awangdev/LintCode/blob/master/Java/383.%20Ransom%20Note.java)** Level: Easy Tags: [Basic Implementation, String] + +count chars in int[256] + + + +--- + +**36. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**37. [293. Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/293.%20Flip%20Game.java)** Level: Easy Tags: [String] + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + + + +--- + +**38. [686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)** Level: Easy Tags: [Basic Implementation, Edge Case, String] + +Track: 纸上分析edge case. +Validation helps speed it up. + + + +--- + +**39. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**40. [5. Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/5.%20Longest%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)? + +#### Method1: DP of interval +- Very similar to `216. Longest Palindromic Subsequence`, but this problem requires solid substring(i+1, j-1) to be palindromic +- Similarly: process i = n-1, from end so [i + 1, j] is always ready to consume +- boolean dp[i][j] to mark range (i, j) as palindrome or not. +- 在计算 dp[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- time: O(n^2) dp +- space: O(n^2) + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + + + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**41. [58. Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/58.%20Length%20of%20Last%20Word.java)** Level: Easy Tags: [String] + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + + + +--- + +**42. [824. Goat Latin.java](https://github.com/awangdev/LintCode/blob/master/Java/824.%20Goat%20Latin.java)** Level: Easy Tags: [Basic Implementation, String] + + + + +--- + +**43. [151. Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/151.%20Reverse%20Words%20in%20a%20String.java)** Level: Medium Tags: [String] + + +#### Method1: Split string by space, then flip +- Option1: With `s.split(" ")`: No brain, and super fast +- Option2: With `s.split("\\s+")`, it skips space, but slow. Use sb.insert(0, xxx) +- trim() output +- Time, Space: O(n) + +#### Method2: Flip entire, then individual, two pointer +- flip entire string, then flip each individual string +- Time, Space: O(n) + + + +--- + +**44. [67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%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 + +#### reverse +- Reverse string -> Convert to Integer List, add up -> Convert back to string +- pointer 从前向后, 所以只需要 1个pointer. +- 操作复杂, 如上, 证明可以解决. 没必要reverse. + +#### Incorrect: convert to Integer +把binary换成数字作加法. 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**45. [557. Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/557.%20Reverse%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就好了 + + + +--- + +**46. [1249. Minimum Remove to Make Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1249.%20Minimum%20Remove%20to%20Make%20Valid%20Parentheses.java)** Level: Medium Tags: [Stack, String] + + +#### Stack +- Goal: remove extra '(' or ')' so it is valid. +- Forward thinking: use stack to track '(' and ')', then keep appending partial string to output +- Backward thinking: use stack to filter out false indexes, and remove them in the end + + + + +--- + +**47. [443. String Compression.java](https://github.com/awangdev/LintCode/blob/master/Java/443.%20String%20Compression.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**48. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**49. [158. Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/158.%20Read%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就好了. +- alaternatively: use queue to hold so we do not need to worry about size + + + +--- + +**50. [43. Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/43.%20Multiply%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]. + - Loop over num1, each row num1[x] * num2, save to correct index (i + j + 1) + - Note: skip leading '0' during output, but do not delete string "0" + - time,space O(mn) +- Option1: Calculate carry on the fly + - index `curr = i + j + 1`, left index `left = curr - 1`, since we start calculation from end of the array. + - **we only touch right side of the array once**, so we can move the carry off from it, and carry to left index + - code is concise +- Option2: save product first without calculating carry + - save product in each int index + - calculate carry on rst[] and `sb.insert(0, c)` (since we start from end of rst) + - this is actaully faster than Option1, somehow. + +#### Reverse, calculate from index 0, and reverse back +- 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'; + + + +--- + +**51. [680. Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/680.%20Valid%20Palindrome%20II.java)** Level: Easy Tags: [String] + +#### Palindrome String +- delete an index: 有两种情况 +- 用一个boolean parameter来表现state. 如果有其他status, state可以变成 String/enum + + + +--- + +**52. [387. First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/387.%20First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +#### Count appearance with int[256] +- 按照题意, 找到第一个 first index == last index的字母. + +#### Count appearance with hashmap (more scalable) +- 用hashmap存字母的index, 有些重复字母的index就会是个list. +- 找到单一index, 结合成list, sort, return list.get(0) +- slow due + + + +--- + +**53. [345. Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/345.%20Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i && 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 cannot be any match. + + + + +--- + +**55. [28. Implement strStr().java](https://github.com/awangdev/LintCode/blob/master/Java/28.%20Implement%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 + + + +--- + +**56. [1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)** Level: Hard Tags: [DFS, Stack, String] + +#### Parse exp as sub problem +- Analyze the pattern: 1) single char, 2) with !, 3) with &, | +- Identify sub problem + - Use stack to parse the data in "()", which is a sub problem to solve with recursive call + - Handle &, | case: need to parse multiple +- Be comfortable with string parsing +- Slight improve: + - If see obvious result, directly return evaluation w/o further parsing + - use memo to store evaluated exp + +#### Evaluate inner exp and save back to Stack +- Use '(' and ')' to mark inner exp +- Evaluate the inner exp and save result back to Stack: the result will be 'f' or 't' +- This is slightly slow because: + - It requires all stack items on top to be processed before reaching the operator + - There is no room to optimize even there is simplification for specific operator + + + +--- + +**57. [12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)** Level: Medium Tags: [Basic Implementation, Math, String] + + +#### String, Basic implementation +- Parse each digit based on rules +- 1) parse: analyze the situations + + +--- + +**58. [14. Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/14.%20Longest%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) + + + +--- + +**59. [20. Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/20.%20Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +#### Stack +- 剥皮过程。解铃还须系铃人 +- 左边的外皮'{['在stack底部 +- 右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**60. [893. Groups of Special-Equivalent Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/893.%20Groups%20of%20Special-Equivalent%20Strings.java)** Level: Easy Tags: [Basic Implementation, String] + +Mark # of characters can be useful to print string signature + + + +--- + +**61. [91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Method1: DP, Bottom-Up by calculating base case first +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- there can be 2 final states at dp[i]: single digit or double digit. + - validate if `single digit`, dp[i] += dp[i - 1]. Last 1 digit can be decoded. + - validate if `double digit`, dp[i] += dp[i - 2]. Last 2 digits can be decoded. +- note: + - get number from char: `c - '0'` + - validatae single digit != '0', 因为'0' 不在条件之中(A-Z) +- Option1: dp[i] as # ways to decode at index i, including letter i + - The validation is done on: 1) single digit i, or 2) double digit [i-1, i] +- Option2: Partition DP, dp[i] as # ways to decode for first i letters (excluding letter i) + - 定义`dp[i] = 前i个digits最多有多少种decode的方法`: new dp[n + 1]. + - dp[i] += dp[i - x], where x = 1, 2 + - The validation is done on: 1) single digit [i-1], or 2) double digit [i-2, i-1] + - Option2 is better in terms of clean coding. We assume `dp[0]=1` as 1 way to decode 0 digits. + - No need to specially handle length == 1, because it is covered later + - No need to manualy init the first 2-digit case as in Option1 + - Return of `dp[n]` is clean +- 引申: 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +#### Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 + - especially when i = n - 2, handle 2-digt edge case carefully: + - 1) check if two-digit range [i, i+1] is valid + - 2) check if single-digit [i] is valid; if so, += dfs(s, i + 1) +- memo[i]: # ways to decode from [i, n). init with `memo[i]=-1` + + + +--- + +**62. [68. Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/68.%20Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- greedy approach: line up as many words as possible; once exceed the MaxLength, justify the list of words +- Steps + - 1) Split & group + - 2) Juststify a row of words + - 3) clean up last row +- Calcualte bounded row length = `width + (list.size() - 1)`. `list.size()-1` = Minimum amount of slot/space used. +- Calculate max ave spaces ot insert into each slot = `totalExtraSpace/slot` +- `Juststify a row of words`: + - 1) take a list of words and assume minimum 1 space in-between words + - 2) distribute the remaining spaces evenly to each slot +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**63. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**64. [796. Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/796.%20Rotate%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 不变 + + + +--- + +**65. [1041. Robot Bounded In Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/1041.%20Robot%20Bounded%20In%20Circle.java)** Level: Easy Tags: [String] + +简单的character checking. 各个方向, 加加减减. + + + +--- + +**66. [157. Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/157.%20Read%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)` +- Edge Case: + - When there is not enough space to the result buffer, `i + 3 > n`, then only copy what we can: `Math.min(n - i, count)` + - `count < 4` meaning there is not enough content to read, break + + + +--- + +**67. [273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%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来找到结果, 每段的处理方法都是一样的 +- Note: + - StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 + - 注意加 " " 的时候, 如果多余, 要`trim()` + - 注意, `小于20的数字, 有自己的特殊写法, 需要额外handle` + - 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 +- Thinking process: + - `1 ~ 19`: [one, two ... nine, ten, eleven, ...., ninteen] + - `20 ~ x0`: [twenty, thirty, fourty, ... ninety] + - `x00`: hundred: 100 + - thousand: 10^3 + - million: 10^6 + - billion: 10^9 + - trillian: 10^12 way over 2^31, not needed +- plan: + - parse 3 digits at a time + - convert the 3 digit to [xx hundred xx-ty x] + - come up with a string[] + - insert the thousands/million/billion to the string[] + + + +--- + +**68. [125. Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/125.%20Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Two Pointers +- Time O(n), Space O(1). +- 普通方法, 两边check, 速度相比较regular expression更快. leetcode 4ms. +- Use helper functions. + +#### 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/ + + + +--- + +**69. [65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**70. [49. Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/49.%20Group%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 (too slow) +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**71. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + +**72. [1170. Compare Strings by Frequency of the Smallest Character.java](https://github.com/awangdev/LintCode/blob/master/Java/1170.%20Compare%20Strings%20by%20Frequency%20of%20the%20Smallest%20Character.java)** Level: Easy Tags: [Array, String] + + +#### Method1: letter frequency map, kinda bucket sort +- Goal: find word count that fits into `f(queries[i]) < f(W)` +- What if: we can store the f(W) as preSum, then goal: `rst[i] = preSum[end] - preSum[queryWordCount]` + - count(W) and store in count[i] + - calc preSum + - processs queries array +- kinda bucket sort: + - 1) we know the boundary of the word length, so we can create `bucket` + - 2) `function count(w)` can produce a value that sort a word into a specific bucket slot + - extend: the bucket can store keys that links back to the word (if there are follow up questions) +- time: O(m + n) +- space: O(m + n) + +#### Method2: No brain solution, basic impl based on the desc, w/o search. +- time: + - O(nm) to count all words, O(nlogn) to sort the wordCount + - O(nm) to to count all queries + - O(n^2) to perform the match +- space: O(n) + + + +--- + +**73. [8. String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/8.%20String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- Handling use cases +- Parse steps: + - 0. trim space + - 1 parse operator + - 2 trim leading zero + - 3. get number string +- Validation: + - 1. max length over max integer length + - 2. exceed min/max value +- Alternatively, regular expression, but not applicable in interview: if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**74. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**75. [71. Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/71.%20Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path: + - 1. `.` 代表current directory, 可以忽略. + - 2. `../` 表示previous level. + - 3. double slash 可以忽略. + - 4. empty string 要output `/` +- parse by '/', and go over using stack + - put [folder] in stack + - ".." pop() 1 element of the stack, if anything + - "." stays the same +- output stack reversely: connect with '/', skip tail + + + +--- + +**76. [13. Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/13.%20Roman%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 + +#### use map to store combinations + + + + +--- + +**77. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + diff --git a/review/Subarray.md b/review/Subarray.md new file mode 100644 index 0000000..fc6d640 --- /dev/null +++ b/review/Subarray.md @@ -0,0 +1,268 @@ + + + +## Subarray (11) +**0. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**1. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + + +简单的求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] + + +给一串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. [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] + + +#### 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. + + + +--- + +**4. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**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. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**8. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**9. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**10. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + diff --git a/review/Sweep Line.md b/review/Sweep Line.md new file mode 100644 index 0000000..07b759b --- /dev/null +++ b/review/Sweep Line.md @@ -0,0 +1,205 @@ + + + +## Sweep Line (7) +**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 + + + +--- + +**1. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**2. [252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### Method1: sort input and compare if curr.end & next.start overlaps +- sort: `Arrays.sort(intervals, Comparator.comparing(i -> i[0]))` +- time: O(nlogn), space: O(1) + +#### Method2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 +- time: O(nlogn), space O(n) +- Not necessary for this problem, since it requires extra space with pq. + + + +--- + +**3. [850. Rectangle Area II.java](https://github.com/awangdev/LintCode/blob/master/Java/850.%20Rectangle%20Area%20II.java)** Level: Hard Tags: [Segment Tree, Sweep Line] + + +#### Sweep Line + Merge Interval concept +- Inspired by: https://leetcode.com/problems/rectangle-area-ii/discuss/137941/Java-TreeMap-solution-inspired-by-Skyline-and-Meeting-Room +- First consider regular sweep line and realize problem: each vertical line has multiple block segments + - Easy: take a list of vertical dots, and calculate the height diff + - We can use a TreeMap with y-coordinate as key, so to `natural sort by y-coordinate` +- Trick: can NOT remove used y coordinate from map, because the rectangle may continue to expand to right side. +- apply simple equation to calc area: `(long)preY * (p.x - preX)` +- time: + - sort initial queue: O(nlogn) + - process queue: O(n) + - TreeMap insertion: O(logn) + - TreeMap traversal: O(n) + - overall, process queue can be O(n^2) +- space: O(n) + +#### Sweep Line concept, bottom->top sweep +- https://leetcode.com/problems/rectangle-area-ii/discuss/137914/C%2B%2BPython-Discretization-and-O(NlogN) + +#### Segment Tree +- TODO lol + + + +--- + +**4. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**5. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**6. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + diff --git a/review/Thread.md b/review/Thread.md new file mode 100644 index 0000000..d3c6b58 --- /dev/null +++ b/review/Thread.md @@ -0,0 +1,25 @@ + + + +## Thread (1) +**0. [1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)** Level: Medium Tags: [Lock, Semaphore, Thread] + + +#### Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 + +#### Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +- semaphore: https://www.geeksforgeeks.org/semaphore-in-java/ + + + +--- + diff --git a/review/Topological Sort.md b/review/Topological Sort.md new file mode 100644 index 0000000..63422b0 --- /dev/null +++ b/review/Topological Sort.md @@ -0,0 +1,183 @@ + + + +## Topological Sort (6) +**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. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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 + + + +--- + +**2. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**3. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**4. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**5. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + diff --git a/review/Tree DP.md b/review/Tree DP.md new file mode 100644 index 0000000..cf34424 --- /dev/null +++ b/review/Tree DP.md @@ -0,0 +1,45 @@ + + + +## Tree DP (1) +**0. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + diff --git a/review/Tree.md b/review/Tree.md new file mode 100644 index 0000000..e5c6951 --- /dev/null +++ b/review/Tree.md @@ -0,0 +1,1378 @@ + + + +## Tree (69) +**0. [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. + + + +--- + +**1. [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. + + + +--- + +**2. [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 + + + +--- + +**3. [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的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**4. [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. + + + +--- + +**5. [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 + + + +--- + +**6. [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 + + + +--- + +**7. [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开头 + + + +--- + +**8. [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的头。 + + + + + +--- + +**9. [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 + + + +--- + +**10. [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. + + + +--- + +**11. [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. + + + +--- + +**12. [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. + + + +--- + +**13. [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加上去就好. + + + +--- + +**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 + + + +--- + +**15. [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的基础上, 比对左左,左右,右左,右右 + + + +--- + +**16. [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 + + + +--- + +**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. [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? + + + +--- + +**19. [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的判断 + + + +--- + +**20. [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的情况。要把遍历的例子写写 + + + +--- + +**21. [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 + + + + +--- + +**22. [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] + + +给一个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, 永远在左边上下上下。 + + + +--- + +**23. [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] + + +给一个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 + + + +--- + +**24. [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` + + + +--- + +**25. [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; + + + + +--- + +**26. [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 + + + +--- + +**27. [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.valleaf 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) + + + +--- + +**29. [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] + + +给一个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才可以施行. + + + +--- + +**30. [[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Lint, 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! + + + + +--- + +**31. [102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### Method1: BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### Method2: DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**32. [236. Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/236.%20Lowest%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 + - Base Case: 当root == null, p or q is found (`root == p || root == q`),那么就return the root as LCA + - 三种情况: + - 1. leftLCA and rightLCA all found: `each path has found one of p and q node as LCA`. Therefore, curr root is the lowest ancestor + - 2. One of leftLCA and rightLCA is found: return whichever one found + - 3. both LCAs are null, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time O(n), stack space O(n) + + + +--- + +**33. [111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%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 Conquer to find min depth. + - if one of child is null, return the other child depth + 1 + - Pick the min of the two child depth + 1 +- need to visit all nodes + + + + +--- + +**34. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**35. [429. N-ary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/429.%20N-ary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Tree] + + +#### BFS +- use queue to hold each level. O(n) + + + +--- + +**36. [199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%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 +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n nodes in final results + + +#### DFS +- Use Map to override the result at each level +- dfs: + - dfs(node.left) and then dfs(node.right) because we want to log right side last +- record global max depth for iteration purpose +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n stacks (and n nodes in final results) + + + + +--- + +**37. [1008. Construct Binary Search Tree from Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/1008.%20Construct%20Binary%20Search%20Tree%20from%20Preorder%20Traversal.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top Down DFS +- This approach highly relies on the preorder rules + - we can use validation rules to navigate throug hteh preorder array + - use a global index +- time: O(n) + + + + +--- + +**38. [515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS +- faster than BFS, using less space if not couting final rst: stack size, O(logn) +- time: O(n), visit all + +#### Method2: BFS with queue +- loop over queue level and record max + + + + +--- + +**39. [222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, DFS, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### Method1: DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样 + - 如果一样, 直接 2 ^ h - 1就好 + - 不一样的话, 再DFS +- calculate `2^(h)`: 位运算, Math.pow(2, h) = 2 << (h - 1). 神奇! + - 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- time: O(n) visit all nodes on 1 side +- space: O(h) visit all nodes on 1 side + + +#### Method2: Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +#### Method3: Binary Search +- NOT DONE, TODO: https://leetcode.com/problems/count-complete-tree-nodes/solution/ + + + +--- + +**40. [543. Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/543.%20Diameter%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 and CombinedPath +- Option1: Use local single path max & global combined max + - Since the local combined diameter is used for comparision, but not used for specific calculation + - calculate path length (diameter), understand: + - for single path: child single path value + 1 (curr node) + - for combined path including curr node: left child single + right child path +- Option2: record local combined and single path for each iteration + - `int[]{combinedPath, singlePath}`; + - single path: pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; + - combined path `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`, find max from: + - 1) complete left child combined path + - 2) complete right child combined path + - 3) combined path with curr root + - Note: we treat a single node itself with diameter of 1, so we want to -1 in final result + - problem statement wants the path length (not # of nodes or depth) + + + +--- + +**41. [1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +- time: O(n), visit all nodes +- space: O(logn), height of the tree + +#### Method2: HashMap, DFS. +- traverse tree and create `map ` to fast O(1) removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- This approach requires a dfs build of parentMap + - it is same amount of efforts to do the regular dfs removal. + - not a good solution +- time: O(n) +- space: O(n) + + + +--- + +**42. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**43. [104. Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/104.%20Maximum%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 + + + +--- + +**44. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**45. [270. Closest Binary Search Tree Value.java](https://github.com/awangdev/LintCode/blob/master/Java/270.%20Closest%20Binary%20Search%20Tree%20Value.java)** Level: Easy Tags: [BST, Binary Search, Tree] + + +给一个BST, 和一个double target, 走位找到最接近的number. + +Concept: Iterate over all logN nodes in the BST and record the closest. Rather than finding the value at +/- 0.5 precision + +#### Binary Search +- 记录找到过的closest +- Binary Search, 根据current node走位, until null leaf +- time: O(logn), space O(1) since no extra space used + +#### DFS, Recursive +- when less than curr val, consider left +- when greater than curr val, consider right +- dfs到底, 然后每一层比较, 再return +- time: O(logn), space: O(logn) + + + + +--- + +**46. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**47. [110. Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/110.%20Balanced%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + +给一个binary tree, 看是否是height-balanced + +#### DFS with end state +- 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 above solution, but cost more traversal efforts +- 试图计算所有情况 + + + +--- + +**48. [100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### Method1: DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### Method2: BFS with 2 queues +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**49. [112. Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/112.%20Path%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. + + + + +--- + +**50. [427. Construct Quad Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/427.%20Construct%20Quad%20Tree.java)** Level: Medium Tags: [Tree] + + +#### Basic Impl +- build tree recursively by definition +- O(n^2) time and space due to single visit to all nodes + + +--- + +**51. [1026. Maximum Difference Between Node and Ancestor.java](https://github.com/awangdev/LintCode/blob/master/Java/1026.%20Maximum%20Difference%20Between%20Node%20and%20Ancestor.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top-Down DFS +- cache parent max and min => produce current max and min +- pass the max and min to dfs +- compare and return the max of dfs(left), dfs(right) +- time: O(n) +- space: O(logn) +- easy to write, a bit hard to think of + +#### Method2: Bottom-up DFS +- pass up the local (min, max) as object `Val{max, min}` +- easy to think of, but more code to write + + + +--- + +**52. [145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Method1: DFS, Recursive +- trivial, 先加left recursively, 再加right recursively, 然后组成头部. +- Option1 w/o helper; option2 with dfs helper. + +#### Method2, Iterative, Stack +- Option1: reversely add to list +- 双stack的思想, 需要在图纸上画一画 + - 原本需要的顺序是: 先leftChild, rightChild, currNode. + - 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild + - 这样出来的结果是reverse的, 那么翻转一下就可以了. +- reverse add: `list.add(0, x)`; +- 利用stack的特点 + - 每次加element进stack的时候, 想要在 bottom/后process的, 先加 + - 想要下一轮立刻process的, 最后push进stack. +- Option2: regular sequence add to stack: add curr, right, left + - Use set to contain the processed children + - only process curr if its children is processed + + + + +--- + +**53. [938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)** Level: Easy Tags: [BST, Recursion, Tree] + +#### DFS based on BST rules +- sum up the matching L & R + - Find (L,R) on left child + - Find (L,R) on right child + - Find (L,R) covering the root node +- space O(n), worst case O(logn), height of dfs. +- time O(n) to find all nodes between (L, R) + +#### Iterative +- Using stack, or queue, list: any data structure (we are not doing ordered search) +- space O(n) +- time O(n) + + + +--- + +**54. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**55. [103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + + +#### 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 + + + +--- + +**56. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**57. [449. Serialize and Deserialize BST.java](https://github.com/awangdev/LintCode/blob/master/Java/449.%20Serialize%20and%20Deserialize%20BST.java)** Level: Medium Tags: [Tree] + + +#### DFS, Divide and Conquer, Preorder (utilizing BST) +- with BST, we can: + - skip adding the null nodes into the serialized string: `String NULL = "#"` + - In deserialization: use min/max boundary to check if queue.peek() can be added: + - if not meeting BST condition, skip this dfs and let other call to consume the queue +- Faster because it shortens the serialized string + + +#### DFS, Divide and Conquer, Preorder (w/o using BST) +- Take reference in Serialize and Deserialize Binary Tree +- The approach works but does not utilize Binary Search Tree properties + + + +--- + +**58. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**59. [94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Method1: DFS +- option1: dfs + rst list to carry results +- option2: Divide and Conquer, 在自己的基础上recursive, 不用helper function +- O(n) time + +#### Method2: Iterative, Stack inorder traversal +- 1) Add root.leftPath all the way to leaf, 2) process curr 3) Move to right if applicable 4) add all right.leftPath +- O(n) time, O(h) space + + + + +--- + +**60. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**61. [1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS, top-down +- key concetpL the `common ancester of deppest leaves` must have its `two branch being same depth`. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +- time: O(n) traversal 1 pass +- space: O(n) dfs worst case depth + +#### Method2: bottom-up, find leaves first and traverse backwards +- 1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +- 2) use leaf nodes to find way backwards till common node is found; return +- time: O(n) but two passes +- space: O(n) dsf + map storage +- this approach is more brutle and uses exrtra spaces + + + +--- + +**62. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + +**63. [101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### Method1: DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Method2: interative with queue +- put left or right children in pair + +#### Method3: iterative with Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**64. [671. Second Minimum Node In a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/671.%20Second%20Minimum%20Node%20In%20a%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +#### BFS +- min tree: parent node is the min of left/right child +- BFS to traverse the tree and find 1st non-root smallest val +- Improvement area: when `node.val >= nextMin`, no need to dive into node children since it is a min Tree. + +#### DFS +- Find left and right val: + - if left/right equals root.val, that means the left or right sub children could have larger number + - Therefore DFS into left or right +- compare and return min(left, right) + + + +--- + +**65. [366. Find Leaves of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/366.%20Find%20Leaves%20of%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS: store nodes at the same depth +- the leaves are at depth 0 and the root is at highest depth +- dfs: the depth = index of the rst, start from depth = 0 at leaf +- end state: leaf node, add to rst, and return depth + + + + +--- + +**66. [235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的 path +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Beofre lasts common ancestor is found: p and q should follow same search pattern: + - compare with root, then dfs(left) or dfs(right) + - util p and q fall on two sides of root, then return root + - 非常巧妙, 但是也比较局限; 稍微变条件, 就很难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(logn) extra space: recursive stack space +- O(logn) time + + + +--- + +**67. [156. Binary Tree Upside Down.java](https://github.com/awangdev/LintCode/blob/master/Java/156.%20Binary%20Tree%20Upside%20Down.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS +- Given that left & right nodes are always available in pair, at each level: + - perform dfs to find new root: return deepest left node as root + - pick out curr left node and fix current level: + - add right node as left node + - add root as right node + + + +--- + +**68. [272. Closest Binary Search Tree Value II.java](https://github.com/awangdev/LintCode/blob/master/Java/272.%20Closest%20Binary%20Search%20Tree%20Value%20II.java)** Level: Hard Tags: [Stack, Tree] + + +#### Method1: Stack, DFS, Inorder Traversal +- find successors and predecessors using BST (both list will be sorted); in the end, we can easily get top k from the two sorted list + - with BST: **inorder traversal gives us sorted predecessors + - with BST: **reversed-inorder traversal gives us sorted successors + - smallest on top of the stack +- time: O(n) visit all nodes, O(k) to output +- space overall: O(n) to store all nodes + +#### Method2: BFS, brutle force +- Itereate over all nodes and maintain pq (improvemenet point: how to avoid traversing entire tree?) +- prioritize nodes that are closer to target, so we may stop early when result reaches k candidates +- time: O(n*logn) +- kinds slow and not utilizing BST + + + +--- + diff --git a/review/TreeMap.md b/review/TreeMap.md new file mode 100644 index 0000000..fc89430 --- /dev/null +++ b/review/TreeMap.md @@ -0,0 +1,127 @@ + + + +## TreeMap (5) +**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. + + + + +--- + +**1. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**2. [981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)** Level: Medium Tags: [Binary Search, Hash Table, TreeMap] + + +#### Method1: Binary Search +- use hash to store +- binary serach on list of values + +#### Method2: TreeMap +- use hash to store > +- treemap.floorKey(timestamp) finds the top item below certain timestamp + + + +--- + +**3. [1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)** Level: Medium Tags: [Array, Hash Table, TreeMap] + + + +#### Hash Table, TreeMap; atomic save +- store efficiently: use List>. only preserve changed itemd +- if no match, find last modifed item based on snapId, use TreeMap.floorEntry + - map.floorEntry(id) return the item.key lower or equal to id +- Utilize a `buffer: Map` and perform atomic save + + + +--- + +**4. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + diff --git a/review/TreeSet.md b/review/TreeSet.md new file mode 100644 index 0000000..07c1f0b --- /dev/null +++ b/review/TreeSet.md @@ -0,0 +1,103 @@ + + + +## TreeSet (4) +**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. + + + +--- + +**2. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**3. [715. Range Module.java](https://github.com/awangdev/LintCode/blob/master/Java/715.%20Range%20Module.java)** Level: Hard Tags: [Segment Tree, TreeSet] + + +#### TreeSet +- start with considering array structure but operation are all O(n) + - what if we can easily find range, and update +- TreeSet: + - build a class `Interval {int start, end;}` + - build a customized `compareTo` that sorts the interval by start at default, but sort by end if a.start==b.start + - Query: TreeSet allow us to find element in O(logn) + - Add Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + - Remove Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + + + +--- + diff --git a/review/Trie.md b/review/Trie.md new file mode 100644 index 0000000..13239ec --- /dev/null +++ b/review/Trie.md @@ -0,0 +1,305 @@ + + + +## Trie (11) +**0. [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), 太慢. + + + +--- + +**1. [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一下。 + + + + +--- + +**2. [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 +还要做一下那. + + + +--- + +**3. [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] + + +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 +- + + + +--- + +**4. [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里面, 这个想法非常值得思考. + + + +--- + +**5. [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 + + + +--- + +**6. [211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +#### Trie, prefix tree. +- Trie Structure: `boolean isEnd`, `HashMap children` + - trie.addWord: 没node就加,有node就移动 + - trie.search: 没node就return false,有node就移动 +- Alternatively, the hash can be `TrieNode[26]` a fixed size array when applicable + - I like map better for the simplicity to write (w/o converting char -> index) + + + + +--- + +**7. [208. Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/208.%20Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- Trie Structure: + - trace the char to children node: Map + - boolean isEnd to indicate if there is string end with this node + - `TrieNode {boolean isEnd; Map children}`. +- No need to store letter c in TrieNode +- 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。 + + + + + +--- + +**8. [720. Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/720.%20Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序(lexicographically), 排序以后才能逐个看是否partial string已经存在 + - 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 + - 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: + - 长 string 排在前; + - 相等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) + +#### 从最长的word开始做 +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**9. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**10. [745. Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/745.%20Prefix%20and%20Suffix%20Search.java)** Level: Hard Tags: [Trie] + + +#### Chain `suffix # prefix` +- Build Trie for all combinations of `suffix#prefix`; all assigned with weight +- how does it make sure to return largest weight/index? + - when we build trie, always update weight for the path nodes it goes through + - yes, it overrides, but this problem does not care if some words are not found +- Time: + - build: go through all words O(n) * word length * 2 => O(n) + - query: O(1) tree height is just at most 20. +- Space: O(N) store all words + + + +--- + diff --git a/review/Two Pointers.md b/review/Two Pointers.md new file mode 100644 index 0000000..068ab30 --- /dev/null +++ b/review/Two Pointers.md @@ -0,0 +1,1146 @@ + + + +## Two Pointers (57) +**0. [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 + + + + +--- + +**1. [Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)** Level: Easy Tags: [String, Two Pointers] + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + + + +--- + +**2. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + +--- + +**3. [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] + + +给一串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 + + + +--- + +**4. [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, 换位子 + + + +--- + +**5. [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' + + + + +--- + +**6. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**7. [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的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**8. [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链接起来。 + + + +--- + +**9. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + +--- + +**10. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + +--- + +**11. [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) + + + +--- + +**12. [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看起来更容易理解. + + + +--- + +**13. [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), 就不写了 + + + +--- + +**14. [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 + + + +--- + +**15. [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 + + + +--- + +**16. [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了 + + + +--- + +**17. [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 原题有一点点不一样. + + + + +--- + +**18. [[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)** Level: Medium Tags: [Array, Lint, Two Pointers] + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**19. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**20. [42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. + +#### Method1: Max wall from both sides +- Array: Left Max Wall vs Right Max Wall. +- 对于每个index而言, vertically 能存放的最大水柱, 就是靠 左 右 最高墙决定的: + - min(leftHighestWall, rightHighestWall) - currHeight. +- time: O(n) +- space: O(n) + +#### Method2: Two Pointers +- Optimization from Method1: two pointer, 还是找左边最高和右边最高. O(1) space. +- 利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +- always limited by the shorter wall: 左边按照maxLeft来计算, 右边按照maxRight来计算. +- time: O(n) +- space: O(1) + +#### Method3: 2 Pointers, start from 2 sides +- 1. 找中间最高bar的index +- 2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条 +- 3. 每次还要减去block自身的height +- time: O(n) +- space: O(1) + +#### Method4: Stack +- 主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +- 最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +- 用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. +- time: O(n) +- space: O(n) + + + + +--- + +**21. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**22. [360. Sort Transformed Array.java](https://github.com/awangdev/LintCode/blob/master/Java/360.%20Sort%20Transformed%20Array.java)** Level: Medium Tags: [Math, Two Pointers] + + +#### Two Pointers, Math +- Being able to analys the a*x^2 + b*x graph and find the `peak` or `valley` +- Math basics: x^2 dominates the overall curve so it is up to a to determine: + - `valley`: if a < 0, both sides will be small and center will be large. Prioritize larger value. + - `peak`: if a > 0, center will be small and both sides will be large. Prioritize smaller value. + - starting index being 0 or n-1, is driven by `a` + + + +--- + +**23. [849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array, Basic Implementation, Two Pointers] + + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, Two Pointers: 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` + + + + +--- + +**24. [1213. Intersection of Three Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/1213.%20Intersection%20of%20Three%20Sorted%20Arrays.java)** Level: Easy Tags: [Hash Table, Two Pointers] + + +Very similar to 349.Intersection of Two Arrays. + +#### Hash Table +- Use set to check +- Verify duplicates at end rst + +#### Two Pointers +- similar to Intersection of Two Sorted Arrays +- Start from front/back, process 1 item at a time + - if match, move all pointers +- Optoin1: check from back +- Optoin2: check from frotn + + + +--- + +**25. [986. Interval List Intersections.java](https://github.com/awangdev/LintCode/blob/master/Java/986.%20Interval%20List%20Intersections.java)** Level: Medium Tags: [Two Pointers] + + + + +#### Method1: Merge Interval +- There can be 1 overlapping on any interval, calculate the inner intersection: lo(A[i][0], B[j][0]), hi(A[i][1], B[j][1]) + - if low <= hi, a valid intersection exist; add + - also, if A[i][1] < B[j][1]; that is A[i].end < B[j].end, then i++; otherwise j++ + - because the further-away `end` has been used, so move on. +- O(n) + +#### Method2: Sweep line +- code is much more complex (pq, Point, process code... etc) than method1 +- we can use point to track open/close, also want to specify if point belongs to A/B +- mark 2 global parameters: aOpen, bOpen. + - process when A/B close, record if (aOpen, bOpen) has overlaop + - clear up corresponding global parameter after A/B closes +- sort all pointers in priority queue by index +- Point: {boolean isOpen; int index} +- process the queue and remember to clean up all items on same index +- time: O(nlogn) +- space: O(n) + + + + +--- + +**26. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**27. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**28. [80.Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/80.Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Basic +- 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其实就足够. + +#### Follow up: k duplicates, Two Pointers +- when index i and i-1 are diff, use count=1 to start +- in while loop, keep count++ until count==k +- reset when next diff comes in + + + +--- + +**29. [26.Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/26.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 + + + +--- + +**30. [259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +1. Similar to 15. 3Sum, but simpler. +1. 只需要count triplet, 但是不需要save triplet, 而且还不需要handle duplicated triplets +1. 发现start, end满足条件时候,(end - start)就是所有 sum target, 那么就end-- +1. 两层循环, O(n2) + + + +--- + +**31. [977. Squares of a Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/977.%20Squares%20of%20a%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +#### Two Pointers +- negative index i, positive index j + + + +--- + +**32. [67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%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 + +#### reverse +- Reverse string -> Convert to Integer List, add up -> Convert back to string +- pointer 从前向后, 所以只需要 1个pointer. +- 操作复杂, 如上, 证明可以解决. 没必要reverse. + +#### Incorrect: convert to Integer +把binary换成数字作加法. 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**33. [15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +#### sort array, for loop + two pointer +- 处理duplicate wthin triplets: + - 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. + - 如果是triplet内有重复, 用完start point, 移动到结尾. +- Note: + - 1. 找 value triplets, 多个结果。注意,并非找index。 + - 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + - 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 +- 时间 O(n^2), 两个nested loop + +#### For loop + 2Sum +- HashMap 2Sum. Remember to handle duplicates + - 1. For loop 挑个数字A + - 2. 2Sum 出一堆2个数字的结果 + - 3. Cross match 步骤1里面的A + + + +--- + +**34. [19. Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/19.%20Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Two Pointer +- 1 end pointer to define the window based n steps +- 1 pre pointer to track the node before the targeting node +- when end reaches null, remove nth node: link pre and head.next + + + +--- + +**35. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**36. [844. Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/844.%20Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + +#### Method1: Two pointers to backtack from end of string +- time: O(n) +- space: O(1) + +#### Method2: Stack +- need to remove entity just added +- use stack to hold array content; pop if # is found + + + +--- + +**37. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**38. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**39. [567. Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/567.%20Permutation%20in%20String.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + +#### Method1: Sliding window with left/right Pointers +- Sliding window template: + - 1) Check right pointer and move right + - 2) Move left when necessary + - 3) Verify count == 0 & end state + - Note: normally 2) and 3) are in reversed order; this problem is a bit different +- This is efficient when the number of characters is not limited to 26, the runtime is still O(m + n) +- time: O(m + n), m = s1 length, n = s2 length +- space: O(k), k = # of possible chars, 26 in this case + +#### Method2: Two Pointer, but brutle verify freq count +- 如果做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) 这一步 +- time: (m + n) + - However, if # of possible chars is more than 26 + - For example, `k unique characters`, then the runtime will become: O(m + nk) + +- space: O(k), k = # of possible chars, 26 in this case + + + +--- + +**40. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**41. [345. Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/345.%20Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + +**48. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**49. [1004. Max Consecutive Ones III.java](https://github.com/awangdev/LintCode/blob/master/Java/1004.%20Max%20Consecutive%20Ones%20III.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + + +#### Sliding window + Left/Right Two Pointers +- https://leetcode.com/problems/max-consecutive-ones-iii/solution/ +- Start with DFS thought, but realize redundant calculations: + - we never need to flip 2 indexes [A], [C] from 0 -> 1, if there is a [B] in middle that is 0 too + - the flipped k zeroes must be consecutive too +- we can utilize two pointers to establish a window that captures k zeroes + - always expend right pointer; if seeing an zero, k-- + - note: `len = right - left + 1` is the ongoing max length + - when k < 0 (too many zeros), we need to slide the left side of the window to make sure: + - keep window len + - potentially do k++ when A[left]==0 +- goal: matain a max size of the window, until right == n +- return (right - left). at this moment, right == n, so no need to (right - left + 1) + + + +--- + +**50. [283. Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/283.%20Move%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 + + + +--- + +**51. [125. Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/125.%20Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Two Pointers +- Time O(n), Space O(1). +- 普通方法, 两边check, 速度相比较regular expression更快. leetcode 4ms. +- Use helper functions. + +#### 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/ + + + +--- + +**52. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**53. [438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)** Level: Medium Tags: [Hash Table, Sliding Window, Two Pointers] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### Method1: Sliding Window, Hash Table. +- A creative way of using anagram char count `hash[c] >= 0` to determine if the curr c is a target char of the deesired anagram. + - because we always reduce hash[c]-- for all characters + - so only the anagram chars would be `hash[c] >= 0` after reducing. +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +- Slinding window always has left/right pointer: + - 1) at any given time move 1 index at a time: expand right window, process rsult, shrink left window + - 2) one of the basic goal is to maintain fixed window size +- algo: + - calc char freq of the target p, and store in a hash[256]; it will be used to distinguish anagram chars: `hash[c] >= 0` indicates a anagram char + - expand right window: move right to expand the window; ONLY when meeting a anagram char, count-- + - process result: if count reduces to 0, one anagram is found + - shrink left window: if (right - left) == p.length(), drop curr left char, and move forward +- how could we rely on only just `count == 0`? + - the hidden pre-condition is `right - left must already be p.length()`, which is validaterd in prev iteration +- time: O(n) +- space: O(1) + +#### Method2: HashTable +- count character apperance -> hash table, here just a int[26] + - use a window to record count++ and count--, in order to compare with countP +- prep the countP takes O(m) time +- time: O(n) + O(m) +- space: O(n) + + + + + +--- + +**54. [632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)** Level: Hard Tags: [Hash Table, Sliding Window, Two Pointers] + + +#### Method1: Sliding Window +- First sort all of the items together by actual val using `Node {int val, int row}` +- Slinding window goal: + - 1) use right to find range that touches all rows, + - 2) use left to shrink the range +- Sliding Window Template + - move right pointer + - Counts[i] = # of elements used in left/right range + - when counts[i] == 0, countUnique++; the number of row/list being included + - when count == row size: + - processing & save shorter range by using left/right Pointers + - move left pointer; when counts[i] == 0, countUnique-- +- time: O(nlogn) for initial sort and then O(n) to process +- space: O(n) +- What is hard here? To think of the idea of counting one usage of each row: + - when each all rows are used at least 1 time + - calculate the min dist + +#### Method2 PQ?, Similar to merging k array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104893/Java-Code-using-PriorityQueue.-similar-to-merge-k-array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/solution/ + + + +--- + +**55. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + +**56. [611. Valid Triangle Number.java](https://github.com/awangdev/LintCode/blob/master/Java/611.%20Valid%20Triangle%20Number.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Method1: Fix max and backward counting +- Sort nums: O(nlogn) +- Set max value fixed on right side at k + - set 2nd value from right index j + - set last value at min index i + - if `nums[i] + nums[j] > nums[k]`: with fixed j, i can pick from [i, j-1] combinations + - then j--, to pick another j candidate + - maintain a window [i,j]; if invalid, move i++ +- time: O(n^2) +- Note: very similar to 3-sum, fixing 1 index and use 2 pointers to move window + +#### Method2: Fix min and forward counting +- Sort nums: O(nlogn) +- Set min value at i + - set 2nd value at j=i+1; and 3rd value at k=i+2 + - find max of k that fits into triangle + - count all possible k candidates from [j+1, k] + - then move j to a new candidate +- O(n^2) + + + +--- + diff --git a/review/Two Stacks.md b/review/Two Stacks.md new file mode 100644 index 0000000..b449fef --- /dev/null +++ b/review/Two Stacks.md @@ -0,0 +1,34 @@ + + + +## Two Stacks (1) +**0. [145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Method1: DFS, Recursive +- trivial, 先加left recursively, 再加right recursively, 然后组成头部. +- Option1 w/o helper; option2 with dfs helper. + +#### Method2, Iterative, Stack +- Option1: reversely add to list +- 双stack的思想, 需要在图纸上画一画 + - 原本需要的顺序是: 先leftChild, rightChild, currNode. + - 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild + - 这样出来的结果是reverse的, 那么翻转一下就可以了. +- reverse add: `list.add(0, x)`; +- 利用stack的特点 + - 每次加element进stack的时候, 想要在 bottom/后process的, 先加 + - 想要下一轮立刻process的, 最后push进stack. +- Option2: regular sequence add to stack: add curr, right, left + - Use set to contain the processed children + - only process curr if its children is processed + + + + +--- + diff --git a/review/Union Find.md b/review/Union Find.md new file mode 100644 index 0000000..f39f750 --- /dev/null +++ b/review/Union Find.md @@ -0,0 +1,354 @@ + + + +## Union Find (16) +**0. [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拿出来就好了。 + + + +--- + +**1. [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)}` + + + +--- + +**2. [Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)** Level: Medium Tags: [Union Find] + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + + + +--- + +**3. [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. + + + +--- + +**4. [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 + + + +--- + +**5. [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--. + + + +--- + +**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)** 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 + + + +--- + +**7. [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 + + + +--- + +**8. [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? + + + + +--- + +**9. [Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)** Level: Medium Tags: [Union Find] + +Lint还不能跑, 全部按照题意和答案document的. + + + +--- + +**10. [[tool]. UnionFind.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20UnionFind.java)** Level: Medium Tags: [Lint, Union Find] + + +#### Method1: Union Find with Array +- union(), find() +- Path Compresion: store skip father after found, which makes find O(1) + +#### Method2: Union Find with HashMap + + + + + +--- + +**11. [305. Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/305.%20Number%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 + + + +--- + +**12. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + +**13. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**14. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + +**15. [721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Union Find] + +- time O(mn) + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Method1: 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, 最终用来输出. + +#### Method2: 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) + +### DFS +- TODO + + + +--- + diff --git a/review/level/Easy.md b/review/level/Easy.md new file mode 100644 index 0000000..0fb613a --- /dev/null +++ b/review/level/Easy.md @@ -0,0 +1,2827 @@ + + + +## Easy (182) +**0. [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 + + + +--- + +**1. [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) + + + +--- + +**2. [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) 太慢, 不合题意 + + + +--- + +**3. [Reverse String.java](https://github.com/awangdev/LintCode/blob/master/Java/Reverse%20String.java)** Level: Easy Tags: [String, Two Pointers] + +Similar to Reverse Integer. +可以用StringBuffer, 也可以two pointer reverse head/tail + + + +--- + +**4. [Trailing Zeros.java](https://github.com/awangdev/LintCode/blob/master/Java/Trailing%20Zeros.java)** Level: Easy Tags: [Math] + + + +--- + +**5. [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一一对应。 + + + +--- + +**6. [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. + + + +--- + +**7. [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. + + + +--- + +**8. [Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Hamming%20Distance.java)** Level: Easy Tags: [] + +bit: XOR, &, shift>> + + + +--- + +**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. [Paint Fence.java](https://github.com/awangdev/LintCode/blob/master/Java/Paint%20Fence.java)** Level: Easy Tags: [DP, Sequence DP] + + +#### 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题,想不到,就是搞不出。 + + + + +--- + +**11. [Minimum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/Minimum%20Subarray.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Subarray] + + +给一串数组, 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] 但是没什么必要 + + + +--- + +**12. [Binary Gap.java](https://github.com/awangdev/LintCode/blob/master/Java/Binary%20Gap.java)** Level: Easy Tags: [Bit Manipulation] + + +#### Bit Manipulation +- 理解Binary Gap的描述 +- 简单的 `>>`, `&1`, track start and end point 就好了 + + + +--- + +**13. [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 + + + +--- + +**14. [Maximum Average Subarray I.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Average%20Subarray%20I.java)** Level: Easy Tags: [Array, Subarray] + + +简单的求sum of fixed window k, 同时求max avg, 结尾求余数就好. + + + +--- + +**15. [IndexMatch.java](https://github.com/awangdev/LintCode/blob/master/Java/IndexMatch.java)** Level: Easy Tags: [] + +有序, 假设有这样的数字:target. +target 左边的数字,一定不比index大,target右边的数字,一定比index大。 +这样可以binary search.O(logn) + + + +--- + +**16. [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 + + + +--- + +**17. [Reshape the Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/Reshape%20the%20Matrix.java)** Level: Easy Tags: [] + +读例子理解题意. +理清counter case. Basic implementation + + + +--- + +**18. [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] + + + +--- + +**19. [Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + + +--- + +**20. [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()里, 每次换水,查看末尾项. + + + + +--- + +**21. [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 + + + +--- + +**22. [HashWithArray.java](https://github.com/awangdev/LintCode/blob/master/Java/HashWithArray.java)** Level: Easy Tags: [] + + + + +--- + +**23. [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 + + + +--- + +**24. [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. + + + +--- + +**25. [Cosine Similarity.java](https://github.com/awangdev/LintCode/blob/master/Java/Cosine%20Similarity.java)** Level: Easy Tags: [Basic Implementation] + +根据 Cosine Similarity 的公式, basic implementation + + + +--- + +**26. [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作比较;求的所有情况的最大值嘛。 + + + +--- + +**27. [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, 问清楚考官那小伙, 别写顺手。 + + + +--- + +**28. [Singleton.java](https://github.com/awangdev/LintCode/blob/master/Java/Singleton.java)** Level: Easy Tags: [Design] + +让一个class 是 singleton + + + +--- + +**29. [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】上位,所踏过的所有小命啊! + +我这解释太生动了。因为耗费了好长时间思考... + + + +--- + +**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)** 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. + + + +--- + +**31. [[tool] Quick Select - Median.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool]%20Quick%20Select%20-%20Median.java)** Level: Easy Tags: [Array, Lint, Quick Select, Quick Sort, Two Pointers] + + +给一串无序数组, 找到median(sort之后 位置在中间的数字). + +#### Quick Select +- 跟`kth largest element in an Array`的 template一样. +- quickSelect 可以找到 kth 最小的元素 + - 利用这个原理, 找这个kth最小值, 然后如果 == target index, 就找到了我们的median +- 主要步骤: + - 1. partition + - 2. check end state `pivot index ?= target index` + - 3. recursive call one part of the array +- time: 与quickSort不同在于, 每次只要在一半list里面recurring, 所以把O(logn)的时间复杂度降到O(n) + - n + n/2 + n/4 + n/8 + ....+ 1 = O(2n) = O(n) +- space: O(logn), based on recursive stacks + + + + +--- + +**32. [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负数补位. + + + +--- + +**33. [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找合适的数字. + + + +--- + +**34. [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,也是类似做法 + + + +--- + +**35. [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的基础上, 比对左左,左右,右左,右右 + + + +--- + +**36. [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规律。 + + + +--- + +**37. [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 + + + +--- + +**38. [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. + + + + +--- + +**39. [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完 + + + +--- + +**40. [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.有点耐心 + + + +--- + +**41. [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 + + + +--- + +**42. [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的判断 + + + +--- + +**43. [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整除. 一步到位. + + + +--- + +**44. [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的情况。要把遍历的例子写写 + + + +--- + +**45. [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去做 + + + +--- + +**46. [[HackerRank]. Change to Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/[HackerRank].%20Change%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. + + + +--- + +**47. [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了. + + + + +--- + +**48. [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 公式 + + + +--- + +**49. [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 + + + +--- + +**50. [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) + + + +--- + +**51. [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 + + + +--- + +**52. [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` + + + +--- + +**53. [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; + + + + +--- + +**54. [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) + + + +--- + +**55. [Search Insert Position.java](https://github.com/awangdev/LintCode/blob/master/Java/Search%20Insert%20Position.java)** Level: Easy Tags: [] + +一般的binary search. +在结尾判断该return 哪个position。 + + +--- + +**56. [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. + + + +--- + +**57. [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> +- 存最长值, 最后map.get(max) + + + +--- + +**64. [[lint]. Unique Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Unique%20Characters.java)** Level: Easy Tags: [Array, Lint, 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) + + + + +--- + +**65. [[lint]. Lowest Common Ancestor II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Lowest%20Common%20Ancestor%20II.java)** Level: Easy Tags: [Hash Table, Lint, 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! + + + + +--- + +**66. [[lint]. Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Subarray%20Sum.java)** Level: Easy Tags: [Array, Hash Table, Lint, PreSum, Subarray] + + +给一串数字, 找其中的一个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的数组. + + + +--- + +**67. [[lint]. Recover Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Recover%20Rotated%20Sorted%20Array.java)** Level: Easy Tags: [Array, Lint] + +rotate的意思,是有个点断开,把一边的array节选出来放在另外一边。 +Rotate三步: +rotate前半 +rotate后半 +rotate全部 + +注意先找到断点。 + + +--- + +**68. [[tool]. Hash Function.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Hash%20Function.java)** Level: Easy Tags: [Hash Table, Lint] + + +In general, there is no universal recipe to stick to when it comes to implementing hashCode(). +https://www.baeldung.com/java-hashcode + +#### 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会变得太大,所以不能算完和 再 %... + + + +--- + +**69. [36. Valid Sudoku.java](https://github.com/awangdev/LintCode/blob/master/Java/36.%20Valid%20Sudoku.java)** Level: Easy Tags: [Enumeration, Hash Table] + + +#### Hash Set +- 用HashSet存visited row/col/block. + - 在nest for loop里面validate row,col,and block. + - Special: validate block要利用i 和 j 增长的规律 +- i, j are [0~n) can build block boundary in a 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 +- 可能代码稍微复杂一点 + + + +--- + +**70. [359. Logger Rate Limiter.java](https://github.com/awangdev/LintCode/blob/master/Java/359.%20Logger%20Rate%20Limiter.java)** Level: Easy Tags: [Design, Hash Table] + + +#### HashMap +- map: avoid duplicate message, records timestamp for validation +- time: O(1) +- space: O(n) + +#### Queue + Set +- 1) keep a trimmed queue and set (all tasks to be within 10 secs); +- 2) use set to O(1) check if incoming message exists. +- time: O(x), trimQueue() +- space: O(n) + + + +--- + +**71. [198. House Robber.java](https://github.com/awangdev/LintCode/blob/master/Java/198.%20House%20Robber.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +搜刮房子, 相邻的不能碰. 每个房子里有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虽然抽象, 但是更加实用. + +#### Method2: Status DP +- dp[i] depends on nums[i-1] or nums[i-2] based on the state at (i-1) + - use dp[n][2] to store dp[i] and stages + - dp[0][0] = 0; dp[0][1] = nums[0] +- calculation + - dp[i][0] = Math.max(dp[i - 1][1], dp[i - 1][0]). The prior house can be either state. + - dp[i][1] = dp[i - 1][0] + nums[i]. The prior house must be `NOT ROBBED`. +- return `Math.max(dp[n - 1][0], dp[n - 1][1])` + + + +--- + +**72. [21. Merge Two Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/21.%20Merge%20Two%20Sorted%20Lists.java)** Level: Easy Tags: [Linked List] + + +如题 + +#### Basics +- 小的放前。每次比head大小 +- while过后,把没完的list一口气接上。 +- 一开始建一个node用来跑路, 每次都存node.next = xxx。存一个dummy。用来return dummy.next. + + + +--- + +**73. [788. Rotated Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/788.%20Rotated%20Digits.java)** Level: Easy Tags: [Basic Implementation, String] + + +#### Basic Implementation of the rules +- [3,4,7] -> cannot rotate, failures. Must NOT have. set1 +- 2,5,6,9 -> good candidates. Must have 1. set2 +- [0,1,8] -> goes back to itself. can have +- loop over [1, N], count=int[10] appearance. + - set1 meet 0 + - set2 meet at least 1 + + + +--- + +**74. [237. Delete Node in a Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/237.%20Delete%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 + + + +--- + +**75. [448. Find All Numbers Disappeared in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/448.%20Find%20All%20Numbers%20Disappeared%20in%20an%20Array.java)** Level: Easy Tags: [Array, Bucket Sort] + + +#### Method1: Bucket Sort concept, set val to its correct position +- Given: values are [1,n], so val can represent index. Therefore, set val to its correct position +- 小心handle i: + - value是 1-based + - 每次换位, 需要`i--`, 重新省察`nums[i]` + +#### Method2: 做标记 (negative number, or super large number) +- Option1: use negative number to mark visited: + - 很巧妙地运用了标记的方法, 标记成负数,证明visit过。 + - Preserve原数的负数,这样可以继续用此负数的绝对值来寻找原数所该被定的位置。非常巧妙! +- Option2: use large number (larger than n) + - 跟方法2类似,也是做标记,这一次是加上一个大于n的数(因为题目给了n的border),最后check一下就好。为不超Integer.MAX_VALUE, 每次加n前,取余数。 + - 做标记的方法固然快,但是相对来说比较hacky,在常规的代码中,估计不会用到. + + + + +--- + +**76. [849. Maximize Distance to Closest Person.java](https://github.com/awangdev/LintCode/blob/master/Java/849.%20Maximize%20Distance%20to%20Closest%20Person.java)** Level: Easy Tags: [Array, Basic Implementation, Two Pointers] + + +给一排座位, 一个人去坐: 找离两边的人都最远的地方(中间点), return 跟旁边人的最大distance + +是Exam Room 的同种概念, 简单化题目: 这里只考虑一个人就好了 + +#### Basic Implementation, Two Pointers: 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` + + + + +--- + +**77. [408. Valid Word Abbreviation.java](https://github.com/awangdev/LintCode/blob/master/Java/408.%20Valid%20Word%20Abbreviation.java)** Level: Easy Tags: [Basic Implementation, String] + +tricky: find integer within a string +edge case: leading '0' should not be allow in such abbr. + + + +--- + +**78. [415. Add Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/415.%20Add%20Strings.java)** Level: Easy Tags: [Basic Implementation, Math, String] + + +#### Two Pointer +- Use i, j to process from end of 2 strings +- handle edge case for i, j + - if i < 0, its num = 0 (since we are doing sum, blindly setting 0 is okay) +- Note: `sb.insert(0, x)` is much slower than doing a final `sb.reverse()` + +#### If manually convertin to int[] +1. when converting to int[], remember to reverse string. +1. when converting to int[], remember to reserve extra space for carry + + + +--- + +**79. [83. Remove Duplicates from Sorted List.java](https://github.com/awangdev/LintCode/blob/master/Java/83.%20Remove%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会非常清晰 + + + +--- + +**80. [1108. Defanging an IP Address.java](https://github.com/awangdev/LintCode/blob/master/Java/1108.%20Defanging%20an%20IP%20Address.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**81. [1021. Remove Outermost Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1021.%20Remove%20Outermost%20Parentheses.java)** Level: Easy Tags: [Stack] + +#### Stack +- use stack to hold potential pair +- when stack is empty: detect outtermost element, dont add to final result +- time: O(n), space O(n) + +#### Count occurance +- solution from discussion, time O(n), space O(1) +- save space, but less scalable: think about if there are 100 different pairs, then the couting will be a bit complex to handle. + + + +--- + +**82. [766. Toeplitz Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/766.%20Toeplitz%20Matrix.java)** Level: Easy Tags: [Array] + + +#### Check diagonal +- 似乎没什么算法特点, 就是array基本运算, 然后分割成一个helper function去做重复计算, 剪短代码. +- 注意check MxN 的分界线. + +#### Simpler Solution +- the goal is to check [i][j] == [i+1][j+1] for every i and j. + + + +--- + +**83. [953. Verifying an Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/953.%20Verifying%20an%20Alien%20Dictionary.java)** Level: Easy Tags: [Hash Table] + + +#### Hash Table +- mark the char position +- check adjacent words +- Optimization + - a) If s1 equals s2, just return true, no need to continue. + - b) if s2 (app) is a substring of s1(apple), just return false. + + + + +--- + +**84. [1213. Intersection of Three Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/1213.%20Intersection%20of%20Three%20Sorted%20Arrays.java)** Level: Easy Tags: [Hash Table, Two Pointers] + + +Very similar to 349.Intersection of Two Arrays. + +#### Hash Table +- Use set to check +- Verify duplicates at end rst + +#### Two Pointers +- similar to Intersection of Two Sorted Arrays +- Start from front/back, process 1 item at a time + - if match, move all pointers +- Optoin1: check from back +- Optoin2: check from frotn + + + +--- + +**85. [383. Ransom Note.java](https://github.com/awangdev/LintCode/blob/master/Java/383.%20Ransom%20Note.java)** Level: Easy Tags: [Basic Implementation, String] + +count chars in int[256] + + + +--- + +**86. [252. Meeting Rooms.java](https://github.com/awangdev/LintCode/blob/master/Java/252.%20Meeting%20Rooms.java)** Level: Easy Tags: [PriorityQueue, Sort, Sweep Line] + + +- 注意接头点要考虑所有开会结会的情况,不要恰巧漏掉相接的点 +- 开会的是超人。瞬间移动接上下一个会议 + +#### Method1: sort input and compare if curr.end & next.start overlaps +- sort: `Arrays.sort(intervals, Comparator.comparing(i -> i[0]))` +- time: O(nlogn), space: O(1) + +#### Method2: Sweep line +- class Point{pos, flag}, PriorityQueue排序。计算count +- 跟 Number of Airplanes in the Sky 是一个类型的题目 +- time: O(nlogn), space O(n) +- Not necessary for this problem, since it requires extra space with pq. + + + +--- + +**87. [665. Non-decreasing Array.java](https://github.com/awangdev/LintCode/blob/master/Java/665.%20Non-decreasing%20Array.java)** Level: Easy Tags: [Array] + + +- 比较升序的时候, 必须要估计到 `i-1, i, i+1` 三个数位. +- 写出来`i-1, i, i+1`之间的关系, 然后做合理的fix. + 1. reduce nums[i+1] to fix + 1. raise nums[i+1] to fix +- 需要真的fix数组, 因为loop through做比较时会用到fix后的数字. + + + + +--- + +**88. [293. Flip Game.java](https://github.com/awangdev/LintCode/blob/master/Java/293.%20Flip%20Game.java)** Level: Easy Tags: [String] + +#### String +- 可以用 sb.replace(i, j, "replacement string") +- 简单按 window=2 来扫描 +- 原来只需要从'++'转到'--'的情况 +- O(n) + + + +--- + +**89. [686. Repeated String Match.java](https://github.com/awangdev/LintCode/blob/master/Java/686.%20Repeated%20String%20Match.java)** Level: Easy Tags: [Basic Implementation, Edge Case, String] + +Track: 纸上分析edge case. +Validation helps speed it up. + + + +--- + +**90. [111. Minimum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/111.%20Minimum%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 Conquer to find min depth. + - if one of child is null, return the other child depth + 1 + - Pick the min of the two child depth + 1 +- need to visit all nodes + + + + +--- + +**91. [7. Reverse Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/7.%20Reverse%20Integer.java)** Level: Easy Tags: [Math] + + +#### 方法1 +每次加上x%10,然后x不断减小~0 +注意处理MAX_VALUE, MIN_VALUE +符号不重要, 直接处理, 也会保留. + +#### 方法2 +转换成String 然后 reverse +Space O(n), time O(n) + + + +--- + +**92. [303. Range Sum Query - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/303.%20Range%20Sum%20Query%20-%20Immutable.java)** Level: Easy Tags: [DP, PreSum] + + +给一串数字, 求sumRange. + +#### PreSum +- pre sum 的definition +- preSum也是dp[]一种最简易的形式把. +- dp[i], preSum[i]: 前(i-1)个元素的和. + + + +--- + +**93. [674. Longest Continuous Increasing Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/674.%20Longest%20Continuous%20Increasing%20Subsequence.java)** Level: Easy Tags: [Array, Coordinate DP, DP, Sliding Window] + + +找连续的持续上升子序列的长度. + +#### Sliding window +- update the window start index; + - `left` in sliding window + - update when we need to start a new range: `nums[i-1] >= nums[i]` +- calculate the max distance `i - widowStart + 1` +- O(n) time and O(1) space + +#### Simple Array solution +- size++ when meeting condition `nums[i] > nums[i - 1]` +- otherwise, reset size = 1 +- track max all the way + +#### 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 + + + + +--- + +**94. [485. Max Consecutive Ones.java](https://github.com/awangdev/LintCode/blob/master/Java/485.%20Max%20Consecutive%20Ones.java)** Level: Easy Tags: [Array, Basic Implementation] + + +- preserve max +- 清零count + + + +--- + +**95. [896. Monotonic Array.java](https://github.com/awangdev/LintCode/blob/master/Java/896.%20Monotonic%20Array.java)** Level: Easy Tags: [Array] + +basic implementation + + + +--- + +**96. [26.Remove Duplicates from Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/26.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 + + + +--- + +**97. [204. Count Primes.java](https://github.com/awangdev/LintCode/blob/master/Java/204.%20Count%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个数就好了 + + + +--- + +**98. [58. Length of Last Word.java](https://github.com/awangdev/LintCode/blob/master/Java/58.%20Length%20of%20Last%20Word.java)** Level: Easy Tags: [String] + +给一个String, 里面有lower case character 和 ' '. 找最后一个单个word的长度 + +#### basics +- 从末尾找' ', 找到了计算长度 +- 记得要s.trim(), 把首尾的space去掉 + + + +--- + +**99. [496. Next Greater Element I.java](https://github.com/awangdev/LintCode/blob/master/Java/496.%20Next%20Greater%20Element%20I.java)** Level: Easy Tags: [Hash Table, Stack] + + +#### stack +- use stack to hold all elements + - keep poping if `stack.peek() < num` + - use map to record (top, num) +- time O(n), run through base once and sub-sequence once +- space O(n), stack, map + +#### HashMap +- O(n) space, O(n^2) time worst case + + + +--- + +**100. [717. 1-bit and 2-bit Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/717.%201-bit%20and%202-bit%20Characters.java)** Level: Easy Tags: [Array] + +理解题目: +1. single-bit always starts with '0', two-bits always start with '1'. +1. Therefore there is ONLY 1 way to reach end. + +#### 方法1 +Greedy. +从第一个bit开始: 如果 % 2 == 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. + + + +--- + +**101. [53. Maximum Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/53.%20Maximum%20Subarray.java)** Level: Easy Tags: [Array, DFS, DP, Divide and Conquer, PreSum, Sequence DP, Subarray] + + +给一串数组, unsorted, can have negative/positive num. 找数组中间 subarray 数字之和的最大值 + +#### PreSum +- 想着用一用prefix sum. 把值一个个叠加 +- 然后presum[j] - presum[i- 1] 就是 (i,j)之间的和 +- O(n^2), not as sufficient + + +#### Sequence DP +- dp[i]: last element(或包括前i个element), 可能组成的 subarray 的最大sum. + - dp[i] = Math.max(dp[i-1]+lastElement, lastElement(drop dp[i-1])) +- init: + - dp = int[n + 1], + - dp[0]: first 0 items, does not have any sum +- 因为continous sequence, 所以不满足条件的时候, 会断. + - 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种情况: 1) 只要左边, 2) 只要右边, 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: +- Worst case O(n^2): visit all nodes O(n); in dfs: calculates continuous sum (including mid), which is also O(n) + + +--- + +**102. [977. Squares of a Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/977.%20Squares%20of%20a%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +#### Two Pointers +- negative index i, positive index j + + + +--- + +**103. [824. Goat Latin.java](https://github.com/awangdev/LintCode/blob/master/Java/824.%20Goat%20Latin.java)** Level: Easy Tags: [Basic Implementation, String] + + + + +--- + +**104. [405. Convert a Number to Hexadecimal.java](https://github.com/awangdev/LintCode/blob/master/Java/405.%20Convert%20a%20Number%20to%20Hexadecimal.java)** Level: Easy Tags: [Bit Manipulation] + +#### Unsigned Shift, Mask +- Move pointer: move digit after process 4 bits. + - `>>>` Unsigned right shift + - always fills 0 irrespective of the sign of the number +- Mas: `num & 0xf` = `num & 15` + + + +--- + +**105. [509. Fibonacci Number.java](https://github.com/awangdev/LintCode/blob/master/Java/509.%20Fibonacci%20Number.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. + + + + +--- + +**106. [136. Single Number.java](https://github.com/awangdev/LintCode/blob/master/Java/136.%20Single%20Number.java)** Level: Easy Tags: [Bit Manipulation, Hash Table] + +Bit XOR: 当两个bit不同时,return 1. +题目正要消光所有重复出现的数儿留下出现一次的那个. + + + +--- + +**107. [257. Binary Tree Paths.java](https://github.com/awangdev/LintCode/blob/master/Java/257.%20Binary%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 +- time: visit all n nodes +- space: to hold all paths, O(nlogn) + - O((n-1)/2) = O(n) nodes at leaf + - O(logn) depth + +#### 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 + + + + +--- + +**108. [543. Diameter of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/543.%20Diameter%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 and CombinedPath +- Option1: Use local single path max & global combined max + - Since the local combined diameter is used for comparision, but not used for specific calculation + - calculate path length (diameter), understand: + - for single path: child single path value + 1 (curr node) + - for combined path including curr node: left child single + right child path +- Option2: record local combined and single path for each iteration + - `int[]{combinedPath, singlePath}`; + - single path: pick single path + 1: `singlePath = Math.max(left[1] , right[1]) + 1`; + - combined path `combinedPath = Math.max(Math.max(left[0], right[0]), left[1] + right[1] + 1)`, find max from: + - 1) complete left child combined path + - 2) complete right child combined path + - 3) combined path with curr root + - Note: we treat a single node itself with diameter of 1, so we want to -1 in final result + - problem statement wants the path length (not # of nodes or depth) + + + +--- + +**109. [67. Add Binary.java](https://github.com/awangdev/LintCode/blob/master/Java/67.%20Add%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 + +#### reverse +- Reverse string -> Convert to Integer List, add up -> Convert back to string +- pointer 从前向后, 所以只需要 1个pointer. +- 操作复杂, 如上, 证明可以解决. 没必要reverse. + +#### Incorrect: convert to Integer +把binary换成数字作加法. 如果input很大,那么很可能int,long都hold不住。不保险。 + + + +--- + +**110. [557. Reverse Words in a String III.java](https://github.com/awangdev/LintCode/blob/master/Java/557.%20Reverse%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就好了 + + + +--- + +**111. [203. Remove Linked List Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/203.%20Remove%20Linked%20List%20Elements.java)** Level: Easy Tags: [Linked List] + +从linked list 里面去掉所有的 target + +#### Basics +- 如果match: node.next = head.next; +- 如果不match, node 和 head 一起移动 + + + +--- + +**112. [266. Palindrome Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/266.%20Palindrome%20Permutation.java)** Level: Easy Tags: [Hash Table] + + +给String, 看permutation是否能是palindrome + +#### Hash, or ASCII array +- count char occurrance + - 只可以接受一个odd # appearance. +- 考虑所有 256 ASCII code, 如果还要拓展, 就用HashMap +- 注意, 不能assume lower case letter. 应该至少是所有ASCII code + + + +--- + +**113. [339. Nested List Weight Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/339.%20Nested%20List%20Weight%20Sum.java)** Level: Easy Tags: [BFS, DFS, NestedInteger] + + +给一串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. + - 简单的处理nested structure, dfs增加depth. +- time: visit all nodes eventually, O(n), space O(n) + +#### BFS +- bfs, queue, 处理queue.size() for a level +- use a level variable to track levels +- slower since it uses extra space, worst case O(n) of all items + + + +--- + +**114. [189. Rotate Array.java](https://github.com/awangdev/LintCode/blob/master/Java/189.%20Rotate%20Array.java)** Level: Easy Tags: [Array, Rotation] + +#### Rotate array in place +- rotate all +- rotate 2 sides: < k or >= + + +#### Rotate by buffer the k array + + + +--- + +**115. [119. Pascal's Triangle II.java](https://github.com/awangdev/LintCode/blob/master/Java/119.%20Pascal's%20Triangle%20II.java)** Level: Easy Tags: [Array, Basic Implementation] + + +简单处理 list. code is very similar to Pascal triangle I. + +- 注意 `list = Arrays.asList(x, y, z ...)` 给fixed-size list, 不能直接 list.add(). +- Use `new ArrayList<>(Arrays.asList(...))` to wrap it up. + + + + +--- + +**116. [206. Reverse Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/206.%20Reverse%20Linked%20List.java)** Level: Easy Tags: [Linked List] + +#### Iterative +- Linked List的基本操作: 每次insert在开头 +- 用head来循环所有node +- 不需要额外空间 +- Time O(n), Space O(1) + +#### Recursive with a helper function +- source node: head +- target node: new head + + + +--- + +**117. [168. Excel Sheet Column Title.java](https://github.com/awangdev/LintCode/blob/master/Java/168.%20Excel%20Sheet%20Column%20Title.java)** Level: Easy Tags: [Math] + + +#### 基本换算 +- 26位, 像10位一样去思考 +- 从末尾开始, mod %26 可以拿到 末尾数字 remain = n % 26 +- 特殊: remain = 0 的时候, 也就是说n是26的倍数, 末尾应该是 'Z' +- 记录'Z'了之后, n-- + + + + +--- + +**118. [104. Maximum Depth of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/104.%20Maximum%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 + + + +--- + +**119. [349. Intersection of Two Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/349.%20Intersection%20of%20Two%20Arrays.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### Set +- 用到hashset找unique && duplicate: O(m+n) + +#### Binary Search +- 可以用binary search 找数字. +- Note:binary search一定需要array sorted: nLog(m) + + + +--- + +**120. [443. String Compression.java](https://github.com/awangdev/LintCode/blob/master/Java/443.%20String%20Compression.java)** Level: Easy Tags: [Basic Implementation, String] + + + +--- + +**121. [844. Backspace String Compare.java](https://github.com/awangdev/LintCode/blob/master/Java/844.%20Backspace%20String%20Compare.java)** Level: Easy Tags: [Stack, Two Pointers] + + +#### Method1: Two pointers to backtack from end of string +- time: O(n) +- space: O(1) + +#### Method2: Stack +- need to remove entity just added +- use stack to hold array content; pop if # is found + + + +--- + +**122. [9. Palindrome Number.java](https://github.com/awangdev/LintCode/blob/master/Java/9.%20Palindrome%20Number.java)** Level: Easy Tags: [Math] + +#### Reverse half of the number +- build reversed integer 直到midpoint, where x <= reverse +- 如果input双数: x == reverse +- 如果input单数 (而且x>reverse): x == reverse/10 + +#### Consider palindrome +- optionA: compare digit by digit +- optionB: reverse half of the string/int, and compare with other half. + + + + + + +--- + +**123. [771. Jewels and Stones.java](https://github.com/awangdev/LintCode/blob/master/Java/771.%20Jewels%20and%20Stones.java)** Level: Easy Tags: [Hash Table] + + +- 给J 和 S两个string. J里的character是unique 的珠宝, S 里面的character包含珠宝和石头. 找S里面有多少珠宝 +- Basic HashSet + + + +--- + +**124. [141. Linked List Cycle.java](https://github.com/awangdev/LintCode/blob/master/Java/141.%20Linked%20List%20Cycle.java)** Level: Easy Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Method1: Two Pointer: Slow Fast Pointer +- Imagine two runners running on a track at different speed. What happens when the track is actually a circle? +- https://leetcode.com/problems/linked-list-cycle/solution/ +- O(1) sapce: 用快慢指针, `start=head.next`, `end=head.next.next` +- Fast pointer will eventually catch up to slow pointer + +#### Method1: Hash Table +- O(n) space: 用HashMap,一直add elements. 如果有重复,那么很显然是有Cycle + + + +--- + +**125. [680. Valid Palindrome II.java](https://github.com/awangdev/LintCode/blob/master/Java/680.%20Valid%20Palindrome%20II.java)** Level: Easy Tags: [String] + +#### Palindrome String +- delete an index: 有两种情况 +- 用一个boolean parameter来表现state. 如果有其他status, state可以变成 String/enum + + + +--- + +**126. [70. Climbing Stairs.java](https://github.com/awangdev/LintCode/blob/master/Java/70.%20Climbing%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]往往是有特殊状态的. 这里, dp[0]: 1种方式可以原地不动 + - dp[1]=1, 1种方式到达index=1, + - 之后的`dp[2] = dp[0]+dp[1]`: 就是dp[0]的走法 or dp[1]的走法 +- O(n) space, time + +#### 序列DP, 滚动数组 +- [i] only associates with [i-2], [i-1]. +- %2 +- O(1) space + + + +--- + +**127. [747. Largest Number At Least Twice of Others.java](https://github.com/awangdev/LintCode/blob/master/Java/747.%20Largest%20Number%20At%20Least%20Twice%20of%20Others.java)** Level: Easy Tags: [Array] + + +多种简单操作: +- O(n) solution: 找最大值, 和第二大的值, 看是否符合题意, 就行了. +- O(2n) 最简单方法: 可以loop 两遍: 找最值; 作比较. +- O(2n) 举反例: 有一个不满足, 就够反对这个'at least twice of alllll others'. + + + +--- + +**128. [561. Array Partition I.java](https://github.com/awangdev/LintCode/blob/master/Java/561.%20Array%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发现规律: 升序排列会让 `高位的min(pair)` 最大化, 于是`一言不合先排列` +- 找到排列取单数位的规律,再考虑负数和正数的相同规律,即可找到排列求解的方法。 +- sort, O(nlogn) + + + + +--- + +**129. [387. First Unique Character in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/387.%20First%20Unique%20Character%20in%20a%20String.java)** Level: Easy Tags: [Hash Table, String] + + +#### Count appearance with int[256] +- 按照题意, 找到第一个 first index == last index的字母. + +#### Count appearance with hashmap (more scalable) +- 用hashmap存字母的index, 有些重复字母的index就会是个list. +- 找到单一index, 结合成list, sort, return list.get(0) +- slow due + + + +--- + +**130. [345. Reverse Vowels of a String.java](https://github.com/awangdev/LintCode/blob/master/Java/345.%20Reverse%20Vowels%20of%20a%20String.java)** Level: Easy Tags: [String, Two Pointers] + +vowels: 元音字母. 要求reverse所有元音字母. + +##### 方法1: two pointer. +- 前后两个指针, 在while loop里面跑. +- 注意 i [mid+1], on right slope, end = mid +- init: start == 1, end = n - 2; + + + +--- + +**135. [110. Balanced Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/110.%20Balanced%20Binary%20Tree.java)** Level: Easy Tags: [DFS, Tree] + +给一个binary tree, 看是否是height-balanced + +#### DFS with end state +- 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 above solution, but cost more traversal efforts +- 试图计算所有情况 + + + +--- + +**136. [246. Strobogrammatic Number.java](https://github.com/awangdev/LintCode/blob/master/Java/246.%20Strobogrammatic%20Number.java)** Level: Easy Tags: [Enumeration, Hash Table, Math, Two Pointers] + + +根据题意枚举, 再根据规则basic implementation + +#### HashTable + Two Pointer +- compare left/right + +#### Alter input +- flip number (6 and 9), and then reverse the string, see if the string is the same. +- takes more + + + + +--- + +**137. [100. Same Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/100.%20Same%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +给两个 binary tree, 看两个tree是否identical. + +#### Method1: DFS +- DFS. 确定leaf条件, && with all dfs(sub1, sub2). +- 这里无论如何都要走过所有的node, 所以dfs更加合适, 好写. + +#### Method2: BFS with 2 queues +- 两个queue存每个tree的所有current level node. Check equality, check queue size. +- Populate next level by nodes at current level. + + + +--- + +**138. [88. Merge Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Merge%20Sorted%20Array.java)** Level: Easy Tags: [Array, Two Pointers] + + +给两个排好序的数组, merge. 其中一个数组nums1有多余的位置 + +#### Basics +- A够长,那么可以从A的尾部开始加新元素: 从尾部,是大数字优先排末尾的. +- Deal with remaining: + - When A values are used up, put remian of B into it + - When B values are finished, there is nothing todo. The remain of A is already in place. + + + +--- + +**139. [112. Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/112.%20Path%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. + + + + +--- + +**140. [463. Island Perimeter.java](https://github.com/awangdev/LintCode/blob/master/Java/463.%20Island%20Perimeter.java)** Level: Easy Tags: [Hash Table] + +#### Brutle, Count Blocks and Walls +- 每个格子 +4 个墙; +- 每个shared的墙要减去: 从每个island走去另外一个, 都-1 (最终没面墙, -2) + +#### Hash Table +- 把每个block相连的block全部存在以当下block为key的list里面. 那么这里需要把2D坐标, 转化成一个index. +- 最后得到的map, 所有的key-value应该都有value-key的反向mapping, 那么就可以消除干净, 每一次消除就是一个shared wall. +- 一点点optimization: DFS去找所有的island, 如果island的图非常大, 而island本身不大,那么适合optimize. +- 但是整体代码过于复杂. 不建议写. + + + + +--- + +**141. [170. Two Sum III - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/170.%20Two%20Sum%20III%20-%20Data%20structure%20design.java)** Level: Easy Tags: [Design, Hash Table, Memoization] + + +#### Hash Table, Memo +- Use Map to store the inputs +- Iterate over map to find the pair +- Use Set memo to store the success cases for fast return +- time: O(n), loop over all elements in map +- space: O(n), store all elements in map & memoization set + + + +--- + +**142. [122. Best Time to Buy and Sell Stock II.java](https://github.com/awangdev/LintCode/blob/master/Java/122.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock%20II.java)** Level: Easy Tags: [Array, DP, Greedy, Sequence DP, Status DP] + + +和Stock I 的区别:可以买卖多次,求总和的最大盈利. + +#### 几种不同的思路 +1. Greedy, 每次有相邻的diff符合profit条件, 就卖了, 最后把所有的diff加在一起. 计算delta, 其实简单粗暴, 也还不错. +2. 从低谷找peek, sell. +3. DP. (old dp solution BuyOn[], SellOn[]) +4. DFS计算所有(timeout).Improvement on DFS -> DP -> calculate sellOn[i] and buyOn[i], and then return buyOn[i]. 有点难想, 但是代码简单, 也是O(n) + +#### 1. Greedy +- 画图, 因为可以无限买卖, 所以只要有上升, 就有profit +- 所有卖掉的, 平移加起来, 其实就是overall best profit +- 当天卖光, 再买进. +- O(n) time + +#### 2. 找涨幅最大的区间, 买卖 +- 找到低谷,买进:peek = start + 1 时候,就是每次往前走一步;若没有上涨趋势,继续往低谷前进。 +- 涨到峰顶,卖出:一旦有上涨趋势,进一个while loop,涨到底, 再加个profit. +- profit += prices[peek - 1] - prices[start]; 挺特别的。 +- 当没有上涨趋势时候,peek-1也就是start, 所以这里刚好profit += 0. + +#### 3. DP, sequence dp + status +- 想知道前i天的最大profit, 那么用sequence DP: +- dp[i]: represents 前i天的最大profit +- 当天的是否能卖, 取决于昨天是否买进, 也就是 `昨天买了或者卖了的状态`: 加状态, dp[i][0], dp[i][1] +- `买`的状态 `dp[i][0]` = + - 1. 今天买入, 昨天sell后的dp[i-1][1]值 - 今天的价格price[i]; + - 2. 今天不买, compare with 昨天buy的dp[i-1][0]值. +- `卖`的状态 `dp[i][1]` = + - 1. 今天卖出, 昨天buy的 dp[i-1][0]值 + price[i]; + - 2. 今天不卖, compare with 昨天sell后的 dp[i-1][1]值. +- 注意init: + - dp[0][0] = dp[0][1] = 0; // day 0 buy/sell: no profit regardless of buy/sell status + - dp[1][1] = 0; // day 1 sell: haven't bought, so just 0 profit. + - dp[1][0] = - prices[0]; // day 1 buy: just cost of prices[0] +- Note: `int[][] dp = new int[n+1][2]`以后, index是 1-based. for loop 注意使用 `prices[i-1]` +- O(n) time, O(n) space + +##### Rolling Array +- [i] 和 [i - 1] 相关联, roll + + + + +--- + +**143. [14. Longest Common Prefix.java](https://github.com/awangdev/LintCode/blob/master/Java/14.%20Longest%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) + + + +--- + +**144. [243. Shortest Word Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/243.%20Shortest%20Word%20Distance.java)** Level: Easy Tags: [Array, Two Pointers] + + + +#### Two Pointers +- Use 2 pointers to record **most recent** pos of word1 and word2 + - move pointer i [0 ~ n) and keep refreshing pos1 and pos2 + - both pos1 and pos2 will be as adjacent as possible since they both moving towards same direction +- keep recalculating best distance when either word is matched +- 而同一时间,只需要计算一个最近的curr distance: greedy不断变更A/B index, 做比较 + + + + +--- + +**145. [414. Third Maximum Number.java](https://github.com/awangdev/LintCode/blob/master/Java/414.%20Third%20Maximum%20Number.java)** Level: Easy Tags: [Array, PriorityQueue] + +#### pq +- 注意special case: `when less than 3 items, return maximum` +- PQ是natural order: remain peek() will be the 3rd maximum + + + + +--- + +**146. [20. Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/20.%20Valid%20Parentheses.java)** Level: Easy Tags: [Stack, String] + + +#### Stack +- 剥皮过程。解铃还须系铃人 +- 左边的外皮'{['在stack底部 +- 右边的外皮应该和stack顶上的左外皮一一对应 + + + +--- + +**147. [893. Groups of Special-Equivalent Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/893.%20Groups%20of%20Special-Equivalent%20Strings.java)** Level: Easy Tags: [Basic Implementation, String] + +Mark # of characters can be useful to print string signature + + + +--- + +**148. [169. Majority Element.java](https://github.com/awangdev/LintCode/blob/master/Java/169.%20Majority%20Element.java)** Level: Easy Tags: [Array, Bit Manipulation, Divide and Conquer, Moore Voting, Sort] + + +#### HashMap count occurance +- Time, Space: O(n) + + +#### Moore Voting Algorithm 投票消减 +- 前提: input必须valid, 比较罕见少用. Moore Voting Algorithm: https://en.wikipedia.org/wiki/Boyer%E2%80%93Moore_majority_vote_algorithm +- 与当下candidate相同, vote++. 与之不同, vote--. +- Majority Number是指超半数, 多1个就行: 消减至最后, 会至少有vote>=1. +- 那么: vote++, vote--到最后剩下的就是winner. +- 这个方法比较greedy, 前提是: valid input, 是一定有一个majority number的。否则此法不成。[1,1,1,2,2,2,3]是个invalid input,结果是3,当然也错了。 +- time: O(n), space O(1) + +#### Sort +- sort entire nums array +- assume there is a solution, then nums[n/2] must be that majority num +- time O(nlogn) + +#### Divide and Conquer +1. Recursive approach +1. For ange rangeA & rangeB, rangeA has majorElementA and rangeB has majorElementB + - majorElementA = majorElementB, of course this element will be the major number for whole range + - if majorElementA != majorElementB, then need to count both elements in whole range + - of course the larger occurance will be the major num + +#### Bit manipulation +- TODO + +#### Related Problems +- Majority Number II,超1/3, 那么就分三份处理,countA, countB来计算最多出现的两个。 +- Majority Number III, 超1/k, 那么自然分k份。这里用到 HashMap。 + + + +--- + +**149. [234. Palindrome Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/234.%20Palindrome%20Linked%20List.java)** Level: Easy Tags: [Linked List, Two Pointers] + + +#### Reverse Linked List +- Palindrome概念很简单: 两边回溯相等. However: + - 1) cannot random access index on linkded list + - 2) cannot reverse iterating linked list +- solution: reverse linked list: 遍历接开头 + - 1) 用快慢指正找到mid point + - 2) reverse 2nd half + - 3) compare leftList and rightList +- Time O(n), 而且不需要用额外的空间(只是调换半个list的内部顺序), 所以空间O(1) + + + +--- + +**150. [202. Happy Number.java](https://github.com/awangdev/LintCode/blob/master/Java/202.%20Happy%20Number.java)** Level: Easy Tags: [Hash Table, Math] + + +Basic Implementation of the requirements. + +用HashSet存查看过的数值。若重复,return false. + + + +--- + +**151. [69. Sqrt(x).java](https://github.com/awangdev/LintCode/blob/master/Java/69.%20Sqrt(x).java)** Level: Easy Tags: [Binary Search, Math] + +#### sqrt(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 + + + +--- + +**152. [876. Middle of Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/876.%20Middle%20of%20Linked%20List.java)** Level: Easy Tags: [Linked List] + +找Linked List的中间node + +#### 快慢指针 +- 不在乎slow是不是到底,因为fast肯定先到。 +- 确保fast, fast.next不是Null就好 + + + +--- + +**153. [219. Contains Duplicate II.java](https://github.com/awangdev/LintCode/blob/master/Java/219.%20Contains%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 +- Time O(nm), m = # of duplicates. 太慢 +- 记录每个element value的index in the list +- 一旦有重复element重复, 就把整个list of indexes 端出来, 查看有没有符合条件的: (index - i) <= k + + +#### 这两种做法的区别很有艺术感觉 +- 方法1是限定选拔的candidate, 不合格就去掉, 那么一旦有符合条件的(duplicates), 那么一定中, 剩下的就不看了. +- 方法2是把符合条件的index找出来, 集中处理, 但是所有candidate都会选出来 +- 就好像招人一样: 一种是遇到好的就停止; 第二种是看过所有人, 从中选拔最好的. 显然第一种更快. + + + + +--- + +**154. [205. Isomorphic Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/205.%20Isomorphic%20Strings.java)** Level: Easy Tags: [Hash Table] + + +#### HashMap +- check 2 failture cases: + - same key, value not matching + - two key maps to same value + + + +--- + +**155. [346. Moving Average from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/346.%20Moving%20Average%20from%20Data%20Stream.java)** Level: Easy Tags: [Design, Queue, Sliding Window] + + +给一个interface, design一个structure, 能够计算moving window average. + +#### Queue +- 读懂题目, 注意average 和 window 的处理. +- 简单的queue.size() comparison +- Note: if we it is calculate moving-window-product, better to use deque :) +- Sliding window? + - It has the spirit of slinding window: 1) maintain a range; 2) check range size `if (queue.size() > size)` + - Though, the solution must use a data structure to store data; it is not the traditional sliding window type of `left/right` pointer problem + + + +--- + +**156. [938. Range Sum of BST.java](https://github.com/awangdev/LintCode/blob/master/Java/938.%20Range%20Sum%20of%20BST.java)** Level: Easy Tags: [BST, Recursion, Tree] + +#### DFS based on BST rules +- sum up the matching L & R + - Find (L,R) on left child + - Find (L,R) on right child + - Find (L,R) covering the root node +- space O(n), worst case O(logn), height of dfs. +- time O(n) to find all nodes between (L, R) + +#### Iterative +- Using stack, or queue, list: any data structure (we are not doing ordered search) +- space O(n) +- time O(n) + + + +--- + +**157. [242. Valid Anagram.java](https://github.com/awangdev/LintCode/blob/master/Java/242.%20Valid%20Anagram.java)** Level: Easy Tags: [Hash Table, Sort] + + +#### int[26] + +#### HashMap + + + +--- + +**158. [217. Contains Duplicate.java](https://github.com/awangdev/LintCode/blob/master/Java/217.%20Contains%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 + + + +--- + +**159. [796. Rotate String.java](https://github.com/awangdev/LintCode/blob/master/Java/796.%20Rotate%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 不变 + + + +--- + +**160. [1041. Robot Bounded In Circle.java](https://github.com/awangdev/LintCode/blob/master/Java/1041.%20Robot%20Bounded%20In%20Circle.java)** Level: Easy Tags: [String] + +简单的character checking. 各个方向, 加加减减. + + + +--- + +**161. [157. Read N Characters Given Read4.java](https://github.com/awangdev/LintCode/blob/master/Java/157.%20Read%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)` +- Edge Case: + - When there is not enough space to the result buffer, `i + 3 > n`, then only copy what we can: `Math.min(n - i, count)` + - `count < 4` meaning there is not enough content to read, break + + + +--- + +**162. [121. Best Time to Buy and Sell Stock.java](https://github.com/awangdev/LintCode/blob/master/Java/121.%20Best%20Time%20to%20Buy%20and%20Sell%20Stock.java)** Level: Easy Tags: [Array, DP, Sequence DP] + +给个array of stock prices, 限制能交易(买/买)一轮, 问如何找到最大profit. + +#### min[n] +- 每天都就交易价格,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 dont need to buyin at 5, 3, 6, 4 later on; + - only need to sell on higher prices. + + + +--- + +**163. [605. Can Place Flowers.java](https://github.com/awangdev/LintCode/blob/master/Java/605.%20Can%20Place%20Flowers.java)** Level: Easy Tags: [Array, Greedy] + + +#### Array +- just check flowerbed[i-1] & flowerbed[i+1] and count + + + +--- + +**164. [1. Two Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1.%20Two%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. + + + + +--- + +**165. [118. Pascal's Triangle.java](https://github.com/awangdev/LintCode/blob/master/Java/118.%20Pascal's%20Triangle.java)** Level: Easy Tags: [Array, Basic Implementation, List] + + + + +--- + +**166. [283. Move Zeroes.java](https://github.com/awangdev/LintCode/blob/master/Java/283.%20Move%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 + + + +--- + +**167. [1033. Moving Stones Until Consecutive.java](https://github.com/awangdev/LintCode/blob/master/Java/1033.%20Moving%20Stones%20Until%20Consecutive.java)** Level: Easy Tags: [Basic Implementation, Sort] + + +#### Analyze to understand +- put 3 elements into array, sort and follow below rules: +- min: + - if 3 elements consecutive, 0 move. + - if only 1 pair of the two elemnets consecutive or if they have 1 slot in between, it needs exactly 1 move + - otherwise, at most 2 moves +- max: # of open slots between them (high - low + 1) - n, where n = 3 +- Follow up: `1040. Moving Stones Until Consecutive` is more interesting with special rulese (cannot move to `ending spot`), and it uses sliding window concept + + + +--- + +**168. [125. Valid Palindrome.java](https://github.com/awangdev/LintCode/blob/master/Java/125.%20Valid%20Palindrome.java)** Level: Easy Tags: [String, Two Pointers] + +验证string是不是 palindrome. 只考虑 alphanumeric, 其他字符可以忽略 + +#### Two Pointers +- Time O(n), Space O(1). +- 普通方法, 两边check, 速度相比较regular expression更快. leetcode 4ms. +- Use helper functions. + +#### 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/ + + + +--- + +**169. [160. Intersection of Two Linked Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/160.%20Intersection%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 + + + +--- + +**170. [724. Find Pivot Index.java](https://github.com/awangdev/LintCode/blob/master/Java/724.%20Find%20Pivot%20Index.java)** Level: Easy Tags: [Array, PreSum] + + +#### PreSum +- want to find `nums[i - 1] == nums[n - 1] - nums[i]`, given: + - preSum[i], sum from [0, i] inclusive + - preSum[j] - preSum[i] = [i+1, j] inclusive +- O(n) to build preSum +- O(n) to find pivot + + + +--- + +**171. [350. Intersection of Two Arrays II.java](https://github.com/awangdev/LintCode/blob/master/Java/350.%20Intersection%20of%20Two%20Arrays%20II.java)** Level: Easy Tags: [Binary Search, Hash Table, Sort, Two Pointers] + + +#### HashMap +- Map of nums1: +- check nums2 against nums1 map +- time:O(n + m) +- space:O(n + m) + +#### Binary Search +- + + + +--- + +**172. [720. Longest Word in Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/720.%20Longest%20Word%20in%20Dictionary.java)** Level: Easy Tags: [Hash Table, Trie] + + +给串word[], 找最长的Word, 满足条件: 这个Word可以从 word[] 里面一个字母一个字母被build出来. + +如果多种答案, respect smallest lexicographical order. + +#### Sort, HashSet +- 先排序(lexicographically), 排序以后才能逐个看是否partial string已经存在 + - 用 set.contains(substring(0, n - 1)) 来查看上一步的 substring 是否存在 + - 如果找到, 因为已经按照字母表排序, 找到的这个肯定是这个长度里面最符合的解答. +- 然后brutally找下一个更大的. +- Sort O(n log n), O(n) set space + +#### Trie +- 可以先sort words Array: + - 长 string 排在前; + - 相等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) + +#### 从最长的word开始做 +- 按大小排序 -> 从最大的开始做contains()的比较 -> 结果再按照字母表顺序(lexicographically) sort一下. +- 但是Collections.sort()了两次, 而且再list.contains(), 比较慢 + + + + +--- + +**173. [760. Find Anagram Mappings.java](https://github.com/awangdev/LintCode/blob/master/Java/760.%20Find%20Anagram%20Mappings.java)** Level: Easy Tags: [Hash Table] + + +- HashMap 存index list +- 遍历一遍数组A, 列举出所有元素 + + + +--- + +**174. [1170. Compare Strings by Frequency of the Smallest Character.java](https://github.com/awangdev/LintCode/blob/master/Java/1170.%20Compare%20Strings%20by%20Frequency%20of%20the%20Smallest%20Character.java)** Level: Easy Tags: [Array, String] + + +#### Method1: letter frequency map, kinda bucket sort +- Goal: find word count that fits into `f(queries[i]) < f(W)` +- What if: we can store the f(W) as preSum, then goal: `rst[i] = preSum[end] - preSum[queryWordCount]` + - count(W) and store in count[i] + - calc preSum + - processs queries array +- kinda bucket sort: + - 1) we know the boundary of the word length, so we can create `bucket` + - 2) `function count(w)` can produce a value that sort a word into a specific bucket slot + - extend: the bucket can store keys that links back to the word (if there are follow up questions) +- time: O(m + n) +- space: O(m + n) + +#### Method2: No brain solution, basic impl based on the desc, w/o search. +- time: + - O(nm) to count all words, O(nlogn) to sort the wordCount + - O(nm) to to count all queries + - O(n^2) to perform the match +- space: O(n) + + + +--- + +**175. [94. Binary Tree Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/94.%20Binary%20Tree%20Inorder%20Traversal.java)** Level: Easy Tags: [Hash Table, Stack, Tree] + + +Inorder traverse Binary Tree + +#### Method1: DFS +- option1: dfs + rst list to carry results +- option2: Divide and Conquer, 在自己的基础上recursive, 不用helper function +- O(n) time + +#### Method2: Iterative, Stack inorder traversal +- 1) Add root.leftPath all the way to leaf, 2) process curr 3) Move to right if applicable 4) add all right.leftPath +- O(n) time, O(h) space + + + + +--- + +**176. [278. First Bad Version.java](https://github.com/awangdev/LintCode/blob/master/Java/278.%20First%20Bad%20Version.java)** Level: Easy Tags: [Binary Search] + + +#### Method1: Check is-NOT-BadVersion +- simply binary Search: if not bad, assign `start = mid+1` + +#### Method2: Check ifBadVersion +- 根据isBadVersion的性质,判断还如何end=mid or start=mid. +- A bit more code to handle + + + +--- + +**177. [101. Symmetric Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/101.%20Symmetric%20Tree.java)** Level: Easy Tags: [BFS, DFS, Tree] + + +检查tree是否symmetric + +注意Symmetric Binary Tree的例子和定义: 是镜面一样的对称. 并不是说左右两个sub-tree相等。 + +#### Method1: DFS +- Recursively check symmetrically相对应的Node. +- 每个node的children都和镜面另外一边相对的node的children刚好成镜面反射位置。 + +#### Method2: interative with queue +- put left or right children in pair + +#### Method3: iterative with Stack +- stack1: 左手边sub-tree先加left, 再加right child; +- stack2: 右手边sub-tree先加right child, 再加left child。 +- process时,若symmetric,所有stack里面出来的node会一一对应。 + + + +--- + +**178. [13. Roman to Integer.java](https://github.com/awangdev/LintCode/blob/master/Java/13.%20Roman%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 + +#### use map to store combinations + + + + +--- + +**179. [671. Second Minimum Node In a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/671.%20Second%20Minimum%20Node%20In%20a%20Binary%20Tree.java)** Level: Easy Tags: [BFS, Tree] + + +#### BFS +- min tree: parent node is the min of left/right child +- BFS to traverse the tree and find 1st non-root smallest val +- Improvement area: when `node.val >= nextMin`, no need to dive into node children since it is a min Tree. + +#### DFS +- Find left and right val: + - if left/right equals root.val, that means the left or right sub children could have larger number + - Therefore DFS into left or right +- compare and return min(left, right) + + + +--- + +**180. [235. Lowest Common Ancestor of a Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/235.%20Lowest%20Common%20Ancestor%20of%20a%20Binary%20Search%20Tree.java)** Level: Easy Tags: [BST, DFS, Tree] + + +给 binary search tree root, q node, p node. 找到p q 的lowest common ancestor + +#### Find path with BST +- 利用 BST 的性质,可以直接搜到target node,而做成两个长度不一定相等的 path +- 然后很简单找到LCA +- O(n) space, O(logn) time + +#### DFS +- Beofre lasts common ancestor is found: p and q should follow same search pattern: + - compare with root, then dfs(left) or dfs(right) + - util p and q fall on two sides of root, then return root + - 非常巧妙, 但是也比较局限; 稍微变条件, 就很难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(logn) extra space: recursive stack space +- O(logn) time + + + +--- + +**181. [256. Paint House.java](https://github.com/awangdev/LintCode/blob/master/Java/256.%20Paint%20House.java)** Level: Easy Tags: [DP, Sequence DP, Status DP] + + +要paint n个房子, 还有 nx3的cost[][]. 求最少用多少cost paint 所有房子. + +#### Sequence DP +- 求dp[i]的min cost, depends on the color of dp[n-1] + - 选中最后一个房子的颜色同时, 根据dp[i - 1]的颜色/cost + cost[i-1], 来找出最低的cost +- Need to have status with dp array: int[index][color status] + - dp[i][j]: 前i个house 刷成 j 号颜色的最小cost. + - dp[0][j] = 0: 0th house, no cost +- 计算顺序: 从每一个house开始算起 [0 ~ n], first for loop +- time: O(nm), m = # of colors +- space: O(nm) + +#### Rolling Array +- 观察发现 index[i] 只跟 [i-1] 相关, 所以2位就足够, %2 +- space:O(1) + + + +--- + diff --git a/review/level/Hard.md b/review/level/Hard.md new file mode 100644 index 0000000..5529a9d --- /dev/null +++ b/review/level/Hard.md @@ -0,0 +1,2621 @@ + + + +## Hard (106) +**0. [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 所可能用的最少次数. + +#### Method1: Greedy +- maintain the `farest can go`, and use it the target for i increse towards. Why? + - 1) when I know the `farest can go`, in fact it is just currently 1 step away. + - 2) why to iterate from curr `i to farset`? In range [i, farest], we will calc the new `maxRange` + - 3) once `i` reaches `farset`, update `farest = maxRange` +- greedy concept: once we know the farest we can reach at the moment, it is just 1 step away :) +- On average should be jumpping through the array without looking back +- time: average O(n) +- Impl: + - 图解 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的思维模式是一模一样的. + + +#### Method2: 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 + + + +--- + +**1. [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, 但是数字可为多位 + + + +--- + +**2. [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 + + + +--- + +**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个数字 + + + +--- + +**4. [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 + + + +--- + +**5. [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的要求! + + + +--- + +**6. [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)}` + + + +--- + +**7. [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 + + + +--- + +**8. [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)) + + + + +--- + +**9. [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. + + + +--- + +**10. [Orderly Queue.java](https://github.com/awangdev/LintCode/blob/master/Java/Orderly%20Queue.java)** Level: Hard Tags: [Math, String] + + + + +--- + +**11. [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? + + + +--- + +**12. [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. + + + +--- + +**13. [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 + + + +--- + +**14. [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, 看解答 + + + +--- + +**15. [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), 太慢. + + + +--- + +**16. [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一下。 + + + + +--- + +**17. [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 + + + +--- + +**18. [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. + + + + +--- + +**19. [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就可以代为解决。 + + + +--- + +**20. [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) + + + +--- + +**21. [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 + + + +--- + +**22. [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 +还要做一下那. + + + +--- + +**23. [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) + + + +--- + +**24. [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。 + + + +--- + +**25. [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 + + + +--- + +**26. [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里面的站位) + + + +--- + +**27. [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 + + + +--- + +**28. [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) + + + +--- + +**29. [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] + + +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 +- + + + +--- + +**30. [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 + + + +--- + +**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};` + + + +--- + +**32. [Subarray Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20II.java)** Level: Hard Tags: [Array, Binary Search, Two Pointers] + + + +--- + +**33. [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, 节约时间复杂度. + + + +--- + +**34. [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] + + + + +--- + +**35. [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 一样 + + + +--- + +**36. [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 + + + +--- + +**37. [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]。 + + + + + +--- + +**38. [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) + + + +--- + +**39. [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) + + + + +--- + +**40. [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 + + + + +--- + +**41. [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. + + + + +--- + +**42. [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. +- 分析过, 还没有写. + + + +--- + +**43. [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 +还没有做 + + + +--- + +**44. [Interleaving String.java](https://github.com/awangdev/LintCode/blob/master/Java/Interleaving%20String.java)** Level: Hard Tags: [DP, String] + +双序列DP, 从最后点考虑. +拆分问题的末尾, 考虑和s1, s2 subsequence之间的关联. + +求存在性, boolean + + + + +--- + +**45. [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 + + + + +--- + +**46. [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` + + + +--- + +**47. [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里面, 这个想法非常值得思考. + + + +--- + +**48. [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) + + + +--- + +**49. [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; + + + + +--- + +**50. [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) + +##### 为什么从外向里遍历 +- 木桶理论, 包水, 是从外面包住里面 +- 洋葱剥皮, 用完丢掉 + + + +--- + +**51. [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? + + + + +--- + +**52. [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 + + + + +--- + +**53. [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的理所应当。 + + + + +--- + +**54. [Sliding Puzzle.java](https://github.com/awangdev/LintCode/blob/master/Java/Sliding%20Puzzle.java)** Level: Hard Tags: [BFS, Graph] + + + +--- + +**55. [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. + + + +--- + +**56. [Maximum Vacation Days.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Vacation%20Days.java)** Level: Hard Tags: [DP] + + + +--- + +**57. [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就可以了. + + + +--- + +**58. [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 + + + +--- + +**59. [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. + + + +--- + +**60. [Bus Routes.java](https://github.com/awangdev/LintCode/blob/master/Java/Bus%20Routes.java)** Level: Hard Tags: [BFS] + + + +--- + +**61. [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. + + + +--- + +**62. [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)的巧妙用法. + + + + +--- + +**63. [[lint]. HashHeap.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20HashHeap.java)** Level: Hard Tags: [HashHeap, Heap, Lint] + +非题.是从九章找来的HashHeap implementation. + +#### HashHeap +- An efficient implementation of a priority queue. +- The linear hash function monotonically maps keys to buckets, and each bucket is a heap +- https://xlinux.nist.gov/dads/HTML/hashheap.html + + + +--- + +**64. [42. Trapping Rain Water.java](https://github.com/awangdev/LintCode/blob/master/Java/42.%20Trapping%20Rain%20Water.java)** Level: Hard Tags: [Array, Stack, Two Pointers] + + +这道题目的方法比较多. + +#### Method1: Max wall from both sides +- Array: Left Max Wall vs Right Max Wall. +- 对于每个index而言, vertically 能存放的最大水柱, 就是靠 左 右 最高墙决定的: + - min(leftHighestWall, rightHighestWall) - currHeight. +- time: O(n) +- space: O(n) + +#### Method2: Two Pointers +- Optimization from Method1: two pointer, 还是找左边最高和右边最高. O(1) space. +- 利用到了方法3里面的想法一样: 整个structure是被中间的最高bar 二分天下: +- always limited by the shorter wall: 左边按照maxLeft来计算, 右边按照maxRight来计算. +- time: O(n) +- space: O(1) + +#### Method3: 2 Pointers, start from 2 sides +- 1. 找中间最高bar的index +- 2. 两面往中心扫:每次加上(topBarIndex - currIndex)* (elevation from previous index).也就是每次加一个横条 +- 3. 每次还要减去block自身的height +- time: O(n) +- space: O(1) + +#### Method4: Stack +- 主要想法和方法3一致: 在山坡下坡的基础上, 一直用stack堆积bottom. +- 最后遇到上升之前, 此时bottom可以用来跟stack之前堆积的所有下坡index做比较, 算跟他们高度相差的积水. +- 用了stack记录下坡, 然后用个while loop一挖到底的想法非常棒. +- time: O(n) +- space: O(n) + + + + +--- + +**65. [269. Alien Dictionary.java](https://github.com/awangdev/LintCode/blob/master/Java/269.%20Alien%20Dictionary.java)** Level: Hard Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +给一个 array of strings: 假如这个array是按照一个新的字母排序表(alien dictionary)排出来的, 需要找到这个字母排序. + +有可能有多重排序的方法, 给出一种就可以. + +#### Graph, Topological Sort with InDegree count, BFS +- `Build graph`: + - 上下两行string, 相对应的相同的index上, 如果字母不同, 就说明排在第一行的字母在字母表里更领先: form sequence between chars + - form graph Map, for topological sort usage. + - 也可以`List[26] edges` (Course Schedule problem) +- `Build InDegreeCountMap`: based on the char diff of 2 words + - 注意: indegree 是反向的 (跟 node to neighbors 相反的方式建立) +- `Topological Sort`, BFS: + - 1) use queue to find `inDegree == 0` node. It is the letter that points to others, 排在字母表前面. + - 2) reduce edges using Graph`map>` (more generic than List[26], 26个字母的dictionary) +- Edge Case: + - `inDegreeCountMap.size() != result.length()`: some nodes did not make it into result sequence + - `cycle`: when 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 "" +- space: O(n), n = # of graph edges +- time: O(n) + +#### DFS +- TODO +- 跟BFS建立 grpah 的过程一模一样 +- DFS的不同在于: 用visited map 来标记走过的地方 +- 走到leaf的时候, add to result: 但因为走到了底才add, 最终的顺序应该颠倒 (或者, sb.insert(0, x) 直接用颠倒的顺序add) + + + +--- + +**66. [843. Guess the Word.java](https://github.com/awangdev/LintCode/blob/master/Java/843.%20Guess%20the%20Word.java)** Level: Hard Tags: [MiniMax] + + +TODO: revist time/space complexity + +#### Minimax, find target, and use it to eliminate +- `擒贼先擒王`: find the candidate that has largest set of correlations with the rest candidates, and eliminate based on this candidate. + - `approach A`: count the candidate that has 0 overlaps, find min of this poll + - `approach B`: count the candidate that has largest # of connections +- cross-compare, count `match==0` : find candidates that has 0 overlap with others + - pick `min-count candidate A`: it is a candidate that has overlaps with most strings (since 0-match-count is lowest) + - the above candidate will help to **eliminate** a largerset of overlapped candidates + - guess A, return matchCount. +- filter set with matchCount: eliminateCandidate + + + +--- + +**67. [76. Minimum Window Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/76.%20Minimum%20Window%20Substring.java)** Level: Hard Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +基本思想: +- 用个char[]存string的frequency. +- 2 pointer: + - move `end` to find a valid window; + - once valid inwindow found: now move `start` to narrow down to minimum window. + - once window invalid, continue moving `end` and repeat last 2 steps +- HashMap的做法比char[]写起来要复杂一点, 但是更generic + +#### Method0: Sliding Window + freq[256] + counter +- Almost identical approach as in `438. Find All Anagrams in a String` +- use sliding window template: + - 1) extend right pointer and reduce char count + - 2) process when count == 0 + - 3) contract/shrink left side +- special on the `3) step`: + - there is no hard length limit in this problem: in fact, the goal is to find the shortest length + - `3) step` now apperas in the `while(counter == 0)` loop + - shrink the left side of the window as long as counter == 0, until we break the `counter==0` balance. +- time: O(n) one pass +- space: O(1), freq[256] can be ignored. + + +#### Method1: init a valid freq map; maintain with counter +- Two Pointers, use 1 char freq map + counter to determine valid state +- Inspired by: https://leetcode.com/problems/minimum-window-substring/discuss/26808/Here-is-a-10-line-template-that-can-solve-most-'substring'-problems +- Idea: use freqMap and counter to maintain a valid substring range, use two pointers to iterate; reduce to `counter==0` which is the valid substring state. +- Steps: + - 1) build valid freq count map based on target string + - 2) use end index [0~n) to find valid char and reduce counter to find valid range + - 3) count==0 gives valid range: process; then `map[s.charAt(start++)]++ == 0` to break the peace +- Explain `if (map[s.charAt(start++)]++ == 0) counter++`: + - when `count != 0`, `map[s.charAt(end++)]--` reduces freq regardless of what char it visits (it can be ANY char, rather than T characters) + - when `count == 0`, `map[s.charAt(start++)]++` increases freq regardless of what char that is. + - if `map[s.charAt(start)] == 0`: it is a T character being reduced to 0 previously (so we can break the balance on this char) + - YES, map has other index that has 0 freq: however, `start` ONLY covers indexes that `end` has stepped through :) +- time: O(n) +- space: O(1) +- much faster than method2: skip the O(256*n) comparison logic. +- Note: from the concept, it is the reversed thinking of method2. + +#### Method2: build valid map on the fly and compare. Two Pointers, Use 2 Char freq map +- Use 2 char freq maps: source/target. + - target map: fixed freq map, used for comparision + - source map: attempt to build a valid freq map on the fly +- two pointers: + - use index `start=[0, n)` as start index of source candidate + - have a end pointer that will attempt to as far as possible to find 1st valid sequence +- time: have double while loop, but still O(n), why? + - end pointer will at most reach full length n, only once + - start pointer iterate source strichtly once O(n) + - overall, it will be O(n) +- space: O(1), only used a constant char[256] +- Option2: use map, a bit more generic + + + +--- + +**68. [301. Remove Invalid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/301.%20Remove%20Invalid%20Parentheses.java)** Level: Hard Tags: [BFS, DFS, DP] + +给一个string, 里面有括号和其他字符. 以最少刀 剪出 valid string, 求所有这样的string. + +这个题目有多种解法, 最强就是O(n) space and time + +#### DFS and reduce input string +- Goal: identify invalid parentheses and remove (minimum removals) +- Step: + - Detect the incorrect parentheses by tracking/counting (similar to validation of the parentheses string): `if(count<0)` + - When invalid occurs: + - chance for correction. Remove the incorrect parentheses, one at a time + - dfs on the rest of the s that has not been tested yet: start index from index i + - Core edge cases: + - Do not correct twice of the same parenthesis by checking [j-1] pos + - Make sure to attempt correction of all possible parenthesis within tested range: because it outputs all results at the same level + - return/finish once correction done +- Success case: + - a string s passed test: make sure it passes REVERSED string test! + - Core Concept: `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**. +- How does it guarantee minimum removals? + - When seeing a chance to correct, it jumps into a for loop of DFS. It `return` after the for loop. This stops additional testing + - When invalid occurs, correct it right away: minimum correction +- Complexity: + - O(nk), k being the # of recursive calls. It takes n calls to finish a full string case. + +#### BFS +- Similar to DFS, we wnat to test: 1) test input s valid, 2) remove 1 invalid parenthesis at a time, 3) process substring +- instead of testing all substrings (timeout), we want to establish rules to improve reprocess: + - Test1: skip regular char. No need to test it. + - Test2: if redundant paren, do 1 is enough. skip adjacent ones. + - Test3: if last removed extra paren is '(', the next ')' must be a valid pair. LastRemoved char: pecial handling by using a struct: `class Node {String s, int index, char lastRemoved}` +- How to end tests? When there is data in rst, stop adding to queue. + + + +--- + +**69. [1216. Valid Palindrome III.java](https://github.com/awangdev/LintCode/blob/master/Java/1216.%20Valid%20Palindrome%20III.java)** Level: Hard Tags: [DFS, DP, Memoization, String] + + +#### Method1: DP, utilize `Longest Palindrome Subsequence` +- Transform the problem: + - `removing at most k items to make it a palindrome` + - that is: find the longest palindrome subsequence with length x, such that `n - x <= k` +- `516. Longest Palindromic Subsequence` utilizes Interval DP to find LPS length x +- at the end, perform n - x <= k? +- time: O(n^2) to find LPS +- space: O(n^2) for dp + +#### Method2: DFS with Memo +- Either times out or too much space used +- time: O(n^2) +- space: O(n^2) or O(k*n^2) + + + +--- + +**70. [327. Count of Range Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/327.%20Count%20of%20Range%20Sum.java)** Level: Hard Tags: [BIT, Divide and Conquer, Merge Sort, PreSum, Segment Tree] + + +TODO: Write the code + merge function + +#### Divide and Conquer + PreSum + MergeSort +- https://leetcode.com/problems/count-of-range-sum/discuss/77990/Share-my-solution +- 1) build preSum[n+1]: then sum range [i,j]= preSum[j+1] - preSum[i] +- 2) Divide and Conquer: + - 先考虑[start, mid] range里的 ran sum result + - 再考虑[mid, end] range里面的结果 + - 最后考虑[low, high]总体的结果 +- 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 作比较的时候, 一旦过界, 就可以停止计算(减少不必要的计算) + + +#### BIT +- TODO? + +#### Segment Tree +- This segment tree approach(https://leetcode.com/problems/count-of-range-sum/discuss/77987/Java-SegmentTree-Solution-36ms) + - does not build segment tree based on given nums index + - it is built on sorted preSum array. +- regular segment tree based on nums array does not work: + - segment tree based on input array is good for: search/query by index + - is NOT good at: given range sum/value, find indexes + - why? segment tree is built based on index division, not by range value division. + + + +--- + +**71. [41. First Missing Positive.java](https://github.com/awangdev/LintCode/blob/master/Java/41.%20First%20Missing%20Positive.java)** Level: Hard Tags: [Analysis, Array, Edge Case] + + +给一串无序数字, 有负数: 找这个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 +1. 如果nums==null, 其实missing positive integer 自然而然是 1 +1. 有可能这串数字里没有断开的integer, 但是最大的integer在首位 (因为index超标, 无法被放到正确的地方) + - 这种时候, n被放在 index 0, 其实就是说, 下一个integer应该是 n + 1 +1. 最终, 如果array本来就是完全sorted, 也不缺, 还符合角标的条件, 那么唯一下一个就是array范围外的第一个positive number: n + + + +--- + +**72. [308. Range Sum Query 2D - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/308.%20Range%20Sum%20Query%202D%20-%20Mutable.java)** Level: Hard Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree +- Same concept as turning an array into a binary segment tree, + - HOWEVER, this is a 4-nary segmenet tree +- Reference. 307 Range Sum Query +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. +- Handling end stage, there are two approaches: + - ApproachA: check at beginning of recursive call (i.e in `build()`, `updateNode()`, `rangeQuery()`). + - pro: calling recursive function blindly; code is easy. + - con: be really clear about termination state, and catch it. + - ApproachB: check & come up with correct query condition before recursive call + - pro: input to recursive function is assumed to be correct + - con: sometimes really hard to write the conditions before recursive call; code is hard. + + + +--- + +**73. [1203. Sort Items by Groups Respecting Dependencies.java](https://github.com/awangdev/LintCode/blob/master/Java/1203.%20Sort%20Items%20by%20Groups%20Respecting%20Dependencies.java)** Level: Hard Tags: [BFS, DFS, Graph, Topological Sort] + + +#### Topological Sort +- Realize we need to: 1) topo sort group, 2) topo sort items in the group. +- Luckily, the candidates to be sorted are all integers: groupIds, or item ids. We can have 1 generic topo sort function +- Overall workflow + - 1) group items to map > + - 2) build group graph + - 3) topo sort group -> return sorted group id list + - 4) for each group: build item graph, topo sort items -> return sorted item list + - 5) flatten and return results + + + +--- + +**74. [1153. String Transforms Into Another String.java](https://github.com/awangdev/LintCode/blob/master/Java/1153.%20String%20Transforms%20Into%20Another%20String.java)** Level: Hard Tags: [Graph] + + +#### Graph +- analysis: + - 1) should not have mult-origin cases: 1 char maps to 1 char at maximum + - 2) need a buffer char NOT exist in target to hold inter-media transformation + - check open char (out of 26 lower letter) that is NOT in target chars +- impl the validation rules +- more to read in https://leetcode.com/problems/string-transforms-into-another-string/discuss?currentPage=1&orderBy=most_votes&query= + + + +--- + +**75. [850. Rectangle Area II.java](https://github.com/awangdev/LintCode/blob/master/Java/850.%20Rectangle%20Area%20II.java)** Level: Hard Tags: [Segment Tree, Sweep Line] + + +#### Sweep Line + Merge Interval concept +- Inspired by: https://leetcode.com/problems/rectangle-area-ii/discuss/137941/Java-TreeMap-solution-inspired-by-Skyline-and-Meeting-Room +- First consider regular sweep line and realize problem: each vertical line has multiple block segments + - Easy: take a list of vertical dots, and calculate the height diff + - We can use a TreeMap with y-coordinate as key, so to `natural sort by y-coordinate` +- Trick: can NOT remove used y coordinate from map, because the rectangle may continue to expand to right side. +- apply simple equation to calc area: `(long)preY * (p.x - preX)` +- time: + - sort initial queue: O(nlogn) + - process queue: O(n) + - TreeMap insertion: O(logn) + - TreeMap traversal: O(n) + - overall, process queue can be O(n^2) +- space: O(n) + +#### Sweep Line concept, bottom->top sweep +- https://leetcode.com/problems/rectangle-area-ii/discuss/137914/C%2B%2BPython-Discretization-and-O(NlogN) + +#### Segment Tree +- TODO lol + + + +--- + +**76. [140. Word Break II.java](https://github.com/awangdev/LintCode/blob/master/Java/140.%20Word%20Break%20II.java)** Level: Hard Tags: [Backtracking, DFS, DP, Hash Table, Memoization] + + +找出所有 word break variations, given dictionary. (`Word Break I` only checks possibility) + +利用 memoization: `Map>` + +#### DFS + Memoization, pick a prefix, and find a list of suffix candidates +- IMPORANT, Memoization: `Map>` to build substring segments. Reduces repeated calculation if the substring has been tried. +- Realize the input s expands into a tree of possible prefixes. +- Find list of candidates from subproblem, and cross-match +- DFS returns List segments of target s: every for loop takes a prefix substring, and append with all suffix (result of dfs) +- Time O(n!). Worst case, permutation of unique letters: `s= 'abcdef....'`, and `dict=[a,b,c,d,e,f...]` + +#### Method2: DFS on suffix + memo of failed cases, like in WordBreakI +- DFS on string: find a valid prefix, dfs on the suffix, building individual candidate in list till substring exhaust. +- improvement: + - use memo to record failed case (solved the timeout issue explained below) + - use min/max to as boundary for dict check. +- core code is short; helper code is slightly longer + +#### Method3: Regular DPs, kinda too slow +- 两个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 + + + +--- + +**77. [51. N-Queens.java](https://github.com/awangdev/LintCode/blob/master/Java/51.%20N-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 +- Backtracking by replacement: each row has 1 queen, so just store it in int[] columns (CC book solution) + +#### 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 + + + + +--- + +**78. [305. Number of Islands II.java](https://github.com/awangdev/LintCode/blob/master/Java/305.%20Number%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 + + + +--- + +**79. [741. Cherry Pickup.java](https://github.com/awangdev/LintCode/blob/master/Java/741.%20Cherry%20Pickup.java)** Level: Hard Tags: [DFS, DP] + + +special hint: `r1 + c1 = constant t = r2 + c2`, if the two points are moving at same time. + +#### DFS + Memo: TOP-DOWN +- Similar concept to Minimum Path Sum +- https://leetcode.com/problems/cherry-pickup/solution/ +- realize r1 + c1 = r2 + c2. Knowing 3 parameters can uniquely identify the 4th. +- assume there are 2 people starting from origin, and the 2 people can go total 4 directions + - perform DFS based on the 4 directions + - concern: do they visit the same spot? possible. when that happens, make sure we do not double count the grid[i][j] +- when is the end state? + - then anyone, for example, (r1,c1) reaches end (n-1, n-1). + - it means the other person also reaches end +- use memo: memo[r1][c1][r2], it records any given (r1, c1, r2, c2) state + + + + +--- + +**80. [297. Serialize and Deserialize Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/297.%20Serialize%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, Preorder +- inorder and postorder does NOT work: it is hard to find mid point, since the tree is not balanced or complete +- 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 queue to process 1 node at a time. dfs on remaining of the queue + - first node from the list is always the head + - '#' will be a null child: this should break & return dfs + - queue is shared, so dfs(right child) will happen after dfs(left child) completes +- Note: + - Append multiple stirngs with `sb.append(x).append(y)` + - If want to process 1 item at a time from head of the list: make it a queue and poll() + +#### BFS, Non-recursive +- serialize: preorder using queue: + - start with root + - process curr node, then: queue.offer(leftNode),queue.offer(rightNode) + - while(!queue.isEmpty()) +- deserialize: + - split into str[] to process + - since serialization ensures 2 children added (including null), we assume: + - the sequence of parent, left child, right child. + - use queue to reproduce the preorder sequence as we process each index of str[] + - Queue will not be empty until all index reaches end of str[], so no need to worry about queue emptiness + + + +--- + +**81. [727. Minimum Window Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/727.%20Minimum%20Window%20Subsequence.java)** Level: Hard Tags: [DP, Hash Table, Sliding Window, String, Two Pointers] + + +#### Sliding Window +- DIFFERENT from sliding window for substring (`76. Minimum Window Substring`) + - because this problem rquries keeping the order of characters from the target string + - Use a `backtrack mechanism` based on target matching to find closest left starting point to right +- Simple two pointers: + - 1) move sIndex and tIndex: find all T chars in S, in order. + - 2) backtrack tIndex to 0; backtrack sIndex to initial char match + - 3) record potential min result +- Be VERY careful about pointer and index. +- time: O(n^2), backtrack n steps +- Since it requires **order of substring**, `freqMap+counter+twoPointers` approach is NOT applicable + +#### DP +- TODO + + + +--- + +**82. [158. Read N Characters Given Read4 II - Call multiple times.java](https://github.com/awangdev/LintCode/blob/master/Java/158.%20Read%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就好了. +- alaternatively: use queue to hold so we do not need to worry about size + + + +--- + +**83. [295. Find Median from Data Stream.java](https://github.com/awangdev/LintCode/blob/master/Java/295.%20Find%20Median%20from%20Data%20Stream.java)** Level: Hard Tags: [Design, Heap, MaxHeap, MinHeap] + + +#### MaxHeap/MinHeap +- 把Input stream想成向上的山坡. 山坡中间那点,自然就是median. +- 前半段,作为maxHeap,关注点是PriorityQueue的峰点,也就是实际上的median. +- 后半段,作为minHeap,正常的PriorityQueue。 开头是最小的。 + +#### 注意 +- 这里要首先定好, 哪一个queue是多存一个element的. 这里选maxHeap: maxHeap.size() == minHeap.size() + 1 || minHeap.size() +- 必须先维护maxHeap里面有个元素, 否则null了会在比较大小时出问题. + + + +--- + +**84. [315. Count of Smaller Numbers After Self.java](https://github.com/awangdev/LintCode/blob/master/Java/315.%20Count%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] + +#### Method1: Binary Search on processed list +- What if `the processed list is sorted`, so that I can BinarySeach for curr target? + - process from end + - binary search for `index to insert new element` in sorted ascending list + - that index = # of smaller numbers; record it for final result +- time: O(nlogn) +- space: O(n) + + +#### Method2: Segment Tree based on actual value +- Segment Tree functions: + - `Build`: construct segment tree based on min/max range: at leaf node, update count of numbers in range + - `modify(SegmentTreeNode root, int value, int count)`: find leaft at with value, and update count for leaf & all parent nodes + - `query(SegmentTreeNode root, int start, int end)`: return count # of numbers in range [start, end] +- Very similar to `Count of Smaller Number`, where segment tree is built on actual value!! +- IMPORTANT to drop processed number from left-hand-side: + - only find on remaining numbers. + - Utilize `modify(root, target, -1)` to erase element count & update the tree. +- time: `n * log(m)`, where m = Math.abs(max-min). log(m) is used to modify() the leaf element +- space: O(m) +- `Define the positive range` + - negative nubmer division `rounds up towards 0` (this is a problem). (i.e. `(-2 - 1) / 2 = -1.5 = -1`), which causes range error. + - We want the entire segment tree range to be ascending, and we want the mid = (start+end)/2 to round down. + - Solution: + - 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 +offset during accessing nums[i] + + + +#### Method3: Binary Search Tree +- https://leetcode.com/problems/count-of-smaller-numbers-after-self/discuss/76580/9ms-short-Java-BST-solution-get-answer-when-building-BST +- Assume we have a BST, where each node has smallerCount and a val, for any new target, how to find smaller items? + - 1) add the # of smaller count to current node + - 2) compare: + - if target < node.val, keep searching `countVisit(node.left, target)` + - if target > node.val: 1) add currNode.smallerCount, 2) minus node.right.smallertCount (reduce double-counting), 3) plus `countVisit(node.right, target)` + - remember to create left/right node before dfs countVisit into the sides. + + +#### Method4: Binary Indexed Tree + + + +--- + +**85. [239. Sliding Window Maximum.java](https://github.com/awangdev/LintCode/blob/master/Java/239.%20Sliding%20Window%20Maximum.java)** Level: Hard Tags: [Deque, Heap, Sliding Window] + + +#### Method1: Deque, Monotonous queue +- 维持monotonuous queue: `front is always at max` and the `tail end is min`. Always need to return the max end of queue. +- when adding new elements x: + - 1) start from small-end of the queue + - 2) drop all smaller elements + - 3) append to the ending element that is larger than x. + - This is to maintain a front->tail decreasing queue +- when sliding window: queue curr window 里面 最大的已经在max-end, remove it if needed. +- 妙:用deque数据结构(实际上采用LinkedList的形式)来做一个`递减的queue`: better than using arraylist, since DeQueue(linked list) removes at O(1) cost +- 每次把小于当前node的,全部剔除,剩下的,自然就是:最大的>第二大的>第三大的...ETC. +- 我们只在乎最大值的存在;而任何小于当前(正要新就加进去的)值的,反正以后也成不了最大值,于是扔掉! +- Option1: sliding window template using right/left + while loop + - 1) tailing the new number to max queue, if applicable + - 2) process: record max + - 3) contract/shrink left: remove top max if the topMax is the left-most val: rst[i - k + 1] +- Option2: same concept, but use index `i` to mark right, and `i - k + 1` to mark left. +- time: O(n), one pass +- space: O(k), store the deque + + +#### Method2: Heap +- can always build a `class Node{index, val}`; and sort them with PQ of size k +- time: O(nlogK) +- space: O(k) +- this is not linear time, not as good as method1 + + + +--- + +**86. [10. Regular Expression Matching.java](https://github.com/awangdev/LintCode/blob/master/Java/10.%20Regular%20Expression%20Matching.java)** Level: Hard Tags: [Backtracking, DP, Double Sequence DP, Sequence DP, String] + +跟WildCard Matching 一样, 分清楚情况讨论 string p last char is '*' 还有并不是 '*' + +IMPORTANT: '*' 需要有一个 prefix element [elm], so it becomes `[elm]*`. There 2 possible cases: +- [elm] repeats 0 times: move p, j + 2 +- [elm] repeats 1 or more times: need s[i] == p[i], then move s, i+1 + +#### DFS, Top-Down, Break into sub problems. +- DFS on remaining of s and p. Analyze the different cases when next char == '*' +- End case: both i,j reached end true; or one of them reached end. +- The two different cases when given any index j on p, the p[j+1]=='*' + - TRUE: + - ignore p[j, j+1], continue from p[j+2] + - check if s[i]==p[j] or p[j]='.'; continue from s[i+1] and p + - FALSE: check i,j, and move forward with s[i+1], p[j+1] +- If next p char != '*', check curr s[i] ?= p[i] +- Improvement with memo with 2D Booelan[][] memo: much faster + - memo[i][j] records result the remaining strings: s.substring(i) compare with p.substring(j) + - use `Boolean`: when memo[i][j] != null, return something! + +#### DP, Sequence DP, Bottom-Up +- Two sequence, DP, find if possible to match. +- The '*' takes effect of preceding/prior 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 cannot be any match. + + + + +--- + +**87. [1106. Parsing A Boolean Expression.java](https://github.com/awangdev/LintCode/blob/master/Java/1106.%20Parsing%20A%20Boolean%20Expression.java)** Level: Hard Tags: [DFS, Stack, String] + +#### Parse exp as sub problem +- Analyze the pattern: 1) single char, 2) with !, 3) with &, | +- Identify sub problem + - Use stack to parse the data in "()", which is a sub problem to solve with recursive call + - Handle &, | case: need to parse multiple +- Be comfortable with string parsing +- Slight improve: + - If see obvious result, directly return evaluation w/o further parsing + - use memo to store evaluated exp + +#### Evaluate inner exp and save back to Stack +- Use '(' and ')' to mark inner exp +- Evaluate the inner exp and save result back to Stack: the result will be 'f' or 't' +- This is slightly slow because: + - It requires all stack items on top to be processed before reaching the operator + - There is no room to optimize even there is simplification for specific operator + + + +--- + +**88. [715. Range Module.java](https://github.com/awangdev/LintCode/blob/master/Java/715.%20Range%20Module.java)** Level: Hard Tags: [Segment Tree, TreeSet] + + +#### TreeSet +- start with considering array structure but operation are all O(n) + - what if we can easily find range, and update +- TreeSet: + - build a class `Interval {int start, end;}` + - build a customized `compareTo` that sorts the interval by start at default, but sort by end if a.start==b.start + - Query: TreeSet allow us to find element in O(logn) + - Add Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + - Remove Range: finding the starting pointing takes O(logn), but update can be worst to update O(n) + + + +--- + +**89. [432. All One Data Structure.java](https://github.com/awangdev/LintCode/blob/master/Java/432.%20All%20One%20Data%20Structure.java)** Level: Hard Tags: [Design, Doubly Linked List] + + +#### Doubly Linked List +- IMPORTANT: the problem aims to put keys of same frequency in same node! This affects the design of node +- Main a class `Node {keySet, count, last/next pointers}` +- Each operation: + - 1) finds target node and extract the key + - 2) calculate: count +/- 1 + - 3) find new spot to store the key (prior positions or later positions) +- Be careful when handling the cases in inc() and dec() + + + +--- + +**90. [639. Decode Ways II.java](https://github.com/awangdev/LintCode/blob/master/Java/639.%20Decode%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]) +- Enumeration: + - 具体分析 '*' 出现的位置, 枚举出数字, 基本功. + - 注意!!题目说 * in [1, 9]. (如果 0 ~ 9 会更难一些) + - 枚举好以后, 其实这个题目的写法和思考过程都不难 +- Mode: + 数字太大, 取mod来给最终结果: 其实在 10^9 + 7 这么大的 mod 下, 大部分例子是能通过的. + + +#### DFS + memoization +- DFS top-down approach is used to analyze the problem. The logic flow: +- 1) consider the case of 1 letter or 2 letters. +- 2) one letter: + - [*]: + 9 * dfs(s, i + 1) + - [0~9]: + dfs(s, i + 1) +- 3) two letters: + - [_, *]: depends + - [*, _]: depends + - [*, *]: + 15 * dfs(s, i + 2) +- memo[i] records # of ways to decode from [i ~ n] +- space: O(n), Size of recursion tree can go upto n +- time: O(n), `memo array is filled exactly once`!!! + + + +--- + +**91. [68. Text Justification.java](https://github.com/awangdev/LintCode/blob/master/Java/68.%20Text%20Justification.java)** Level: Hard Tags: [Enumeration, String] + + +按照规则 adjust text. 就是Word里面: 有一行太长, adjust word 中间的space, 然后保证每一行的total width 顶格. + +还有一些细节规则, 看原题 + +#### String +- greedy approach: line up as many words as possible; once exceed the MaxLength, justify the list of words +- Steps + - 1) Split & group + - 2) Juststify a row of words + - 3) clean up last row +- Calcualte bounded row length = `width + (list.size() - 1)`. `list.size()-1` = Minimum amount of slot/space used. +- Calculate max ave spaces ot insert into each slot = `totalExtraSpace/slot` +- `Juststify a row of words`: + - 1) take a list of words and assume minimum 1 space in-between words + - 2) distribute the remaining spaces evenly to each slot +- Overall runtime: O(n) to go over all space +- Overall space O(maxWidth) for maxWidth amount of strings + + + +--- + +**92. [340. Longest Substring with At Most K Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/340.%20Longest%20Substring%20with%20At%20Most%20K%20Distinct%20Characters.java)** Level: Hard Tags: [Hash Table, LinkedHashMap, Sliding Window, String, Two Pointers] + + +- Method1 and Method2 are identical to `159. Longest Substring with At Most Two Distinct Characters`. +- However, time complexity for Method2 in increases to O(nk). https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- we want to do better than that (Method3) + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == k, process and record max len + - 3) if map.size() > k, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(k) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(nk) to find the left-most element +- space: O(k) + +#### Method3: Sliding window + LinkedHashMap +- https://leetcode.com/problems/longest-substring-with-at-most-k-distinct-characters/solution/ +- as mentioned above, Method2 uses O(nk), because it takes O(k) to find head item that was inserted first + - meanwhile, we still need the hash map feature to get/put/remove last occurance of a char with O(1) +- Solution: use a LinkedHashMap: + - `map.entrySet().iterator()` maintains the insertion order! +- Special handling: + - since we want the `lastOccurMap` to preserve laset insertion order + - we need to `remove` the char every time before put. +- time: O(n) +- space: O(k) + + + + +--- + +**93. [273. Integer to English Words.java](https://github.com/awangdev/LintCode/blob/master/Java/273.%20Integer%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来找到结果, 每段的处理方法都是一样的 +- Note: + - StringBuffer 更有效率! `sb.insert(0, xxx)` append在sb前面 + - 注意加 " " 的时候, 如果多余, 要`trim()` + - 注意, `小于20的数字, 有自己的特殊写法, 需要额外handle` + - 这道题目就是要细致`耐心`, 几乎么有什么算法, 就是想要写的efficient并且正确, 需要很小心 +- Thinking process: + - `1 ~ 19`: [one, two ... nine, ten, eleven, ...., ninteen] + - `20 ~ x0`: [twenty, thirty, fourty, ... ninety] + - `x00`: hundred: 100 + - thousand: 10^3 + - million: 10^6 + - billion: 10^9 + - trillian: 10^12 way over 2^31, not needed +- plan: + - parse 3 digits at a time + - convert the 3 digit to [xx hundred xx-ty x] + - come up with a string[] + - insert the thousands/million/billion to the string[] + + + +--- + +**94. [218. The Skyline Problem.java](https://github.com/awangdev/LintCode/blob/master/Java/218.%20The%20Skyline%20Problem.java)** Level: Hard Tags: [BIT, Divide and Conquer, HashHeap, Heap, PriorityQueue, Segment Tree, Sweep Line] + + +#### Sweep Line, Time O(nLogN), Space O(n) +- Analysis (inspired by, but not same solution: https://leetcode.com/problems/the-skyline-problem/solution/) + - If there are just 2 overlapping building (totally 4 points on x-axis), here is the outline process: + - Process x coordinate from left->right, one at a time. + - 1) compare all `on-going heights` and find max, add as new outline point + - 2) Handling building end: if the position ends a building, need to remove this height from the list of `on-going heights` + - Requires 2 heap: + 1) sort by x coordinates + 2) `on-going heights`: maintain a pq of ongoing heights +- Steps: + - 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. + - `on-going heights`: 用max-heap (reversed priorityqueue),再iterate heightPoints 来存最大的height + - NOTE: heightQueue里面加一个0, 用来在结尾的时候做closure +- time: initial sort O(nlogn) + calculate n * O(nlogn) [maxQueue sort] +- space: O(n) + +#### 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? + + + + + +--- + +**95. [52. N-Queens II.java](https://github.com/awangdev/LintCode/blob/master/Java/52.%20N-Queens%20II.java)** Level: Hard Tags: [Backtracking] + + +跟 N-Queens 一样, 不是找所有结果, 而是count多少结果. + +#### Backtracking (with replacement) +- Each row has just 1 Queen value +- As CC book suggests, use `int[] columns` of length n to store all queen col positions for n rows + - `int[] columns` is slightly easier to backtrack by updating certain index i with new col + - list will usualy has the add/remove pattern for backtracking + +#### Backtracking +- 当list.size() == n 的时候,说明找到了一个Solution。 +- 1. dfs function (List, n) +- 2. validate function + + + + +--- + +**96. [65. Valid Number.java](https://github.com/awangdev/LintCode/blob/master/Java/65.%20Valid%20Number.java)** Level: Hard Tags: [Enumeration, Math, String] + + +分析edge case, 和各种情况, 然后判别是否是valid number + +#### 情况总结 +- 遇到 `.`, `e`, `+/-`, `int`的几种不同情况 +- 分别遇到的顺序不同时候, 结果也不同. +- 这道题更多是分析情况, 然后把edge case enumerate出来, 算法的意义比较少. + + + +--- + +**97. [632. Smallest Range Covering Elements from K Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/632.%20Smallest%20Range%20Covering%20Elements%20from%20K%20Lists.java)** Level: Hard Tags: [Hash Table, Sliding Window, Two Pointers] + + +#### Method1: Sliding Window +- First sort all of the items together by actual val using `Node {int val, int row}` +- Slinding window goal: + - 1) use right to find range that touches all rows, + - 2) use left to shrink the range +- Sliding Window Template + - move right pointer + - Counts[i] = # of elements used in left/right range + - when counts[i] == 0, countUnique++; the number of row/list being included + - when count == row size: + - processing & save shorter range by using left/right Pointers + - move left pointer; when counts[i] == 0, countUnique-- +- time: O(nlogn) for initial sort and then O(n) to process +- space: O(n) +- What is hard here? To think of the idea of counting one usage of each row: + - when each all rows are used at least 1 time + - calculate the min dist + +#### Method2 PQ?, Similar to merging k array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/discuss/104893/Java-Code-using-PriorityQueue.-similar-to-merge-k-array +- https://leetcode.com/problems/smallest-range-covering-elements-from-k-lists/solution/ + + + +--- + +**98. [745. Prefix and Suffix Search.java](https://github.com/awangdev/LintCode/blob/master/Java/745.%20Prefix%20and%20Suffix%20Search.java)** Level: Hard Tags: [Trie] + + +#### Chain `suffix # prefix` +- Build Trie for all combinations of `suffix#prefix`; all assigned with weight +- how does it make sure to return largest weight/index? + - when we build trie, always update weight for the path nodes it goes through + - yes, it overrides, but this problem does not care if some words are not found +- Time: + - build: go through all words O(n) * word length * 2 => O(n) + - query: O(1) tree height is just at most 20. +- Space: O(N) store all words + + + +--- + +**99. [124. Binary Tree Maximum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/124.%20Binary%20Tree%20Maximum%20Path%20Sum.java)** Level: Hard Tags: [DFS, DP, Tree, Tree DP] + + +找max path sum, 可以从任意treeNode 到任意 treeNode. + +#### DFS +- IMPORTANT: DO NOT ASSUME positive integers +- Overall idea: write example and realize 2 cases at each node: + - 1. `combo sum`: left + right + root + - 2. `single path sum WITH curr node`: left/right + root +- DFS returns the path over curr node: a path needs to be continuous, so we cannot skip curr node. +- IMPORTANT, key discovery: if left/right single path over curr node is less than 0: reutrn 0. + - Parent path will simply drop this path, since we want **maximize** the path sum. + - It is so IMPORTANT: when left or right becomes 0, when comparing with global combo path: + - it automatically covers a special case: `single left/right path + node`, since one of left/right == 0!!! +- With the above understanding: what if I want to skip curr node and just want left/right path w/o curr node: + - it is handled and compared with global in dfs(node.left) or dfs(node.right) automatically! +- time: O(n), go over whole tree +- space: O(logn), tree height. + +#### 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 +- 用 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 + + + +--- + +**100. [689. Maximum Sum of 3 Non-Overlapping Subarrays.java](https://github.com/awangdev/LintCode/blob/master/Java/689.%20Maximum%20Sum%20of%203%20Non-Overlapping%20Subarrays.java)** Level: Hard Tags: [Array, DP] + + +#### DP, Divide and conquer +- split into 3 parts [0, i -1], [i, i + k -1]. [i + k, n - 1] +- NOTE: be very careful about index handling: + - `presum[i + 1] - presum[0]` gives inclusive range of `[0, i]` +- Use DP to record the starting position of max sum, +- inspired by: https://leetcode.com/problems/maximum-sum-of-3-non-overlapping-subarrays/discuss/108231/C%2B%2BJava-DP-with-explanation-O(n) + - 1) calculate preSum with range [0, n] + - 2) calculate leftMaxIndex[], rightMaxIndex[] + - 3) test middle range to find max solution +- Note: the test range for 1, 2, 3 always start with assumption that k has been consumed from one side +- Note: When need to record at max/min value change, we can check/assign it manually (rather than use a object to carry & sort) + + + + +--- + +**101. [149. Max Points on a Line.java](https://github.com/awangdev/LintCode/blob/master/Java/149.%20Max%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" + + + +--- + +**102. [57. Insert Interval.java](https://github.com/awangdev/LintCode/blob/master/Java/57.%20Insert%20Interval.java)** Level: Hard Tags: [Array, PriorityQueue, Sort, Sweep Line] + + +#### Method1: Convert to list, insert, and merge list +- 这里已经给了 **sorted** intervals by start point; + - 1) 直接找到可以insert newInterval的位子. Insert and convert to list + - 2) Merge: Use `pre, curr` to iterate over list, and remove curr after merging + - remove之前都会重新assgin `pre.end`, 确保被remove的node.end 被capture + - 3) Convert back to int[][] +- time/space: O(n) +- code is slightly better to read + +#### Method2: Insert on the fly, and handle edge cases +- handle edge cases: + - new interval is non-overlapping + - 1) head + - 2) tail + - 3) in middle + - new interval is overlapping: + - 1) end index in existing interval; reuse the existing interval end to close new range + - 2) end index in the gap of 2 intervals, use new interval.end to close the new range +- time, space: O(n) + +#### Method3: Sweep Line +- Interval 拆点,PriorityQueue排点 +- Merge时用count==0作判断点 +- 注意, 一定要compare curr `p.x == queue.peek().x` 确保重合的点全部被process: `count+=p.x` +- PriorityQueue: O(logN). 扫n点, 总共:O(nLogn). SLOW. + + +#### 另外 +- 因为interval已经sort, 本想用Binary Search O(logn). +- 但是找到interval insert position 最后 merge还是要用 O(n), 所以不必要 binary Search + + + +--- + +**103. [265. Paint House II.java](https://github.com/awangdev/LintCode/blob/master/Java/265.%20Paint%20House%20II.java)** Level: Hard Tags: [DP, Sequence DP, Status DP] + + +一排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) + + + +--- + +**104. [272. Closest Binary Search Tree Value II.java](https://github.com/awangdev/LintCode/blob/master/Java/272.%20Closest%20Binary%20Search%20Tree%20Value%20II.java)** Level: Hard Tags: [Stack, Tree] + + +#### Method1: Stack, DFS, Inorder Traversal +- find successors and predecessors using BST (both list will be sorted); in the end, we can easily get top k from the two sorted list + - with BST: **inorder traversal gives us sorted predecessors + - with BST: **reversed-inorder traversal gives us sorted successors + - smallest on top of the stack +- time: O(n) visit all nodes, O(k) to output +- space overall: O(n) to store all nodes + +#### Method2: BFS, brutle force +- Itereate over all nodes and maintain pq (improvemenet point: how to avoid traversing entire tree?) +- prioritize nodes that are closer to target, so we may stop early when result reaches k candidates +- time: O(n*logn) +- kinds slow and not utilizing BST + + + +--- + +**105. [72. Edit Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/72.%20Edit%20Distance.java)** Level: Hard Tags: [DP, Double Sequence DP, Sequence DP, String] + + +两个字符串, 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会写的比较复杂, 牛刀杀鸡. + + + +--- + diff --git a/review/level/Medium.md b/review/level/Medium.md new file mode 100644 index 0000000..3e94716 --- /dev/null +++ b/review/level/Medium.md @@ -0,0 +1,6345 @@ + + + +## Medium (310) +**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. [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 + + + +--- + +**2. [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 + + + +--- + +**3. [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. + + + +--- + +**4. [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, 而不考虑是哪个. + + + + + + +--- + +**5. [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. + + + +--- + +**6. [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. + + + +--- + +**7. [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 + + + +--- + +**8. [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) + + + +--- + +**9. [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了 + + + +--- + +**10. [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 + + + + +--- + +**11. [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的方程, 有点玄学的意思啊! 不好想. + + + +--- + +**12. [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), 排序 + + + +--- + +**13. [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);` + + + +--- + +**14. [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 + + + +--- + +**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. [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,要乘一下。 + + + + +--- + +**17. [Total Hamming Distance.java](https://github.com/awangdev/LintCode/blob/master/Java/Total%20Hamming%20Distance.java)** Level: Medium Tags: [Bit Manipulation] + + +给出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. + + + + +--- + +**18. [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 + + + +--- + +**19. [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 一样) + + + +--- + +**20. [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拿出来就好了。 + + + +--- + +**21. [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 + + + +--- + +**22. [Stone Game.java](https://github.com/awangdev/LintCode/blob/master/Java/Stone%20Game.java)** Level: Medium Tags: [DP] + +这个DP有点诡异. 需要斟酌。 +NOT DONE YET + + +--- + +**23. [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, 就一个数字,不增不减嘛。 + + + + +--- + +**24. [Line Reflection.java](https://github.com/awangdev/LintCode/blob/master/Java/Line%20Reflection.java)** Level: Medium Tags: [Hash Table, Math] + + +给一串点, 找是否有一个所有点中间的, 跟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) + + + +--- + +**25. [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) + + + +--- + +**26. [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 + + + +--- + +**27. [Connecting Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph.java)** Level: Medium Tags: [Union Find] + +没有跑过这个程序, 是一个UnionFind的简单实现. +Document了每个环节的计算原理/思想. + + + +--- + +**28. [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 进去。 +- 这样八成是另外一种咯。 + + + +--- + +**29. [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) + + + +--- + +**30. [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开头 + + + +--- + +**31. [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()` + + + +--- + +**32. [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 + + + + +--- + +**33. [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. + + + +--- + +**34. [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 + + + +--- + +**35. [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了。 + + + +--- + +**36. [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 + + + + +--- + +**37. [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 的原型. +- 这样做似乎要多一些代码, 好像并不是非常需要 + + + + +--- + +**38. [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 + + + +--- + +**39. [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] + + + +--- + +**40. [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许多。 + + + + +--- + +**41. [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) + + + +--- + +**42. [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 + + + +--- + +**43. [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] + + +给一串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 + + + +--- + +**44. [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的头。 + + + + + +--- + +**45. [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). + + + + +--- + +**46. [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 + + + +--- + +**47. [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) + + + + +--- + +**48. [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. + + + +--- + +**49. [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,可惜错了。 + + + + +--- + +**50. [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) + + + +--- + +**51. [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. + + + +--- + +**52. [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, 换位子 + + + +--- + +**53. [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. + + + +--- + +**54. [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开始选。 + + + +--- + +**55. [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/ + + + +--- + +**56. [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 是理所当然的 + + + +--- + +**57. [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, 碰壁的时候就回头走。 + + + +--- + +**58. [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. + + + +--- + +**59. [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的用法: |, & + + + + +--- + +**60. [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 + + + +--- + +**61. [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. + + + + +--- + +**62. [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 +- 都是基本操作, 概念实现 + + + +--- + +**63. [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 + + + +--- + +**64. [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. + + + +--- + +**65. [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 + + + +--- + +**66. [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] + + +给一串 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) + + + +--- + +**67. [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) + + + +--- + +**68. [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加上去就好. + + + +--- + +**69. [Space Replacement.java](https://github.com/awangdev/LintCode/blob/master/Java/Space%20Replacement.java)** Level: Medium Tags: [String] + + + +--- + +**70. [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? + + + +--- + +**71. [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]。 然后把三段链接在一起。 + + + + +--- + +**72. [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 + + + +--- + +**73. [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' + + + + +--- + +**74. [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的益处。 + + +--- + +**75. [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来巧妙的掌控山峰和低谷的变化。又是神奇的一幕啊! +这样子的奇观,见过就要知道了,没见过的时候有点摸不着头脑。 + + + +--- + +**76. [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一下最后一个词 + + + + +--- + +**77. [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会出问题 + + + +--- + +**78. [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. + + + +--- + +**79. [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] + + +无序数组, 找最长的上升(不需要连续)数组 的长度. 先做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就是一个递增的数列' 的证明, 还没有仔细想. + + + +--- + +**80. [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) + + + +--- + +**81. [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的边框限制好,中间就全部遍历了。 + + + +--- + +**82. [Subsets II.java](https://github.com/awangdev/LintCode/blob/master/Java/Subsets%20II.java)** Level: Medium Tags: [Array, BFS, Backtracking, DFS] + + +给一串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的时间, 不建议 + + + + +--- + +**83. [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) + + + +--- + +**84. [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了 + + + +--- + +**85. [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的盛水的上限,若高墙移动,导致两墙之间距离减少,就注定水量更少了。(弄啥来,不能缺心眼啊) + + + +--- + +**86. [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 + + + +--- + +**87. [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 + + + +--- + +**88. [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 + + + +--- + +**89. [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[] + + + +--- + +**90. [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链接起来。 + + + +--- + +**91. [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 + + + +--- + +**92. [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--. + + + +--- + +**93. [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) + + +--- + +**94. [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] + + +#### 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. + + + +--- + +**95. [The Smallest Difference.java](https://github.com/awangdev/LintCode/blob/master/Java/The%20Smallest%20Difference.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + + + +--- + +**96. [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? + + + +--- + +**97. [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. + + + + +--- + +**98. [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 + + + + +--- + +**99. [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 + + + +--- + +**100. [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` + + + +--- + +**101. [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维护清楚就行。 + + +--- + +**102. [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. + + + +--- + +**103. [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 + + + +--- + +**104. [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 + + + +--- + +**105. [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本身. + + + +--- + +**106. [Ugly Number II.java](https://github.com/awangdev/LintCode/blob/master/Java/Ugly%20Number%20II.java)** Level: Medium Tags: [DP, Enumeration, Heap, Math, PriorityQueue] + + +#### 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) + + + + +--- + +**107. [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; + + + +--- + +**108. [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) + + + +--- + +**109. [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. + + + + +--- + +**110. [Brick Wall.java](https://github.com/awangdev/LintCode/blob/master/Java/Brick%20Wall.java)** Level: Medium Tags: [Hash Table] + + +给一面墙, 每一行是一行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 + + + +--- + +**111. [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 太慢, 不可行. + + + +--- + +**112. [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. + + + + +--- + +**113. [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) + + + + +--- + +**114. [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看出有直接关系. + + + +--- + +**115. [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看起来更容易理解. + + + +--- + +**116. [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), 就不写了 + + + +--- + +**117. [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在某个点小了,加进去当下这个空隙。 + + + +--- + +**118. [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. + + + +--- + +**119. [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) + + + +--- + +**120. [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 + + + +--- + +**121. [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一下。 + + + +--- + +**122. [Subarray Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/Subarray%20Sum%20Closest.java)** Level: Medium Tags: [PreSum, PriorityQueue, Sort, Subarray] + + +给一串数字, 找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来存这两个信息, 然后合理排序 + + + +--- + +**123. [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的状态, 然后看最后一步. + + + +--- + +**124. [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] + + +给一个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, 永远在左边上下上下。 + + + +--- + +**125. [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[] 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. + + + +--- + +**127. [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] + + +给一个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 + + + +--- + +**128. [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] + + +给一个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) + + + + +--- + +**129. [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)))` + + + +--- + +**130. [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] + + +跟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] + + + +--- + +**131. [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 的情况. + + +--- + +**132. [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]) + + + +--- + +**133. [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 + + + +--- + +**134. [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` + + + +--- + +**135. [Connecting Graph II.java](https://github.com/awangdev/LintCode/blob/master/Java/Connecting%20Graph%20II.java)** Level: Medium Tags: [Union Find] + +Lint还不能跑, 全部按照题意和答案document的. + + + +--- + +**136. [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. 这个用法要记住吧, 没别的捷径. + + + +--- + +**137. [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走过的地方 + + + +--- + +**138. [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 + + + +--- + +**139. [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一下。 + + + +--- + +**140. [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 + + + +--- + +**141. [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 + + + +--- + +**142. [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]` + + + +--- + +**143. [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 + + + +--- + +**144. [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. + +可能有简化的余地, 代码有点太长. +比如试一试不用额外空间? + + + +--- + +**145. [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; + + + +--- + +**146. [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了 + + + +--- + +**147. [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) + + + +--- + +**148. [Target Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/Target%20Sum.java)** Level: Medium Tags: [DFS, DP] + +// 如何想到从中间initialize + + + +--- + +**149. [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 原题有一点点不一样. + + + + +--- + +**150. [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 + + + +--- + +**151. [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? + + + + +--- + +**152. [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。 + + + +--- + +**153. [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] + + +给一个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才可以施行. + + + +--- + +**154. [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 + + + +--- + +**155. [[lint]. Merge k Sorted Arrays.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Merge%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 + + + +--- + +**156. [[lint]. Segment Tree Build II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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, 自底向上建立起的。 + + + +--- + +**157. [[lint]. Product of Array Exclude Itself.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Product%20of%20Array%20Exclude%20Itself.java)** Level: Medium Tags: [Array, Lint] + + + + +--- + +**158. [[lint]. Segment Tree Query.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%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) 做比较: + - 1) [start,end] on LEFT of mid + - 2) [start, end] on RIGHT of mid + - 3) [start, end] includes mid: break into 2 queries + - query [leftNode, start, node.left.end] + - query [rightNode, node.right.start, end] + + + +--- + +**159. [[lint]. Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Anagrams.java)** Level: Medium Tags: [Array, Hash Table, Lint] + + +把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. + + + + +--- + +**160. [[lint]. 3 Sum Closest.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%203%20Sum%20Closest.java)** Level: Medium Tags: [Array, Lint, Two Pointers] + +3Sum 的一种简单形式, 并且都没有找index, value, 而只是找个sum罢了. + +double for loop。 2Sum只能用土办法 left/right 2 pointers。 O(n^2) + +注意:check closest时候用long, 以免int不够用 + + + +--- + +**161. [[lint]. Heapify.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Heapify.java)** Level: Medium Tags: [HashHeap, Heap, Lint, 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的位子往下面盘查。 + + + +--- + +**162. [[lint]. 2 Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%202%20Sum%20II.java)** Level: Medium Tags: [Array, Binary Search, Lint, 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部分作比较。 + + + +--- + +**163. [[lint]. Segment Tree Build.java](https://github.com/awangdev/LintCode/blob/master/Java/[lint].%20Segment%20Tree%20Build.java)** Level: Medium Tags: [Binary Tree, Divide and Conquer, Lint, Segment Tree] + +给一个区间[startIndex, endIndex], 建造segment tree structure, return root node. + +#### Segment Tree +- Usage + - which of these intervals contain a given point + - which of these points are in a given interval +- Recursively build the binary tree + - 左孩子:(A.left, (A.left+A.rigth)/2) + - 右孩子:((A.left+A.rigth)/2+1, A.right) + + + +--- + +**164. [[tool]. MergeSort.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20MergeSort.java)** Level: Medium Tags: [Lint, 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) + + + +--- + +**165. [[tool]. UnionFind.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20UnionFind.java)** Level: Medium Tags: [Lint, Union Find] + + +#### Method1: Union Find with Array +- union(), find() +- Path Compresion: store skip father after found, which makes find O(1) + +#### Method2: Union Find with HashMap + + + + + +--- + +**166. [[tool]. Topological Sorting.java](https://github.com/awangdev/LintCode/blob/master/Java/[tool].%20Topological%20Sorting.java)** Level: Medium Tags: [BFS, DFS, Lint, 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 + + + +--- + +**167. [102. Binary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/102.%20Binary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +如题. + +#### Method1: BFS +- 最普通,Non-recursive: BFS, queue, 用个queue.size()来end for loop:换行。 +- 或者用两个queue. 当常规queue empty,把backup queue贴上去 + +#### Method2: DFS +- 每个level都应该有个ArrayList. 那么用一个int level来查看:是否每一层都有了相应的ArrayList。 +- 如果没有,就加上一层。 +- 之后每次都通过DFS在相应的level上面加数字。 + + + + +--- + +**168. [347. Top K Frequent Elements.java](https://github.com/awangdev/LintCode/blob/master/Java/347.%20Top%20K%20Frequent%20Elements.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue] + + +给一串数字, 找到top k frequent element, 并且time complexity 要比nLogN要好 + +#### Method1: Bucket Sort. HashMap + bucket List[] +- Use HashMap to store +- Bucket `List[]`: stores + - Size of the data structure will be uniqe item size. + - The bucket[i] stores item at frequency i +- Simply loop from bucket.length -> 0, when bucket[i] not null, add to result. +- Solid O(n) + + +#### Method2: 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) +- option1: just use `map`; option2: use `class Record {int num; int freq}` + +#### MaxHeap Attempt. INCORRECT +- 题目有提醒: 必须beetter than O(nLog(n)). +- max heap approach stores all nodes: it is wrong + - even though freq count size m < n, but it can be m == n. ALL unique. + - then it is O(nlogN) again. +- therefore, storing all items into pq is INCORRECT. + + + +--- + +**169. [142. Linked List Cycle II.java](https://github.com/awangdev/LintCode/blob/master/Java/142.%20Linked%20List%20Cycle%20II.java)** Level: Medium Tags: [Cycle Detection, Linked List, Slow Fast Pointer, Two Pointers] + + +#### Slow Fast Pointers +- find slow/fast to detect the meeting point +- find begin node of the cycle: traverse from head, also move slow; utill head/slow meets slow + + + +--- + +**170. [360. Sort Transformed Array.java](https://github.com/awangdev/LintCode/blob/master/Java/360.%20Sort%20Transformed%20Array.java)** Level: Medium Tags: [Math, Two Pointers] + + +#### Two Pointers, Math +- Being able to analys the a*x^2 + b*x graph and find the `peak` or `valley` +- Math basics: x^2 dominates the overall curve so it is up to a to determine: + - `valley`: if a < 0, both sides will be small and center will be large. Prioritize larger value. + - `peak`: if a > 0, center will be small and both sides will be large. Prioritize smaller value. + - starting index being 0 or n-1, is driven by `a` + + + +--- + +**171. [22. Generate Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/22.%20Generate%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 +- time: T(n) = 2 * T(n - 1) + O(1) = O(2^n) +- space: < than 2^n results = O(2^n) + +#### 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 + + + +--- + +**172. [236. Lowest Common Ancestor of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/236.%20Lowest%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 + - Base Case: 当root == null, p or q is found (`root == p || root == q`),那么就return the root as LCA + - 三种情况: + - 1. leftLCA and rightLCA all found: `each path has found one of p and q node as LCA`. Therefore, curr root is the lowest ancestor + - 2. One of leftLCA and rightLCA is found: return whichever one found + - 3. both LCAs are null, return null +- Worst case, visit all nodes to find p q at last level, last two leaves: time O(n), stack space O(n) + + + +--- + +**173. [1053. Previous Permutation With One Swap.java](https://github.com/awangdev/LintCode/blob/master/Java/1053.%20Previous%20Permutation%20With%20One%20Swap.java)** Level: Medium Tags: [Array, Greedy, Permutation] + + +#### Analyze Permutation behavior +- concept similar to `31. Next Permutation` +- 1) first pass: find the one that is in incorrect order +- 2) second pass: find the right spot to swap + + + +--- + +**174. [56. Merge Intervals.java](https://github.com/awangdev/LintCode/blob/master/Java/56.%20Merge%20Intervals.java)** Level: Medium Tags: [Array, PriorityQueue, Sort, Sweep Line] + + + +给一串int[Interval] (unsorted), 把所以Interval merge起来. + +#### Method1: Sweep Line with Priority Queue +- O(nlogn) time (PriorityQueue), O(n) space + - 1. 扫描线+Count: when `count==0`, startFlags==endFlags. 是interval的开头/结尾 (write an example) + - 2. Note: remember to merge points on same sweep line position +- Comparator: `new PriorityQueue<>(Comparator.comparing(p -> p.val))`; + +#### Method2: Sort Intervals and append end logically +- Sort intervals: O(nlogn), extra space O(n) when creating rst list + - `Arrays.sort(intervals, Comparator.comparing(i -> i[0]));` + - 找到结尾 interval, 满足条件就可以save + - 如果不到return的条件, 就继续延伸 interval.end + +#### Method3: Sort Interval, Remove overlaop interval & modify interval +- Less applicable when input is `int[][] intervals`, but more applicable when we have `List intervals` +- Related example: Insert Interval +- Sort fist, loop over and merge, cut off overlapped interval. + - sort by Interval.start: `intervals.sort(Comparator.comparing(interval -> interval.start)); // O(nlogn)` + - 用两个相连的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) + + + +--- + +**175. [986. Interval List Intersections.java](https://github.com/awangdev/LintCode/blob/master/Java/986.%20Interval%20List%20Intersections.java)** Level: Medium Tags: [Two Pointers] + + + + +#### Method1: Merge Interval +- There can be 1 overlapping on any interval, calculate the inner intersection: lo(A[i][0], B[j][0]), hi(A[i][1], B[j][1]) + - if low <= hi, a valid intersection exist; add + - also, if A[i][1] < B[j][1]; that is A[i].end < B[j].end, then i++; otherwise j++ + - because the further-away `end` has been used, so move on. +- O(n) + +#### Method2: Sweep line +- code is much more complex (pq, Point, process code... etc) than method1 +- we can use point to track open/close, also want to specify if point belongs to A/B +- mark 2 global parameters: aOpen, bOpen. + - process when A/B close, record if (aOpen, bOpen) has overlaop + - clear up corresponding global parameter after A/B closes +- sort all pointers in priority queue by index +- Point: {boolean isOpen; int index} +- process the queue and remember to clean up all items on same index +- time: O(nlogn) +- space: O(n) + + + + +--- + +**176. [244. Shortest Word Distance II.java](https://github.com/awangdev/LintCode/blob/master/Java/244.%20Shortest%20Word%20Distance%20II.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +#### Map +- Prep: 存Map +- Process: 相继从两个 index list 里面拿出 p1,p2 + - 根据index的大小, 移动双指针: try to move the pointers closer; always calculate diff +- Optionally: if one list is much larger, do binary search on the larger list + + + +--- + +**177. [80.Remove Duplicates from Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/80.Remove%20Duplicates%20from%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Two Pointers] + +给一个sorted array, 把重复的去掉: 也就是把不重复的按照顺序贴上来, array末尾多余的位置无所谓. + +最多可重复出元素的数量不超过2个. return unique item 的长度. + +#### Basic +- 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其实就足够. + +#### Follow up: k duplicates, Two Pointers +- when index i and i-1 are diff, use count=1 to start +- in while loop, keep count++ until count==k +- reset when next diff comes in + + + +--- + +**178. [5. Longest Palindromic Substring.java](https://github.com/awangdev/LintCode/blob/master/Java/5.%20Longest%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)? + +#### Method1: DP of interval +- Very similar to `216. Longest Palindromic Subsequence`, but this problem requires solid substring(i+1, j-1) to be palindromic +- Similarly: process i = n-1, from end so [i + 1, j] is always ready to consume +- boolean dp[i][j] to mark range (i, j) as palindrome or not. +- 在计算 dp[i][j]的时候, isPalin[i+1][j-1]应该已经计算过了. +- time: O(n^2) dp +- space: O(n^2) + +#### String, Palindrome definition +- 从中间劈开, 遍历i: 从n个不同的点劈开: 每次劈开都看是否可以从劈开出作为palindromic的中点延伸 +- palindrome两种情况: odd, even palindrome +- Worst case: 整个string都是相同字符,time complexity变成: 1 + 2 +3 + ... +n = O(n^2) + + + +#### O(n) +- TODO +- https://www.felix021.com/blog/read.php?2040 + + + +--- + +**179. [1007. Minimum Domino Rotations For Equal Row.java](https://github.com/awangdev/LintCode/blob/master/Java/1007.%20Minimum%20Domino%20Rotations%20For%20Equal%20Row.java)** Level: Medium Tags: [Array, Greedy] + + + +#### Method1: Count all occurrance, and count on overlap indexes +- when there is a value that can cover entire row of size n + - it must be: `n = countA[i] + countB[i] - overlap[i]` +- Code easy to write and read +- time: O(n) +- space: O(1) + +#### Method2: Negative count +- Observation: if A[0] works, no need to check B[0]. +- Because if both A[0] and B[0] exist in all dominoes, + - when you swap A[0] in a whole row, + - you will swap B[0] in a whole at the same time. + - The result of trying A[0] and B[0] will be the same. +- time: O(n) +- space: O(1) + +#### Method3: positive count Match +- there should exist 1 numbers, that can appear in (A[i], B[i]). +- failure case: there exist at least 1 index, that does not have the common number +- maximum case: there can be 2 numbers, that both will make it work. +- findCommon2, and count them: + - set.add(A[0], A[B]), + - if any new one does not exist in set, remove it from set + - if set is empty() , return -1 +- use the 2 numbers from set to do a sweep and count in A, O(n), return the less appearance one. +- time: O(n) +- space: O(1) + + + +--- + +**180. [207. Course Schedule.java](https://github.com/awangdev/LintCode/blob/master/Java/207.%20Course%20Schedule.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Graph, Topological Sort] + + +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目问是否能把所有的课排了 +- input是 numOfCourses, 还有这个prerequisites [[]] +- Concept of Indegree: `# of incoming node that depends on me`. It is a graph of `arrows pointing inward to me` structure. + +##### Indegree 原理 +- Remember: **indegree是周围的node到我这里的次数count**. + - Note: 如果有cycle, 这个node上面会多一些inDegree, 也就无法清0, 它也无法进入 queue && sorted list. + - 如果周围所有node的连线, 都意义切除后, 我的indegree还不等于0, 那么肯定有某些node间接地有重复连线, 也就是有cycle +- Topological problem: almost always care about cycle case (if detecting cycle is not goal) + +#### BFS, Topological Sort +- Two structures: + - 1) build inDegreeEdges: `List[] inDegreeEdges`: list of incoming nodes that depends on `node i`, + - 2) build dependencyCount: `int[] dependencyCount`, count # of braches that curr node depends on +- any dependencyCount[node]==0, means this node is now a leaf, add to queue +- Topological Sort Process, Kahn algorithem: +- topologically process: + 1) add leaf node to queue, get ready to process; + 2) process leafNode, like cutting of leaf + 3) if any child node dependencyCount == 0, it is a leaf node now: add this node to queue. + +#### DFS +- this problem aims for deteching cycle, not output final list. Simply: visit all nodes and verify cycle +- Option1: array of indegree lists, List[] + - 用 visited int[] 来确认是否有cycle. 1 means `visited`, -1 means `visted from last dfs level` + - Deteching `-1`: 说明这个node在上一级或者以上的同一个dfs path里面已经走过, 那么证明有cycle, return false. + - dfs on curr node indegree dependencies; if all passes w/o failing, set visited[i] = 1 + - Similarly, can use `HashMap> map` to replace List[], but exact same idea. +- Optoin2: use a struct `class Node {Boolean visiting; Map inDegreeMap}` to be more generic +- topo sort may output the sort order: 1) at DFS bottom level, put record to a `stack`, 2) rst.insert(0, curr record) + +#### Notes +- 还有 List[] arrayOfList = new ArrayList[]; 这样的操作啊, 代替了map. Though: map may be more flexible +- 是topological sort的题目。一般都是给有dependency的东西排序。 + - 最终都会到一个sink/leaf node,no further dependency, 在那个点截止 +- 画个图的话, prerequisite都是指向那个sink/leaf node +- when building the inDegreeMap/inDegreeEdge: we use sink/leaf node as key/index, which pionts back to inDegree/parent nodes +- BFS: when all braches/dependency count are reduced to 0, then it is now a leaf node, ready to be used. +- DFS Insert Order: rst.insert(0, node); Assume we want leaf/node at index 0 in final output: + - the very bottom-node **depends on everybody** + - any visited node should be added to 0 index of the list, so it will be at tail later + + + +--- + +**181. [987. Vertical Order Traversal of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/987.%20Vertical%20Order%20Traversal%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, Binary Tree, DFS, Hash Table, Tree] + +space: O(n) + +Very similar to `314. Binary Tree Vertical Order Traversal` with 1 special condition: if 2 nodes at same (offset, level): +sort it by its value + +#### Method1: DFS +- the special requirement causes: we have to track exact position of nodes +- Using `Node {int offset, level, val}` and `Map>>`: + - set all nodes to its correct position + - output all together +- the `max/min` offset allows us to loop over the map in a ordered manner (save efforts of sorting) +- time: O(n) to mark all nodes at correct spot, but `O(nlogn)` to sort the vertical array +- space: O(n), mark all nodes in the nested map + +#### Method2: BFS + Hash table +- A (offset, level) has 2 nodes: use nested `Map>>` to track nodes +- Also need a `class Node{int offset; TreeNode node}` to build queue: + - need `offset`: queue at each level cannot derive level index + - need `TreeNode`: `Node` extends original `TreeNode` so we can queue it. +- lots code to write due to the `class Node` for BFS + + + +--- + +**182. [429. N-ary Tree Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/429.%20N-ary%20Tree%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Tree] + + +#### BFS +- use queue to hold each level. O(n) + + + +--- + +**183. [275. H-Index II.java](https://github.com/awangdev/LintCode/blob/master/Java/275.%20H-Index%20II.java)** Level: Medium Tags: [Binary Search] + + +找到h-index, 给的citation int[] 已经sorted. h-index 的definition 具体看题目. + +Aim to find the lowest index mid, which maximize h = n - mid + +#### Binary Search +- H-index的一个简单版, 已经sorted(从小到大), 找target value +- 按定义, 找最后一个 `dictations[mid] >= h`, where `h = n - mid` +- O(logn) + + + +--- + +**184. [694. Number of Distinct Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/694.%20Number%20of%20Distinct%20Islands.java)** Level: Medium Tags: [DFS, Hash Table] + + +#### DFS + HashSet +- DFS can find # of island, just like `200.Number of Islands`, aim to count total +- We need to map same-shap land + - One approach: print the **footprint** starting from coordinate (0, 0) + - Another approach: print the actual island in its boundary, like a QR code. (too hard to code, skip) +- Footprint approach: + - 1. always assume a newly found islands starts from (0, 0) + - 2. take 4 direction from init pos and keep printing the footprint + - 3. Since we always visit nodes from top->right->nextrow, we always visit top-left cornor of a new island, and the footprint will be identical + - Otherwises, if needed, we can sort the footprint and output the hash +- time: O(n), visit all +- space: O(n), store footprint, and dfs stacks worst case visit all nodes + + + +--- + +**185. [152. Maximum Product Subarray.java](https://github.com/awangdev/LintCode/blob/master/Java/152.%20Maximum%20Product%20Subarray.java)** Level: Medium Tags: [Array, DP, PreProduct, Subarray] + + +从一组数列(正负都有)里面找一串连续的子序列, 而达到乘积product最大值. + +#### Method1: DP, Two PreProduct array +- Continuous product can be positive/negative/zero + - If nums[i] > 0, want prior largest product[i-1] * nums[i] + - If nums[i] < 0, want prior smallest product[i-1] * nums[i] + - If nums[i] == 0, product = 0 +- `prior product[i-1]: 想到DP + - 1. 正负数情况, 需要用两个 `PreProduct` array: minProduct[], maxProduct[] + - 2. continuous prodct: it has to utilize curr nums[i] + - 是跟nums[x]当下值比较的, 如果当下值更适合, 会舍去之前的continous product, 然后重新开始. + - Use a global variable to hold overall result. +- Time/Space O (n) +- Space optimization, rolling array + - maxProduct && minProduct 里面的 index i, 都只能 i - 1相关, 所以可以省去redundant operatoins + - Time: O(n) + - space: O(1) + +#### Method2: hold `local max at index i` and `local min at index i` +- same concept as method1, but simplified: given that we always have to use nums[i], so only 1 result can be passed on +- FAST, simple to write and read +- time: O(n) +- space: O(1) + +#### Failed attempt: `memo[i][j]` of continuous product from index i -> j +- working solution, BUT Time/Space complexity O(n^2) are too much + + + +--- + +**186. [199. Binary Tree Right Side View.java](https://github.com/awangdev/LintCode/blob/master/Java/199.%20Binary%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 +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n nodes in final results + + +#### DFS +- Use Map to override the result at each level +- dfs: + - dfs(node.left) and then dfs(node.right) because we want to log right side last +- record global max depth for iteration purpose +- time: O(n) visit all nodes +- space: O(n) worst case unbalanced tree to have n stacks (and n nodes in final results) + + + + +--- + +**187. [259. 3Sum Smaller.java](https://github.com/awangdev/LintCode/blob/master/Java/259.%203Sum%20Smaller.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +1. Similar to 15. 3Sum, but simpler. +1. 只需要count triplet, 但是不需要save triplet, 而且还不需要handle duplicated triplets +1. 发现start, end满足条件时候,(end - start)就是所有 sum target, 那么就end-- +1. 两层循环, O(n2) + + + +--- + +**188. [1008. Construct Binary Search Tree from Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/1008.%20Construct%20Binary%20Search%20Tree%20from%20Preorder%20Traversal.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top Down DFS +- This approach highly relies on the preorder rules + - we can use validation rules to navigate throug hteh preorder array + - use a global index +- time: O(n) + + + + +--- + +**189. [151. Reverse Words in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/151.%20Reverse%20Words%20in%20a%20String.java)** Level: Medium Tags: [String] + + +#### Method1: Split string by space, then flip +- Option1: With `s.split(" ")`: No brain, and super fast +- Option2: With `s.split("\\s+")`, it skips space, but slow. Use sb.insert(0, xxx) +- trim() output +- Time, Space: O(n) + +#### Method2: Flip entire, then individual, two pointer +- flip entire string, then flip each individual string +- Time, Space: O(n) + + + +--- + +**190. [855. Exam Room.java](https://github.com/awangdev/LintCode/blob/master/Java/855.%20Exam%20Room.java)** Level: Medium Tags: [PriorityQueue, Sort, TreeMap, TreeSet] + + +#### Method1 :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 之间的距离. 这里要特别注意. + +#### Method2: TreeSet + TreeMap +- TreeSet +- TreeMap +- seat(): O(logn) + - find largest dist with TreeSet.first() + - break into 2 intervals; save to set and save to map +- leave(x): O(logn) + - find the interval before starting point x using TreeMap.floorEntry() + - merge and store back to set/map +- for test case it is slower than PQ, because it saves to 2 data structure + + + +--- + +**191. [31. Next Permutation.java](https://github.com/awangdev/LintCode/blob/master/Java/31.%20Next%20Permutation.java)** Level: Medium Tags: [Array, Permutation] + + +#### Permutation Behavior +- Great write up: https://leetcode.com/problems/next-permutation/solution/ +- next lexicographically permutation: `smallest` but `larger than curr` permutation: + - find first low point from right [low] + - find the slight larger [high] to swap with [low] + - make sure right side of low is eventually the smallest +- Analyze the use cases, to find next low permutation, 2 major steps: + - 1) Find `first low/drop candidate` from right + - 2) Find `first high where nums[high] > nums[low]` from right + - 3) swap(low, high). + - By now, [low, n-1] forms a greater permutation + - but it is not the smallest, because right side [low + 1, n - 1] is descending + - 4) reverse(low + 1, n-1) to create ascending slopt on right of low (smallest next lexicographically permutation) +- Corner case: if input array is decending (1st low not found), reverse it all together O(n) +- time: O(n) visit all indexes +- space: O(1) not using additional +- Similar question: `1053. Previous Permutation With One Swap` + + + + +--- + +**192. [518. Coin Change 2.java](https://github.com/awangdev/LintCode/blob/master/Java/518.%20Coin%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 + + + +--- + +**193. [515. Find Largest Value in Each Tree Row.java](https://github.com/awangdev/LintCode/blob/master/Java/515.%20Find%20Largest%20Value%20in%20Each%20Tree%20Row.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS +- faster than BFS, using less space if not couting final rst: stack size, O(logn) +- time: O(n), visit all + +#### Method2: BFS with queue +- loop over queue level and record max + + + + +--- + +**194. [253. Meeting Rooms II.java](https://github.com/awangdev/LintCode/blob/master/Java/253.%20Meeting%20Rooms%20II.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort, Sweep Line] + + +给一串数字pair, 代表会议的开始/结束时间. 找同时又多少个会议发生(需要多少件房间) + +#### Method1: sort both start and end times +- Sort start times, and end times in 2 different arrays +- Loop over start time + - when start[i] < end[endIndex], Count++, need more room + - start[i] >= end[endIndex], done using some room, move to next end time, endIndex++ (like vacating a room) +- Note: we never decrese count because: + - what ever count reaches, it is the max + - since we keep moving endIndex, when start[i] >= end[endIndex], we will just reuse meeting room w/o count++ +- time: O(nlogn) +- space: O(n) +- somehow, super fast, over 100% +- inspired by: https://leetcode.com/problems/meeting-rooms-ii/discuss/67855/Explanation-of-%22Super-Easy-Java-Solution-Beats-98.8%22-from-%40pinkfloyda + +#### Method2: Sweep Line +- Use sweep line to process, track max count as max # of rooms needed +- 跟 Number of Airpline in the sky是同一道题 +- time: O(nlogn) +- space: O(n) + +#### Method3: 尝试了一下用一个sorted Array + HashMap +也还行,但是handle edge的时候,HashMap 要小心,因为相同时间start和end的map key 就会重复了。 + + + +--- + +**195. [1161. Maximum Level Sum of a Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/1161.%20Maximum%20Level%20Sum%20of%20a%20Binary%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph] + + + +#### BFS +- simply calc each level sum with BFS +- top-level is processed first, since we go from top level -> deeper level + - only update result if sum is truly > global MAX. + + + +--- + +**196. [221. Maximal Square.java](https://github.com/awangdev/LintCode/blob/master/Java/221.%20Maximal%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; +- init: 每个点都可能是边长1, 如果 matrix[i][j] == '1' +- Space, time O(mn) +- Rolling array: [i] 和 [i - 1] 之间的关系, 想到滚动数组优化 space, O(n) sapce. + + + + +--- + +**197. [131. Palindrome Partitioning.java](https://github.com/awangdev/LintCode/blob/master/Java/131.%20Palindrome%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) + + + +--- + +**198. [222. Count Complete Tree Nodes.java](https://github.com/awangdev/LintCode/blob/master/Java/222.%20Count%20Complete%20Tree%20Nodes.java)** Level: Medium Tags: [Binary Search, DFS, Tree] + + +Complete Tree就是说, 最后一个level可能是缺node的(不是说最右下角缺node, 别忘了!) + +#### Method1: DFS + Optimization +- 每次看最左left depth和最右leaf depth 是不是一样 + - 如果一样, 直接 2 ^ h - 1就好 + - 不一样的话, 再DFS +- calculate `2^(h)`: 位运算, Math.pow(2, h) = 2 << (h - 1). 神奇! + - 2 << 1就是把所有bits往左移动一位, 也就是 * 2 +- time: O(n) visit all nodes on 1 side +- space: O(h) visit all nodes on 1 side + + +#### Method2: Iteratively +- See details in comments inline. 要对tree非常理解 +- binary tree one child tree nodes # = 2 ^ h - 1; 所以一个child tree + root = 2 ^ h + +#### Method3: Binary Search +- NOT DONE, TODO: https://leetcode.com/problems/count-complete-tree-nodes/solution/ + + + +--- + +**199. [398. Random Pick Index.java](https://github.com/awangdev/LintCode/blob/master/Java/398.%20Random%20Pick%20Index.java)** Level: Medium Tags: [Reservior Sampling] + + +#### 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. +- probability theory: + - 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 + + + + +--- + +**200. [238. Product of Array Except Self.java](https://github.com/awangdev/LintCode/blob/master/Java/238.%20Product%20of%20Array%20Except%20Self.java)** Level: Medium Tags: [Array, PreProduct] + + +给一串数字, output rst[n], 每个index是 除了nums[i]以外 所有itemd的乘积. + +#### Array, PreProduct +- 分析普通做法, 了结到用从左到右一遍O(n), 从右到左一遍 O(n) 就可以 +- 注意carry的维护 +- 第一轮:PreProduct (跟preSum的感觉有点像) + - PreProduct[i] stores product from num[0] -> num[i-1] (skipping current num[i]) + - init preProduct[i] = 1, as base for product + - 错过一位操作: always `preProduct[i] *= carry;` and `carry *= nums[i]` +- 第二轮: 从右边乘起, 每次在index i, 收到的carry都是 `nums[i+1] *....* nums[end]` + - 第一轮的结果 * 第二轮的结果, 刚好在index i 缺少掉 nums[i]. 如题所愿. +- Time: O(n) + + + +--- + +**201. [1060. Missing Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1060.%20Missing%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Binary Search] + + +#### Binary Search +- total missing nums = nums[curr] - nums[0] - curr +- edge case: if k > total missing nums, then just add the diff from nums[end] +- otherwise, find this `missing count == k` in the nums using binary search +- After binary search: `start + 1 == end`: + - re-calculate `count = nums[start] - nums[0] - start;` + - output final num: `nums[start] + k - count;` +- Option1: always compare total missing nums count +- Option2: compare partial missing nums count (inspired by: https://leetcode.com/problems/missing-element-in-sorted-array/discuss/303444/Java-O(logN)-solution-Binary-Search) + + + + +--- + +**202. [1048. Longest String Chain.java](https://github.com/awangdev/LintCode/blob/master/Java/1048.%20Longest%20String%20Chain.java)** Level: Medium Tags: [Bucket Sort, DP, Hash Table, Sort] + + +#### Hash table, DP +- store `Map` +- sort all words, try from short to long: short word will be calculated first to serve later words as candidate +- time: O(nlogn) +- space: O(n) + +#### Hash Table, Bucket Sort,DP +- store `Bucket: List[17] of words`, given word size limit [0 ~ 16] +- time: O(n) +- space: O(n) + + + +--- + +**203. [299. Bulls and Cows.java](https://github.com/awangdev/LintCode/blob/master/Java/299.%20Bulls%20and%20Cows.java)** Level: Medium Tags: [Hash Table] + + +#### Solution1: use int[10] to count frequency +1. check match chars +1. check unmatched chars by counting and offset their frequency + - count++ on secret chars: if secretCount is ever < 0 => `char g` has match, then cows++ + - count-- on guess chars: if guessCount is ever >0 => `char s` has match, then cows++ + +#### Solution2: Use hashmap to count +- Improvement: since all digit, use int[10] to count + + + +--- + +**204. [1219. Path with Maximum Gold.java](https://github.com/awangdev/LintCode/blob/master/Java/1219.%20Path%20with%20Maximum%20Gold.java)** Level: Medium Tags: [Backtracking, DFS] + + + +### DFS, Backtracking +- typical recursive visit all situation + + + + +--- + +**205. [62. Unique Path.java](https://github.com/awangdev/LintCode/blob/master/Java/62.%20Unique%20Path.java)** Level: Medium Tags: [Array, Coordinate DP, DP] + + +2D array, 算走到最右下角,有多少种方式. + +#### DP, 加法原理 +- 计数DP: 2 ways to reach (i,j): dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; + - non-overlapping: `dp[i - 1][j]`, `dp[i][j - 1]` + - covers the only 2 possible way to reach (i,j) +- initialization: dp[i][0] = 1, dp[0][i] = 1 + - Of course, row i = 0, or col j = 0, there is only 1 way to reach +- time O(mn), space O(mn) + +##### 滚动数组 Rolling Array +- [i] 只跟 [i - 1] 有关系, 用 curr/prev 建立滚动数组. +- space O(n) 优化空间 + +#### DFS + Memoization +- move from (0,0) towards (m, n) +- use Map as memoization technique + + + +--- + +**206. [1091. Shortest Path in Binary Matrix.java](https://github.com/awangdev/LintCode/blob/master/Java/1091.%20Shortest%20Path%20in%20Binary%20Matrix.java)** Level: Medium Tags: [BFS] + + + +#### BFS +- find shortest path using queue +- time/space: O(n^2), n = grid length +- why SKIP `boolean visited[i][j]`? after a position grid[i][j] is used: + - 1) the curr path will not return to (i, j) + - 2) other route that may eventually reach (i, j) need not to be recorded, + - because the other route is already longer than the curr path + - therefore, we just simply block the visited node by `grid[x][y] = 1` + - note: block it right after it is added to the queue, so other nodes at same level will not attempt this visited node. + + + +--- + +**207. [1110. Delete Nodes And Return Forest.java](https://github.com/awangdev/LintCode/blob/master/Java/1110.%20Delete%20Nodes%20And%20Return%20Forest.java)** Level: Medium Tags: [DFS, Divide and Conquer, Tree] + + +#### Method1: DFS, divide and conquer +- dfs function: have toDelete set, and a result list +- dive deep into child node FIRST, and test if a removal is needed at bottom of tree +- if remove, add orphan and return null; otherwise, return itself +- time: O(n), visit all nodes +- space: O(logn), height of the tree + +#### Method2: HashMap, DFS. +- traverse tree and create `map ` to fast O(1) removal. O(n) +- set root into a rootSet +- after deleting a node A, the children of the node becomes 2 forests root + - children should be marked in rootSet + - also remove node A from rootSet (if appears) +- output: find all root in root set, traverse and output. +- This approach requires a dfs build of parentMap + - it is same amount of efforts to do the regular dfs removal. + - not a good solution +- time: O(n) +- space: O(n) + + + +--- + +**208. [1249. Minimum Remove to Make Valid Parentheses.java](https://github.com/awangdev/LintCode/blob/master/Java/1249.%20Minimum%20Remove%20to%20Make%20Valid%20Parentheses.java)** Level: Medium Tags: [Stack, String] + + +#### Stack +- Goal: remove extra '(' or ')' so it is valid. +- Forward thinking: use stack to track '(' and ')', then keep appending partial string to output +- Backward thinking: use stack to filter out false indexes, and remove them in the end + + + + +--- + +**209. [15. 3Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/15.%203Sum.java)** Level: Medium Tags: [Array, Sort, Two Pointers] + +#### sort array, for loop + two pointer +- 处理duplicate wthin triplets: + - 如果最外圈的移动点i重复, 一直顺到结尾的最后一个再用. + - 如果是triplet内有重复, 用完start point, 移动到结尾. +- Note: + - 1. 找 value triplets, 多个结果。注意,并非找index。 + - 2. 要升序, 第一层for loop 从最后一个元素挑起, 保证了顺序。 + - 3. 去掉duplicate: check用过的同样的数字,都跳掉。不需要用同样的数字再计算一边已有结果。 +- 时间 O(n^2), 两个nested loop + +#### For loop + 2Sum +- HashMap 2Sum. Remember to handle duplicates + - 1. For loop 挑个数字A + - 2. 2Sum 出一堆2个数字的结果 + - 3. Cross match 步骤1里面的A + + + +--- + +**210. [311. Sparse Matrix Multiplication.java](https://github.com/awangdev/LintCode/blob/master/Java/311.%20Sparse%20Matrix%20Multiplication.java)** Level: Medium Tags: [Hash Table] + + +给两个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 + + + +--- + +**211. [322. Coin Change.java](https://github.com/awangdev/LintCode/blob/master/Java/322.%20Coin%20Change.java)** Level: Medium Tags: [Backpack DP, DFS, DP, Memoization] + + +给一串不同数额的coins, 和total amount to spent. 求 最少 用多少个coin可以组合到这个amount. 每种coins个数不限量. + + +#### DP, Bottom -> UP 从小到大的顺序! +- define dp[x], 积累到amount x, 最少用多少个coin +- function: `dp[x] = Math.min(dp[x], dp[x - coinValue] + 1)`. two branches based on choosing coinValue or not +- initialization + - dp[0], zero amount uses 0 coin. so dp[0] = 0 + - Utilize `Integer.MAX_VALUE` as default val for initialize dp[x]: 1) alert error stage; 2) easy comparison + +#### Method2: Memoization, DFS, Top->Down +- create subproblem: (coins, amount - pickedCoin) +- memo[i] 依然表示: min # of coints to make amount i +- initialize memo[i] = Integer.MAX_VALUE +- 先选最后一步(遍历coins), 然后dfs做同样的操作 +- 记录memo[amount] 如果已经给过value, 不要重复计算, 直接return. +- time: O(n * S), worst case it runs n coins for S(amount) iterations +- space: O(S) + + + +--- + +**212. [55. Jump Game.java](https://github.com/awangdev/LintCode/blob/master/Java/55.%20Jump%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 +- 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) +- space: O(1) + +#### DP +- DP[i]: 在i点记录,i点之前的步数是否可以走到i点? True of false. +- 其实j in [0~i)中间只需要一个能到达i 就好了 +- Function: DP[i] = DP[j] && (j + A[j] >= i), for all j in [0 ~ i) +- Return: DP[dp.length - 1]; +- time: O(n^2) +- space: O(n) + + + + +--- + +**213. [173. Binary Search Tree Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/173.%20Binary%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(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过,如此便会死循环。 + + + + +--- + +**214. [875. Koko Eating Bananas.java](https://github.com/awangdev/LintCode/blob/master/Java/875.%20Koko%20Eating%20Bananas.java)** Level: Medium Tags: [Binary Search] + + + +#### Binary Search +- Bianry serach on the min/max value range +- The mid value is calcualted with helper function `calc(piples, k)` +- find celing: `count += (i - 1) / k + 1`, faster than `Math.ceil(i / k)` +- time: O(logm) to find the best velocity, assume total range is m; O(n) for each `calc` call + + + +--- + +**215. [19. Remove Nth Node From End of List.java](https://github.com/awangdev/LintCode/blob/master/Java/19.%20Remove%20Nth%20Node%20From%20End%20of%20List.java)** Level: Medium Tags: [Linked List, Two Pointers] + + +#### Two Pointer +- 1 end pointer to define the window based n steps +- 1 pre pointer to track the node before the targeting node +- when end reaches null, remove nth node: link pre and head.next + + + +--- + +**216. [134. Gas Station.java](https://github.com/awangdev/LintCode/blob/master/Java/134.%20Gas%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 +- 而不是求: 最后点可否走完/最值/计数 + + + +--- + +**217. [1197. Minimum Knight Moves.java](https://github.com/awangdev/LintCode/blob/master/Java/1197.%20Minimum%20Knight%20Moves.java)** Level: Medium Tags: [BFS] + + +#### BFS +- `from starting point, find min steps to reach certain point`: think of BFS + - similar: shortest path, shortest distance +- bfs: minimum steps, enumerate the possible moves + - move closer to x or y (test 8 possible directions) + - add possible moves in queue +- use visited to cache visited coordinates +- time: O(8^n), # of BFS branches +- space: O(8^n), # of BFS branche nodes + + + +--- + +**218. [493. Reverse Pairs.java](https://github.com/awangdev/LintCode/blob/master/Java/493.%20Reverse%20Pairs.java)** Level: Medium Tags: [BST, Binary Indexed 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, Divide and Conquer +- Using merge sort concept (NOT merge sort impl). +- One very simply desire: if we want to know # elements between [i, j] such that `nums[i] > 2*nums[j]`, it would be so great if array is **sorted**! + - If sorted, fix index i, keep j++ for all `nums[i]/2.0 > nums[j]` + - We CANNOT just sort entire array. WHY? Because it distrupts the value of curr index i, and the restriction is: `find matching elements on right side of curr index i` + - BUT, what about just sort `right side of i`, and make sure the subproblem (i+1, end) is solved first? +- 灵感: use merge sort concept.divide and conquer [i ~ n] into 2 sections: + - 1) solve subProblem(start,mid) & subProblem(mid+1, end). sort the sub array so that it can be used recursively at parent level. + - 2) solve the curr pblem: for all [i, mid], check against [mid+1, end]. +- Question1: does it cover all use cases? + - First, subProblem(start,mid) & subProblem(mid+1, end) recursively solves its own range + - Last, the only range is the current level problem check `[i, mid]` against its entire right side range: `[mid+1, end]`. DONE. all covered. +- Question2: what it is okay for `subProblem(start,mid) & subProblem(mid+1, end)` partially sort the array? + - that is the goal: 1) we want the right side range to be sorted; 2) left range is sorted but it does not matter since we treat [start, mid] as 1 group +- 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 min 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 + + + + +--- + +**219. [1306. Jump Game III.java](https://github.com/awangdev/LintCode/blob/master/Java/1306.%20Jump%20Game%20III.java)** Level: Medium Tags: [BFS, Graph] + + +### Method1: BFS +- Find possibility to reach certain point, we can BFS: faster to find shortest candidate +- use queue to hold left, right candidates +- use set to record visited + +### Method2: DFS +- attemp all nodes, use set to record visited. +- time: O(n) +- space: O(n) + + + +--- + +**220. [277. Find the Celebrity.java](https://github.com/awangdev/LintCode/blob/master/Java/277.%20Find%20the%20Celebrity.java)** Level: Medium Tags: [Adjacency Matrix, Array, Graph, Greedy, Pruning] + + +有n个人, 其中有个人是celebrity, 注意必要条件 `Celeb knows nobody; Everyone else knows the celeb`. 找到celeb + +Note: the relationship graph can be presented as an adjacency matrix, but graph is not directly used in this problem. + +#### Pruning +- Given assumption: 1) `only 1 celebrity`, 2) person k, who knows nobody ahead of him or after him. +- if first pass finds candidate, `person k`, it means: + - person [0, k-1] are not celebrity: they know a previous or current candidate + - person k knows no one between [k + 1, n): k+1 to n-1 can not be the celebrity either. + - person k is just the last standing possible celebrity +- second pass validation: we do not know if `knows(celeb, [0~k-1] )`. Do a final O(n) check +- time:O(n), space O(1) +- DO NOT: Brutle compare all -> all: O(n^2) handshakes. + +##### 思考逻辑 +- 先写出来[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所有人. + + + +--- + +**221. [46. Permutations.java](https://github.com/awangdev/LintCode/blob/master/Java/46.%20Permutations.java)** Level: Medium Tags: [BFS, Backtracking, DFS, Permutation] + + +#### Method1-Option1: Recursive Backtracking, with queue to maintain the remaining list +- Best of all recursive approaches +- iterate over nums: pick or not pick +- reduce remaining item in next level: + - option1: use queue, restrict queue size; backtrack append to queue + - option2: remove element before passing into next level; backtrack: insert back +- time O(n!): visit all possible outcome + - T(n) = n * T(n-1) + O(1) + +Method1-Option2: Recursive Backtracking, with `list.contains()` to avoid reuse of index +- A bit worse than option1, uses more time: + - list.contains() cost O(logn). Technically, it is O(n^n), plus the `contains` is nlogn time + - also, each dfs, it has to iterate over entire nums list + +Method1-Option3: Recursive Backtracking, with `visited[]` to avoid reuse of index +- Use visited[] to track, still causes for(over n items), not efficient + +#### Method2-Option1: Iterative, Build permutation by insertion +- Best of all iterative approaches +- Each time pick 1 NEW element and find places to insert into candidate list: + - 1. 一个一个element加进去 + - 2. 每一次把rst里面的每个list拿出来, 创建成新list, 然后选位置加上new element + - 3. 加新元素的时候, 要在list的每个位置insert, 最终也要在原始的list末尾加上new element +- 还是O(n!), 因为rst insert O(n!)个permutations +- Better than the Option2/Option3 (`BFS+Queue`), because this solution does not need to check duplicates + +#### Method2-Option2: Iterative, use Queue to hold candidate list +- 用个queue,每次poll()出来的list, 把在nums里面能加的挨个加一遍 +- Time O(n!) +- Slow: checking candidate.contains() is O(logn) each time + +#### Method2-Option3: Iterative, use queue to candidate list, and calculate remain list on the fly +- Almost same as Method2-Option2, but it builds remainingCandidate list on the fly list.removeall(xyz): O(n) +- Even slower than Method2-Option2 + + + +--- + +**222. [1094. Car Pooling.java](https://github.com/awangdev/LintCode/blob/master/Java/1094.%20Car%20Pooling.java)** Level: Medium Tags: [Greedy, Heap, PriorityQueue, Sort] + + +#### Method1: bucket sort +- define the bucket by index: the total distance is fixed [0, 1000] +- +/- capacities for each pos and save into the bucket +- go over the bucket and see if the total cap goes over input capacity +- O(n), trips size +- space: O(1), bucket size 1000 is constant +- `IMPORTANT`: before using PQ to sort, consider bucket sort: + - if the boundary set and seems resonable? i.e., max size = `1000` + - is the sorted items index based? + +#### Method2: Priority Queue, sort distnace +- Like meeting room, merge interval +- process items on same index + + + +--- + +**223. [245. Shortest Word Distance III.java](https://github.com/awangdev/LintCode/blob/master/Java/245.%20Shortest%20Word%20Distance%20III.java)** Level: Medium Tags: [Array, Design, Hash Table, Two Pointers] + + +跟243/244不同: 这里允许list里面有重复的word. + +#### Method1: Two Pointers, one pass +- Follow up of 243. Shortested Word Distance +- 特别handle `word == word1 == word2` case: + - p1 and p2 will always be the same + - when `word == word1 == word2`, simply calculate distance using the `old p1 or p2` with `curr index i` +- The rest impl aligns with 243. + +#### Method2: Hash Table +- when `word1==word2`, make usre to skip `p1==p2` by increasing i or j +- The rest impl aligns with 244 +- Time: still O(n), but slower than Method1: 2 passes +- Space: uses extra space O(n) to hold all indexes + + + +--- + +**224. [1117. Building H2O.java](https://github.com/awangdev/LintCode/blob/master/Java/1117.%20Building%20H2O.java)** Level: Medium Tags: [Lock, Semaphore, Thread] + + +#### Meethod1: Use lock & counter to lock a thread based on counter +- when counter != 2 , it will execute hydrogen() two times so that 'H' will reach 2 +- when count == 2, it will execute oxygen() once so that 'O' will reach 2 + +#### Method2: use Semaphore to manage the life cycle of 'H' and 'O' +- to start: H is at count 2 and O is at count 0. They need both be at 0 to be unlocked +- hydrogen(): + - `h.acquire()` will execute 2 times until H.count is reduced to 0 + - `o.release` will add O.count by 1 for 2 times +- oxygen(): + - `o.acquire(2)` can only occur when O.count == 2 due to the 2 calls in `hydrogen(..)` + - `h.release(2)` will restore the H.count back to 2 +- semaphore: https://www.geeksforgeeks.org/semaphore-in-java/ + + + +--- + +**225. [973. K Closest Points to Origin.java](https://github.com/awangdev/LintCode/blob/master/Java/973.%20K%20Closest%20Points%20to%20Origin.java)** Level: Medium Tags: [Divide and Conquer, Heap, Sort] + + +#### PriorityQueue +- Create customized Point{} class +- Sort by distance +- Maintain queue size <= K + +#### Divide and Conquer +- ?, select sort? + + + +--- + +**226. [200. Number of Islands.java](https://github.com/awangdev/LintCode/blob/master/Java/200.%20Number%20of%20Islands.java)** Level: Medium Tags: [BFS, DFS, Matrix DFS, Union Find] + + +给一个2Dmatrix, 里面是1和0, 找#of island. + +#### Method1, DFS +- visit all nodes connected with the starting node + - double for loop, test all starting nodes + - val == 1: 1) count++; 2)DFS from this (i,j); + - Mark visited (x,y) = '0' +- time: O(n), visit all nodes +- space: O(n), stack + +#### Method2, 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. + - Return the total # of unions (which is # of islands) +- in reality: it is a bit slow. +- time: visit all nodes just once, O(n). Union Find will visit all nodes once and union them +- space: O(n), union find takes O(n) space +- 记住UnionFind的模板和几个变化(Connecting Graph I, II, III), 最后归总的代码写起来就比较简单. + +#### Method3: BFS +- use queue to hold 1 island, keep adding 4-direction islands; mark visited with '0' +- check entire board for any remaining one. + + + +--- + +**227. [567. Permutation in String.java](https://github.com/awangdev/LintCode/blob/master/Java/567.%20Permutation%20in%20String.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + +#### Method1: Sliding window with left/right Pointers +- Sliding window template: + - 1) Check right pointer and move right + - 2) Move left when necessary + - 3) Verify count == 0 & end state + - Note: normally 2) and 3) are in reversed order; this problem is a bit different +- This is efficient when the number of characters is not limited to 26, the runtime is still O(m + n) +- time: O(m + n), m = s1 length, n = s2 length +- space: O(k), k = # of possible chars, 26 in this case + +#### Method2: Two Pointer, but brutle verify freq count +- 如果做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) 这一步 +- time: (m + n) + - However, if # of possible chars is more than 26 + - For example, `k unique characters`, then the runtime will become: O(m + nk) + +- space: O(k), k = # of possible chars, 26 in this case + + + +--- + +**228. [369. Plus One Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/369.%20Plus%20One%20Linked%20List.java)** Level: Medium Tags: [Linked List] + + +#### Reverse to make significant digit at tail +- Need add from the back and calculate carry +- Reverse list, so insignificant digit at head; calculate carry +- Reverse back when output + + + +--- + +**229. [211. Add and Search Word - Data structure design.java](https://github.com/awangdev/LintCode/blob/master/Java/211.%20Add%20and%20Search%20Word%20-%20Data%20structure%20design.java)** Level: Medium Tags: [Backtracking, Design, Trie] + + +#### Trie, prefix tree. +- Trie Structure: `boolean isEnd`, `HashMap children` + - trie.addWord: 没node就加,有node就移动 + - trie.search: 没node就return false,有node就移动 +- Alternatively, the hash can be `TrieNode[26]` a fixed size array when applicable + - I like map better for the simplicity to write (w/o converting char -> index) + + + + +--- + +**230. [43. Multiply Strings.java](https://github.com/awangdev/LintCode/blob/master/Java/43.%20Multiply%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]. + - Loop over num1, each row num1[x] * num2, save to correct index (i + j + 1) + - Note: skip leading '0' during output, but do not delete string "0" + - time,space O(mn) +- Option1: Calculate carry on the fly + - index `curr = i + j + 1`, left index `left = curr - 1`, since we start calculation from end of the array. + - **we only touch right side of the array once**, so we can move the carry off from it, and carry to left index + - code is concise +- Option2: save product first without calculating carry + - save product in each int index + - calculate carry on rst[] and `sb.insert(0, c)` (since we start from end of rst) + - this is actaully faster than Option1, somehow. + +#### Reverse, calculate from index 0, and reverse back +- 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'; + + + +--- + +**231. [621. Task Scheduler.java](https://github.com/awangdev/LintCode/blob/master/Java/621.%20Task%20Scheduler.java)** Level: Medium Tags: [Array, Enumeration, Greedy, PriorityQueue, Queue] + + +#### PriorityQueue; Greedy +- 正面去做: + - count task出现的次数 + - 然后PQ sort Task object in descending order +- 每个section: k slots = n + 1. Same task being n slots apart, meaning one section has n + 1 slots. + - 目标是穷尽 k, or 穷尽 pq (poll k times, but will save it back to queue if Task # > 0) + - 如果qp 真的穷尽, break, return count + - 不然, count += k, where k are just # of idle intervals +- time O(n) + constant time O(xlogx), where x = 26 +- extra space O(x) ~ O(1) + + +#### Array, count frequency, enumerate +- Enumerate to understand: + - 1.module tasks in module/section; + - 2.Only need sum the intervals/slots, not return actual layout + - Perfect case: 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(n), space O(1) +- ??? Need to study + + + +--- + +**232. [47. Permutations II.java](https://github.com/awangdev/LintCode/blob/master/Java/47.%20Permutations%20II.java)** Level: Medium Tags: [Backtracking, DFS] + +space: O(n!) + +给一串数组, 找出所有permutation数组. 注意: 给出的nums里面有重复数字, 而permutation的结果需要无重复. + +Similar to 46. Permutations, but with dedup + +#### Backtracking +- Sort the list, and on same level, if last element is the same as curr, skip this recursive call +- time O(n!) + +#### 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]在重复时候, 不用重新记录. + + + +--- + +**233. [332. Reconstruct Itinerary.java](https://github.com/awangdev/LintCode/blob/master/Java/332.%20Reconstruct%20Itinerary.java)** Level: Medium Tags: [Backtracking, DFS, Graph] + + +#### DFS with backtcking +- Construct graph: `map>`; sort the list of destinations. +- DFS: + - with any curr city, go over the destination list: `graph.get(curr)` + - add visit city to rst + - remove visited city from the desitnation list + - backtrack +- NOTE: + - 1) the graph allows cycle: revisiting same city. Do NOT assume no cycle + - 2) it asks to us to treat `smaller lexical order city` with priority; however: + - it does NOT mean visiting `smaller lexical order city` is THE correc anser + - it can be a leaf sink node of the graph and does not provide correct trip plan +- time: O(n^n). n = # of cities. worst case, each city has (n-1) edges and need to try all combinations +- space: O(n^2), there can at most be n * (n - 1) edges + + + +--- + +**234. [88. Search in Rotated Sorted Array II.java](https://github.com/awangdev/LintCode/blob/master/Java/88.%20Search%20in%20Rotated%20Sorted%20Array%20II.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- Also most identical to `33. Search in Rotated Sorted Array`: + - find where nums[mid] lands by comparing to nums[start]. i.e., if nums[mid] < nums[start], on right half of the array + - when `nums[mid] == nums[start]`: duplicate. Shift by start++ +- the worst case of `nums[mid] == nums[start]` willl cause O(n), +- but if duplicate is not entire array, should be O(logn) + + + +--- + +**235. [39. Combination Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/39.%20Combination%20Sum.java)** Level: Medium Tags: [Array, Backtracking, Combination, DFS] + + +给一串数字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 and Space complexity: + - transform the analysis as for `40. Combination Sum II` + - Since this problem allows reuse of elemenets, assume they exist in original input as duplicates + - time: O(k * 2^n), k = avg rst length + - space: O(k) stack depth, if not counting result size + + + +--- + +**236. [144. Binary Tree Preorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/144.%20Binary%20Tree%20Preorder%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Stack, Tree] + + +#### Recursive, DFS, Divide and conquer +- 加root, left, then right. Obvious +- Option1: recursive on preorderTraversal. the dfs function returns List +- Option2: pass in rst, and write a void dfs. + +#### Iterative +- 先加root, 然后push上需要末尾process的在stack垫底(root.right), 然后push root.left +- Stack: push curr, push right, push left. + + + +--- + +**237. [146. LRU Cache.java](https://github.com/awangdev/LintCode/blob/master/Java/146.%20LRU%20Cache.java)** Level: Medium Tags: [Design, Doubly Linked List, Hash Table, Linked List] + + +#### Double Linked List +- 用了一个特别的双向的ListNode,有了head和tail,这样就大大加快了速度 +- 主要加快的就是那个‘更新排位’的过程,找到item hashmap O(1), 做减法换位也都是O(1) +- Overall O(1) +- 巧妙点 + - 1. head和tail特别巧妙:除掉头和尾,和加上头和尾,都O(1) + - 2. remove node: 把node.pre和node.next 连起来, node就自然而然的断开不要了 +- 一旦知道怎么解决了,就不是很特别,并不是难写的算法 +- moveToHead() +- insertHead() +- remove() + +#### Deque, less efficient +- Instead of building `Double Linked List`, utilize Java `Deque queue = new LinkedList<>()` +- works but problem: `queue.remove(E)` is O(n) +- time: O(1) on average but much slower + + + +--- + +**238. [1040. Moving Stones Until Consecutive II.java](https://github.com/awangdev/LintCode/blob/master/Java/1040.%20Moving%20Stones%20Until%20Consecutive%20II.java)** Level: Medium Tags: [Array, Sliding Window] + + + +#### Analyze to understand +- Make sure to sort array: we need to use the actual number range `A[j] - A[i]`, which requires the array to be sorted +- we want to form a new array where A[n-1] - A[0] + 1 == n; order does not matter but all slots need to be filled consecutivly +- max moves: https://leetcode.com/problems/moving-stones-until-consecutive-ii/discuss/289357/c%2B%2B-with-picture + - A interval will be automatically dropped between A[0] and A[1], if moving A[0] first + - Same, a interval between A[n-2] and A[n-1] will be dropped when moving A[n-1] first + - so largest possible move = firstItem + remaining range size - n items = 1 + (A[n-1] - A[1] + 1) - n = A[n-1] - A[1] -n + 2 + - or A[n-2] - A[0] - n + 2 +- min moves: `Sliding Window` + - use slinding window to assume a right pointer, to make sure A[right] - A[left] + 1 < n; otherwise, move left++ + - check # of included stones + - calculate remaining, which is remaining moves +- Handle min move edge case: + - Consecutive Array up to right = n - 1; need 2 moves to finish + + + +--- + +**239. [307. Range Sum Query - Mutable.java](https://github.com/awangdev/LintCode/blob/master/Java/307.%20Range%20Sum%20Query%20-%20Mutable.java)** Level: Medium Tags: [Binary Indexed Tree, Segment Tree] + + +#### Segment Tree, devide and conquer +- sample problem for segment tree +- build(), update(), rangeQuery() + - build and update are standard + - rangeQuery: handle the range split check +- Null leaf node handling: NO, ideally it will not encounter null leaf. + - in update/rangeQuery: when final state (`start==end`) is reached, the recursive call ends + - there is no way for any node to dive futher into null child. +- Range Query concept: + - Using the input range, sum up everything in the range + - sometimes the input range cover multiple segments, then dive into the segments (still use original range) + - once we found a bounded segment (completely surrounded by input range), return segment value. + + + +--- + +**240. [319. Bulb Switcher.java](https://github.com/awangdev/LintCode/blob/master/Java/319.%20Bulb%20Switcher.java)** Level: Medium Tags: [Brainteaser, Math] + + +#### Brainteaser +- https://leetcode.com/problems/bulb-switcher/discuss/77104/Math-solution.. + +#### Brutle: +- if just impl, it take O(n^2): +- repating: some pos are toggled mutiple times: if we know total times, easy to determin each pos. +- loop over [2, n], count times on each index + + + +--- + +**241. [12. Integer to Roman.java](https://github.com/awangdev/LintCode/blob/master/Java/12.%20Integer%20to%20Roman.java)** Level: Medium Tags: [Basic Implementation, Math, String] + + +#### String, Basic implementation +- Parse each digit based on rules +- 1) parse: analyze the situations + + +--- + +**242. [1267. Count Servers that Communicate.java](https://github.com/awangdev/LintCode/blob/master/Java/1267.%20Count%20Servers%20that%20Communicate.java)** Level: Medium Tags: [Array, Graph] + + +#### two axis array, cross-reference +- analyze problem, and realize we want to eliminate `isolated servers` +- count row[], count col[] +- cross-reference row[] and col[]: `row[i]==1 & col[j]==1` indicates a isolated server + +### DFS brutle +- Unfortunately, this problems checks unconnected items, so dfs needs to brutlely check entire row or column +- Only add if `vertical + horizontal count` > 1 +- time: O(mn) * O(m + n) + + + +--- + +**243. [427. Construct Quad Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/427.%20Construct%20Quad%20Tree.java)** Level: Medium Tags: [Tree] + + +#### Basic Impl +- build tree recursively by definition +- O(n^2) time and space due to single visit to all nodes + + +--- + +**244. [981. Time Based Key-Value Store.java](https://github.com/awangdev/LintCode/blob/master/Java/981.%20Time%20Based%20Key-Value%20Store.java)** Level: Medium Tags: [Binary Search, Hash Table, TreeMap] + + +#### Method1: Binary Search +- use hash to store +- binary serach on list of values + +#### Method2: TreeMap +- use hash to store > +- treemap.floorKey(timestamp) finds the top item below certain timestamp + + + +--- + +**245. [1026. Maximum Difference Between Node and Ancestor.java](https://github.com/awangdev/LintCode/blob/master/Java/1026.%20Maximum%20Difference%20Between%20Node%20and%20Ancestor.java)** Level: Medium Tags: [DFS, Tree] + + +#### Method1: Top-Down DFS +- cache parent max and min => produce current max and min +- pass the max and min to dfs +- compare and return the max of dfs(left), dfs(right) +- time: O(n) +- space: O(logn) +- easy to write, a bit hard to think of + +#### Method2: Bottom-up DFS +- pass up the local (min, max) as object `Val{max, min}` +- easy to think of, but more code to write + + + +--- + +**246. [78. Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/78.%20Subsets.java)** Level: Medium Tags: [Array, BFS, Backtracking, Bit Manipulation, DFS] + + +给一串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 +- space: O(2^n) results + +#### 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 +- BFS, 注意考虑如果让one level to generate next level + - 1. maintain a list of Indexe to store 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 +- O(n) space + + + +--- + +**247. [380. Insert Delete GetRandom O(1).java](https://github.com/awangdev/LintCode/blob/master/Java/380.%20Insert%20Delete%20GetRandom%20O(1).java)** Level: Medium Tags: [Array, Design, Hash Table] + + +#### Hash Table, Swap when removing +- Map: `map, Lis: tracks `index->value` +- list maintain 用来 insert/remove/random operations. +- Remove: swap input valueIndex & tialIndex = list.size() -1. + - list.remove(object) 应该是要O(logn) 做一个search的. + - list.remove(list.size() - 1) is cheapter + + + +--- + +**248. [560. Subarray Sum Equals K.java](https://github.com/awangdev/LintCode/blob/master/Java/560.%20Subarray%20Sum%20Equals%20K.java)** Level: Medium Tags: [Array, Hash Table, PreSum, Subarray] + + +给一串数字, 找其中的 # of subarray的 where subararySum == k. + +#### Method1: Hash Table to sture presum (like in 2 sum problem) +- Approach#4 of https://leetcode.com/problems/subarray-sum-equals-k/solution/ +- Hash Table two sum 思想, but to save frequency of current sum: `preSumCount` + - for loop 从左开始积累 `preSumCount` + - derive `priorSum = sum - k`: 看看前面有多少此种sum, `preSumCount.get(priorSum)` + - `# ways to reach priorSum` gives # of ways for that `priorSum + k = curr Sum` + - therefore, count += preSumCount.get(priorSum) +- O(n) time, O(n) space +- Note: 如果需要实际index, 可以存 `Map>` + +#### Method2: Calculate each individual range, with PreSum +- presum: socalled `cummulative sum` +- move from starting point i = [0 ~ n -1] and test each `range = [i ~ j]` +- use presum to verify k: `preSum[j + 1] - preSum[i]` +- time: O(n^2): `1 + 2 + 3 + 4 ... + n ~= O(n^2)` + + + + +--- + +**249. [91. Decode Ways.java](https://github.com/awangdev/LintCode/blob/master/Java/91.%20Decode%20Ways.java)** Level: Medium Tags: [DP, Partition DP, String] + + +给出一串数字, 要翻译(decode)成英文字母. [1 ~ 26] 对应相对的英文字母. 求有多少种方法可以decode. + +#### Method1: DP, Bottom-Up by calculating base case first +- 加法原理: 根据题意, 有 range = 1 的 [1, 9], range = 2 的 [10~26] 来作为partition. +- there can be 2 final states at dp[i]: single digit or double digit. + - validate if `single digit`, dp[i] += dp[i - 1]. Last 1 digit can be decoded. + - validate if `double digit`, dp[i] += dp[i - 2]. Last 2 digits can be decoded. +- note: + - get number from char: `c - '0'` + - validatae single digit != '0', 因为'0' 不在条件之中(A-Z) +- Option1: dp[i] as # ways to decode at index i, including letter i + - The validation is done on: 1) single digit i, or 2) double digit [i-1, i] +- Option2: Partition DP, dp[i] as # ways to decode for first i letters (excluding letter i) + - 定义`dp[i] = 前i个digits最多有多少种decode的方法`: new dp[n + 1]. + - dp[i] += dp[i - x], where x = 1, 2 + - The validation is done on: 1) single digit [i-1], or 2) double digit [i-2, i-1] + - Option2 is better in terms of clean coding. We assume `dp[0]=1` as 1 way to decode 0 digits. + - No need to specially handle length == 1, because it is covered later + - No need to manualy init the first 2-digit case as in Option1 + - Return of `dp[n]` is clean +- 引申: 这里只有两种partition的情况 range=1, range =2. 如果有更多partition的种类, 就可能多一层for loop做循环 + + +#### Method2: DFS, Top-Down +- if single-digit is working, sum += dfs(s, i+1); +- if double-digit is working, sum += dfs(s, i+2); +- end case: i >= n, return 0; i == n - 1; i == n - 2 + - especially when i = n - 2, handle 2-digt edge case carefully: + - 1) check if two-digit range [i, i+1] is valid + - 2) check if single-digit [i] is valid; if so, += dfs(s, i + 1) +- memo[i]: # ways to decode from [i, n). init with `memo[i]=-1` + + + +--- + +**250. [145. Binary Tree Postorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/145.%20Binary%20Tree%20Postorder%20Traversal.java)** Level: Medium Tags: [Stack, Tree, Two Stacks] + + +如题, POST-ORDER traversal. + +LeetCode给了hard, 应该是觉得stack的做法比较难想到. + +#### Method1: DFS, Recursive +- trivial, 先加left recursively, 再加right recursively, 然后组成头部. +- Option1 w/o helper; option2 with dfs helper. + +#### Method2, Iterative, Stack +- Option1: reversely add to list +- 双stack的思想, 需要在图纸上画一画 + - 原本需要的顺序是: 先leftChild, rightChild, currNode. + - 营造一个stack, reversely process: 先currNode, 再rightChild, 再leftChild + - 这样出来的结果是reverse的, 那么翻转一下就可以了. +- reverse add: `list.add(0, x)`; +- 利用stack的特点 + - 每次加element进stack的时候, 想要在 bottom/后process的, 先加 + - 想要下一轮立刻process的, 最后push进stack. +- Option2: regular sequence add to stack: add curr, right, left + - Use set to contain the processed children + - only process curr if its children is processed + + + + +--- + +**251. [210. Course Schedule II.java](https://github.com/awangdev/LintCode/blob/master/Java/210.%20Course%20Schedule%20II.java)** Level: Medium Tags: [BFS, DFS, Graph, Topological Sort] + + +- `207. Course Schedule` has more notes +- 一堆课用int[2] pair 来表示. [1, 0] 表示要上课1的话, 必须先把课0上了. +- 每一个数字都是一个ndoe, 题目求这个最后排好的课的list +- 如果排不好, 就给个空就好 +- input是 numOfCourses, 还有这个prerequisites [[]] + + +#### 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[] { } + + + +--- + +**252. [314. Binary Tree Vertical Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/314.%20Binary%20Tree%20Vertical%20Order%20Traversal.java)** Level: Medium Tags: [BFS, DFS, Hash Table, Tree] + + +给一个Binary Tree, traverse所有node, 按照vertial order 排列成output: List + +重点是: col里面有排序, lower level的排在前面; 如果node遇到collision在同一个位置: 根据他们的相对位置 先放left, 再放right + +#### BFS +- 1) level-traverse all nodes, 2) add node to appropriate col list(using map) +- For final output: + - 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] +- Since each vertical list is appended level by level: no need to sort during output. FAST +- time: O(n), visit all nodes +- spac: O(n) to store + +#### DFS +- Regular DFS to traverse all nodes, and add node to appropriate col list (using map) +- Problem: DFS does not provide natural ordering for nodes on a row. + - Left side may have a super deep Right child branch, which will be added to col list first (since left side is visisted first) + - It is wrong because right branch may have nodes in same column but at higher level + - To Solve: preserve `level` for all nodes in same column +- Need to sort the final list, and slow: visit all nodes O(n) + O(KMlogM), K = # of cols, M = # of items in col +- Time: O(nLogN). O(n) + O(KMlogM), K = # of cols, M = # of items in col; in extrem, it can be a vertical line of nodes, then sort: O(nLogN) +- Space: O(n) + + + + +--- + +**253. [287. Find the Duplicate Number.java](https://github.com/awangdev/LintCode/blob/master/Java/287.%20Find%20the%20Duplicate%20Number.java)** Level: Medium Tags: [Array, Binary Search, Binary Search on Value, Cycle Detection, Slow Fast Pointer, Two Pointers] + + +#### Method1: Slow Fast Pointer +- Use LinkedList Cycle Concept: + - Each element the array is like a `Node {int currIndex; int val;}`, where the `val` is also pointer to next Node + - A node is like a portal; a pointer can: 1) visit a node by currIndex, 2) pick up newIndex = `nums[currIndex]`, then keep repeating step 1 and 2. + - Important: since nums is immutable, the pointer footprint is unique/linear + - Just like linked list. Therefore, use slow/fast pointer to detect cycle. +- https://leetcode.com/problems/find-the-duplicate-number/solution/ +- it is now the same as `142. Linked List Cycle II` + +#### Method2: Binary Search on value +- 注意不要思维定式: binary search `NOT on index` + - `binary search on value`: [1, n] + - O(logN) +- validate(nums, candidate): for loop to count number of `value <= candidate` + - `count == candidate`: no duplicate from [1 ~ candidate]. + - `count < candidate`: missing element in [1~ candidate], so duplicates are in later range. start = mid; + - `count > candidate`: there are duplicates in [1~ candidate]. end = mid; +- Time: O(nLogN) +- Space: O(1) + + + +--- + +**254. [103. Binary Tree Zigzag Level Order Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/103.%20Binary%20Tree%20Zigzag%20Level%20Order%20Traversal.java)** Level: Medium Tags: [BFS, Stack, Tree] + + +#### 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 + + + +--- + +**255. [1057. Campus Bikes.java](https://github.com/awangdev/LintCode/blob/master/Java/1057.%20Campus%20Bikes.java)** Level: Medium Tags: [Bucket Sort, Greedy, PriorityQueue, Sort] + + +#### Method1: PriorityQueue +- PQ can be used to sort on multiple attributes +- follow the specified rules, and build all possible pairs of visits vs. bike. Pair {int dist, workerIndex, bikeIndex} +- PQ to sort them + - first by dist + - if same dist, sort by workerIndex + - if same workderIndex, sort by bikeIndex +- process all candidates, and skip the ones (workers/bikes) visited + +#### Method2: Bucket Sort +- Similar to using PQ: the goal is to find: 1) min dist; 2) closer worker index, 3) closer bike index +- can use bucket sort to hold all possible distances [0 ~ 2000]: bucket[List of pairs] + - do a hard iteration (ordered access from min dist). +- time: O(mn), no need to sort +- space: O(mn) + + + +--- + +**256. [261. Graph Valid Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/261.%20Graph%20Valid%20Tree.java)** Level: Medium Tags: [BFS, DFS, Graph, Union Find] + +给一个数字n代表n nodes, marked from 1 ~ n, 和一串undirected edge int[][]. + +检查这些edge是否能合成一个 valid tree + +#### Method1: 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` + - ideally, this edges[i] should be the very first time x and y node connect; + - however, if they have been grouped together under same ancestor before, there exist a feedback loop (cycle) between them. +- `father[x]`: element x (index x) stores its root ancestor +- 注意: 结尾要检查, 是否只剩下1个union: Tree必须连接到所有给出的node. +- 另一个union-find, 用hashmap的: +- http://www.lintcode.com/en/problem/find-the-weak-connected-component-in-the-directed-graph/ +- Deep Dive into UnionFind: https://www.cs.princeton.edu/~rs/AlgsDS07/01UnionFind.pdf + +#### Method2: Build Graph in adjacency list format: `Map>` and DFS +- Very similar to `Redundant Connection` +- Create adjacency 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) + +#### Method3: BFS on adjacency list graph +- traverse through adjacency list graph: `Map>` +- 1. validate cycle with set: if revisit same node + - avoid infinite loop: remove backward mapping from child node to parent node +- 2. validate check set size for connected + + + +--- + +**257. [64. Minimum Path Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/64.%20Minimum%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(N) + - 1) 需要在同一个for loop里面完成initialization, 2) reuse dp[i][j] + - Reason: dp[i % 2][j] 在被计算出来的时候, 马上在下一轮会被用. 被覆盖前不用,就白算 + - Option3 below initialize dp outside of loop: 看起来固然简单, 但是不方便空间优化 + +#### DFS (top-down) Thinking process +- Enumerate the possible 2 paths: go right, go down +- sub problem: dfs(i+1,j), dfs(i,j+1); pick the min of the two +- memoization: after the path from a point (i,j) to end is computed, record memo[i][j] to avoid repatative calculation +- time: O(mn), only visite and record memo[i][j] once. +- space: O(mn) extrem case of m=100000, n = 2; the stack height is O(mn) + + +--- + +**258. [229. Majority Element II.java](https://github.com/awangdev/LintCode/blob/master/Java/229.%20Majority%20Element%20II.java)** Level: Medium Tags: [Array, Moore Voting] + + +#### Two counters, Moore Voting +1. Moore voting: vote可以加减, 一旦为零, 换下一个candidate, 之前抵消掉的算作清零. +1. 一个array里面, 最多也只有2个数字 出现次数大于2次, 所以用A/B表示. +1. count overall apperance at the end for the two items: countA, countB +1. save to result as valA, valB +1. 有点 moore voting的意思: + - 当count == 0的时候, reset + - 两个candidate A/B都不等, 那么countA--, countB-- +1. 最终重新计数, 然后比较出结局. +1. 注意: 按照if statement的顺序, valA&&countA 比valB&&countB有优先性 + + + +#### Sort + count +- O(nlogN) + + + +--- + +**259. [2. Add Two Numbers.java](https://github.com/awangdev/LintCode/blob/master/Java/2.%20Add%20Two%20Numbers.java)** Level: Medium Tags: [Linked List, Math] + + +LinkedList都已经反转好了,直接做. 跟Add Binary的理解方式一模一样. + +#### Math, Linked List +- reverse order helps calculation + - add additional carry to end + - not same length: align on left +- traverse till both ends +- 遍历两个l1,l2把carry-on处理好,每次生成一个新node,最后检查carry-on. + + + + +--- + +**260. [114. Flatten Binary Tree to Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/114.%20Flatten%20Binary%20Tree%20to%20Linked%20List.java)** Level: Medium Tags: [Binary Tree, DFS] + + +给一个binary tree, 把tree做成 linked list的形式, in-place. + +#### Method1: DFS return tail node +- DFS to return the tail of the flattened list +- Careful handling the null child. Choose one and both works: + - Option1: put non-null as right child and continue dfs + - Option2: put non-null as left child and continue dfs + +#### Method2: void DFS +- latten the tree, no extra space. + - 1. Set right node in buffer, and connect: `root.right = root.left`, DFS flatten(root.right) + - 3. 移花接木: traverse to tail of the current root.right and attach the buffer node. + - 3. flatten the remaining of buffer + +##### 注意 +- 顺序一定要清楚, 不能写错, 写几个example可以看出来 +- 移动的那些node, 要把node.left = null, 清扫干净 + + + +--- + +**261. [1004. Max Consecutive Ones III.java](https://github.com/awangdev/LintCode/blob/master/Java/1004.%20Max%20Consecutive%20Ones%20III.java)** Level: Medium Tags: [Sliding Window, Two Pointers] + + + +#### Sliding window + Left/Right Two Pointers +- https://leetcode.com/problems/max-consecutive-ones-iii/solution/ +- Start with DFS thought, but realize redundant calculations: + - we never need to flip 2 indexes [A], [C] from 0 -> 1, if there is a [B] in middle that is 0 too + - the flipped k zeroes must be consecutive too +- we can utilize two pointers to establish a window that captures k zeroes + - always expend right pointer; if seeing an zero, k-- + - note: `len = right - left + 1` is the ongoing max length + - when k < 0 (too many zeros), we need to slide the left side of the window to make sure: + - keep window len + - potentially do k++ when A[left]==0 +- goal: matain a max size of the window, until right == n +- return (right - left). at this moment, right == n, so no need to (right - left + 1) + + + +--- + +**262. [1146. Snapshot Array.java](https://github.com/awangdev/LintCode/blob/master/Java/1146.%20Snapshot%20Array.java)** Level: Medium Tags: [Array, Hash Table, TreeMap] + + + +#### Hash Table, TreeMap; atomic save +- store efficiently: use List>. only preserve changed itemd +- if no match, find last modifed item based on snapId, use TreeMap.floorEntry + - map.floorEntry(id) return the item.key lower or equal to id +- Utilize a `buffer: Map` and perform atomic save + + + +--- + +**263. [304. Range Sum Query 2D - Immutable.java](https://github.com/awangdev/LintCode/blob/master/Java/304.%20Range%20Sum%20Query%202D%20-%20Immutable.java)** Level: Medium Tags: [DP, PreSum] + + +#### Method1: rangeSum/caching +- build rangeSum[i][j]: square range sum from (0,0) to (i,j), O(mn) to init +- query: time O(1) + +#### Method2: presum/caching +- build rowPreSum[i][j]: row i sum from [0 ~ j], O(mn) to init +- callign takes O(m); space O(mn) + + + + +--- + +**264. [23. Merge k Sorted Lists.java](https://github.com/awangdev/LintCode/blob/master/Java/23.%20Merge%20k%20Sorted%20Lists.java)** Level: Medium Tags: [Divide and Conquer, Heap, Linked List, Merge Sort, PriorityQueue] + + +给一个array of ListNode, 把所有node按照大小连成一条. + +#### Method1: Divide and Conquer, Merge sort +- By Definition, merge sort: divide the list into 2 parts +- recursively merge them together. +- time complexity: O(nlogk) divide by log(k) times, each recursive call can work on n nodes. +- space: O(logk) stacks + +#### Method2: Priorityqueue +- Iterative, PQ来排列所有list的leading node. +- Note: k lists need to be sorted (luckily, already given) +- 时间:n*O(logk), where n = total node number, and PriorityQueue: logk, +- Note: + - 1. 不要忘记customized priority需要一个customized new Comparator() + - 2. Given list 里面也可能有null node, 不要忘记查. + +#### Followup +- 如果k很大,一个机器上放不下所有的k list怎么办? +- 如果Merge起来的很长,一个机器上放不下怎么办? + + + + +--- + +**265. [208. Implement Trie (Prefix Tree).java](https://github.com/awangdev/LintCode/blob/master/Java/208.%20Implement%20Trie%20(Prefix%20Tree).java)** Level: Medium Tags: [Design, Trie] + +Implement Tire, 也即是 Prefix Tree. 做三个function: insert, search, startWith + +#### Trie +- Trie Structure: + - trace the char to children node: Map + - boolean isEnd to indicate if there is string end with this node + - `TrieNode {boolean isEnd; Map children}`. +- No need to store letter c in TrieNode +- 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。 + + + + + +--- + +**266. [516. Longest Palindromic Subsequence.java](https://github.com/awangdev/LintCode/blob/master/Java/516.%20Longest%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 +- Use example to understand: for any given ending char, 3 cases of palindromes + - a. ss[i, j] is a palindrome. dp[i+1][j-1] + 2 since the two indexes are counted, assume dp[i + 1][j - 1] is calculated + - b. ss[i + 1, j] is a palindrome + - c. ss[i, j - 1] is a palindrome +- time/space: O(n^2) +- **Option1: start processing substring from tail** + - set: `i = [n-1 towards 0]`, `j = i + 1` + - consider ss[i, j], ss[i + 1, j], ss[i, j - 1] + - since i starts from n - 1 -> 0 and j = i + 1, these are calculated and ready to go: dp[i+1][j-1], dp[i+1][j] and dp[i][j-1] + - FAST: skipped the initialization +- **Option2: start processing substring from head** + - 用[i][j]表示区间的首尾: 考虑3个情况: 砍头, 砍尾, 砍头并砍尾 (考虑首尾关系) + - Iteration on len: + - len = j - i + 1; 那么反推, 如果len已知, `j = len + i - 1`; + - 注意考虑len == 1, len == 2是的特殊情况. + + +#### Memoization +#### DFS + Memoization +- consider sub problems with 3 major cases + - a. ss[i, j] is a palindrome: dfs check ss[i + 1, j - 1] + - b. ss[i + 1, j] maybe a palindrome: dfs check ss[i + 1, j] + - c. ss[i, j - 1] maybe a palindrome: dfs check ss[i, j - 1] +- memo[i][j]: max palindrome length in range [i, j], if calculated, return directly +- Init memo[i][j] = -1 to track the progress, memoization + - 注意: 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. +- Space: O(n^2) +- Time: O(n^2) +- prepare dp[n][n]: O(n^2); dfs: visit all combinations of [i,j]: O(n^2) + + + + + +--- + +**267. [430. Flatten a Multilevel Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/430.%20Flatten%20a%20Multilevel%20Doubly%20Linked%20List.java)** Level: Medium Tags: [DFS, Linked List] + + +#### DFS +- Depth-first: + - 1) process curr.child, return tailChild + - 2) connect tailChild.next = curr.next +- function: link(Node a, Node b); + + + +--- + +**268. [63. Unique Paths II.java](https://github.com/awangdev/LintCode/blob/master/Java/63.%20Unique%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] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + +#### 坐标DP +- dp[i][j]: # of paths to reach grid[i][j] +- Bottom-up: at end, there are 2 ways to reach dp[i][j] + - dp[i][j] = dp[i - 1][j] + dp[i][j - 1]; +- Handle obstacle (cause dp[i][j] to be 0). + + + +--- + +**269. [139. Word Break.java](https://github.com/awangdev/LintCode/blob/master/Java/139.%20Word%20Break.java)** Level: Medium Tags: [DP, Hash Table, Sequence DP] + + +给一个String word, 和一个字典, 检查是否word可以被劈开, 而所有substring都应该是dictionary里面的words. + +#### Sequence DP +- Bottom-up, test simply case. Sequence DP. +- true/false problem, think about dp + - 子问题: 前i个字母, 是否可以有valid break + - check: 1) dp[j] && 2) `if substring(j, i) valid`, for all j = [0 ~ i] + - dp = new boolean[n + 1]; dp[0] = true; + - test: `dp[i] |= dp[j] == true && word[j, n] in dict`. + - Need iterate over i = [0 ~ n], also j = [0, i] + - When there is a way to make dp[i] == true, then break the [j ~ i] loop, move on to test dp[i++] +- Use set dict: `dict.contains()` +- Improvement: O(n) to figure out max length, so we can skip some substring[j~i] dict.contains() +- overall O(n^2) time since the double for loop + +#### DFS +- Top-Down, break into small problems: Check front subString, and put the rest substring into dfs to test +- Memoization: for tested failed substring, record and do NOT test them again. +- Same Improvement as in DP: use max/min length of dict words as boundary + + + +--- + +**270. [105. Construct Binary Tree from Preorder and Inorder Traversal.java](https://github.com/awangdev/LintCode/blob/master/Java/105.%20Construct%20Binary%20Tree%20from%20Preorder%20and%20Inorder%20Traversal.java)** Level: Medium Tags: [Array, DFS, Divide and Conquer, Hash Table, Tree] + + +如题 + +#### DFS ApproachA: +- use preorder to find root, one index at a time (global index) +- use the root to divide and conquer inorder int[] to 2 sides; + - root.left = dfs(left); root.right = dfs(right) + - end stage: start == end index, create a node +- can use a map to store inorder for O(1) find + +#### 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 + + + +--- + +**271. [449. Serialize and Deserialize BST.java](https://github.com/awangdev/LintCode/blob/master/Java/449.%20Serialize%20and%20Deserialize%20BST.java)** Level: Medium Tags: [Tree] + + +#### DFS, Divide and Conquer, Preorder (utilizing BST) +- with BST, we can: + - skip adding the null nodes into the serialized string: `String NULL = "#"` + - In deserialization: use min/max boundary to check if queue.peek() can be added: + - if not meeting BST condition, skip this dfs and let other call to consume the queue +- Faster because it shortens the serialized string + + +#### DFS, Divide and Conquer, Preorder (w/o using BST) +- Take reference in Serialize and Deserialize Binary Tree +- The approach works but does not utilize Binary Search Tree properties + + + +--- + +**272. [274.H-Index.java](https://github.com/awangdev/LintCode/blob/master/Java/274.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. + +#### Couting Sort / 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 + +#### More about Counting Sort +1. Application: for number/character range +1. Steps: + - Find range, define countArray + - Count element and record in the array + - PreSum the countArray + - Start from beginning of the array, `print & decrese count` to produce the sorted elements + + + + +--- + +**273. [40. Combination Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/40.%20Combination%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. 考虑input: 有duplicate, 必须sort + - 2. in for loop, skip same neighbor: `if (i > index && candidates[i] == candidates[i - 1]) continue;` + - 因为在同一个level里面重复的数字在下一个dfs level里面是会被考虑到的, 这里必须skip +- the result is trivial, save success list into result. +- Time complexity: O(k * 2^n), k = average result length + - 1) Assume on average, there are k elements in result + - 2) Since element can be used ONLY once, so the total # of solutions can be `C(n, k)`: `pick k out of n` + - 3) Now let k be any number [0, n], so total # of solutions can be: `C(n,0) + C(n,1) + C(n,2) + ... C(n,n) = 2^n` + - 4) Now: `the new ArrayList<>(list)` takes average O(k) time + - Total: O(k * 2^n) +- Space: O(n), stack depth, if not counting results size + + + +--- + +**274. [523. Continuous Subarray Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/523.%20Continuous%20Subarray%20Sum.java)** Level: Medium Tags: [Coordinate DP, DP, Math, PreSum, Subarray] + + +给一个非负数的数列和数字k(可正负, 可为0). 找到连续子序列(长度超过2), 使得这个subarray的sum 是 k的倍数. 问: 是否可能? + +#### Method1: Validate Mod Result +- Check if mod result exist in earlier preSum +- Utilize `Pigeonhole principle` to optimize: + - 1) put positive integers into k slots + - 2) when # of integers > 2*k, then there must be a range sum that is multipler of k + - more illustration here: https://leetcode.com/problems/continuous-subarray-sum/solution/ +- Draw the presum and try to take mod of each presum and save to set, we realize + - 1) assume a mod result = 7, and we mark it in the set + - 2) some time later, after summing up more values, (7 + x + y ...+ z) % k == 7 + - it means `(x + y ...+ z) % k == 0` + - There is a `整除` exist; return true +- Meanwhile, if we want to record the list of indexes, we can use a Map rather than set. +- Note: if all we do to the presum is to % k, therefore `preSum % k` can represent `presum` in some cases. +- time: O(n) +- space: O(k), size restrited by mod result of `%k` + + +#### Method2: DP, PreSum +- PreSum[]: + - 1) cal preSum array + - 2) preSum(i, j) = continuous range sum + - 3) determine if `preSum(i, j) % k == 0` +- time: O(n^2) +- DP (坐标型. specifically, preSum[]) + - 记录在0 ~ i点(包括nums[i], 以nums[i]结尾)的sum, 坐标型动态规划. + - dp[i] = dp[i - 1] + nums[i]; + +#### Method3: 直接算结果 +- 从sum = 每次[i ~ j]的所有情况 +- time: O(n^2) +- space: O(1) + + + +--- + +**275. [364. Nested List Weight Sum II.java](https://github.com/awangdev/LintCode/blob/master/Java/364.%20Nested%20List%20Weight%20Sum%20II.java)** Level: Medium Tags: [DFS, NestedInteger] + + +#### Method1: DFS +- Build a list of NestedInt +- DFS: + - sum up integers in the list are integers + - dfs on nested list + - overallSum = sum * (depth+1) + - End state: if no nested list (no more child dfs), return depth 1 +- Parent level: sum up all ints and times the (depth+1) + + +#### Method2: BFS +- Using stack to flatten all nestedList, and process in the end +- Can actually use list, does not need to be stack. +- uses more memory + + + +--- + +**276. [49. Group Anagrams.java](https://github.com/awangdev/LintCode/blob/master/Java/49.%20Group%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 (too slow) +- 和check anagram 想法一样:转化并sort char array,用来作为key。 +- 把所有anagram 存在一起。注意结尾Collections.sort(). +- O(NKlog(K)), N = string[] length, k = longest word length + + + + +--- + +**277. [438. Find All Anagrams in a String.java](https://github.com/awangdev/LintCode/blob/master/Java/438.%20Find%20All%20Anagrams%20in%20a%20String.java)** Level: Medium Tags: [Hash Table, Sliding Window, Two Pointers] + + +跟 Permutation in String 很像. 给短string p, 长string s. + +找所有p的 anagram (permutation) 在s 里面的起始index. + +#### Method1: Sliding Window, Hash Table. +- A creative way of using anagram char count `hash[c] >= 0` to determine if the curr c is a target char of the deesired anagram. + - because we always reduce hash[c]-- for all characters + - so only the anagram chars would be `hash[c] >= 0` after reducing. +- https://leetcode.com/problems/minimum-window-substring/discuss/26808/here-is-a-10-line-template-that-can-solve-most-substring-problems +- Slinding window always has left/right pointer: + - 1) at any given time move 1 index at a time: expand right window, process rsult, shrink left window + - 2) one of the basic goal is to maintain fixed window size +- algo: + - calc char freq of the target p, and store in a hash[256]; it will be used to distinguish anagram chars: `hash[c] >= 0` indicates a anagram char + - expand right window: move right to expand the window; ONLY when meeting a anagram char, count-- + - process result: if count reduces to 0, one anagram is found + - shrink left window: if (right - left) == p.length(), drop curr left char, and move forward +- how could we rely on only just `count == 0`? + - the hidden pre-condition is `right - left must already be p.length()`, which is validaterd in prev iteration +- time: O(n) +- space: O(1) + +#### Method2: HashTable +- count character apperance -> hash table, here just a int[26] + - use a window to record count++ and count--, in order to compare with countP +- prep the countP takes O(m) time +- time: O(n) + O(m) +- space: O(n) + + + + + +--- + +**278. [138. Copy List with Random Pointer.java](https://github.com/awangdev/LintCode/blob/master/Java/138.%20Copy%20List%20with%20Random%20Pointer.java)** Level: Medium Tags: [Hash Table, Linked List] + + +deep copy linked list. linked list 上有random pointer to other nodes. + +#### HashMap, Linked List, time, space: O(n) +- Basic Implementation of copy linked list: + - use a iterator node to iterate over the list: 遍历head.next .... null. + - use a dummy node to hold reference to the iterator node. +- Map: 1. avoid creating same node; 2. return the new node if existing + - 每一步都check map里面有没有head. 没有? 加上 + - 每一步都check map里面有没有head.random. 没有? 加上 +- Note, there is a way to skip the extra map O(n): https://leetcode.com/problems/copy-list-with-random-pointer/discuss/43491/A-solution-with-constant-space-complexity-O(1)-and-linear-time-complexity-O(N) + - However, creating a deep clone of the list is already O(n) extra space, so it is NOT effectively O(1) w/o map + - It may be beneficial, if we can not hold all nodes in memory, then the approach w/o map is more applicable. + + + +--- + +**279. [159. Longest Substring with At Most Two Distinct Characters.java](https://github.com/awangdev/LintCode/blob/master/Java/159.%20Longest%20Substring%20with%20At%20Most%20Two%20Distinct%20Characters.java)** Level: Medium Tags: [Hash Table, Sliding Window, String, Two Pointers] + + +如题. + +#### Method1: Slinding Window, Two Pointers: move 1 element at a time +- Typical slinding window: the goal is to keep a distinct char size/window of size 2. +- use a map to track; map.size() is the window size. Follow the template + - 1) move right pointer, and update freq count map + - 2) if map.size() == 2, process and record max len + - 3) if map.size() > 2, maintain window size: drop curr left char, update map +- return max +- time: O(n) +- space: O(1) + +#### Method2: Sliding window, Two Pointer: truncate the entire block at a time +- record last occurance index in map + - when size goes over limit, find last occurance of left-most element + - set left = leftMostIndex + 1. + - This truncates entire block before the last occurance of left-most element +- sliding window 的切割: 用hashmap 存 last occurrance of char index; +- map.remove(c) 之后, 就等于彻底切掉了那一段; 那么 map.get(c) + 1 也就是新的 left window border +- time: O(n) +- space: O(1) + + + +--- + +**280. [1043. Partition Array for Maximum Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/1043.%20Partition%20Array%20for%20Maximum%20Sum.java)** Level: Medium Tags: [DFS, DP, Graph, Memoization] + + +#### Top-Down DFS + Memoization +- Pick a subset (max-size k), and produce sub problem to solve by dfs +- NOTE: no need to change actual index value. That makes this problem easier (no need to record the choice path) +- time: O(n), calc memo[n] +- space: O(n), memo + stack depth + + + +--- + +**281. [33. Search in Rotated Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/33.%20Search%20in%20Rotated%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- 关键点, 是找到 [mid]是在左边/还是右边的continous increasing subarray: 比较 `A[start] < A[mid]` +- 在两个section 里面分别讨论 target 的位置 + - 1. `nums[start] < nums[mid]`: start是从index=0开始的, 那就说明 `mid在前半段` + - 2. `nums[start] > nums[mid]`: start是从index=0开始的, 那就说明 `mid在后半段` +- Binary search template: + - 1) `start + 1 < end` (adjacent indexes) + - 2) start/end = mid, + - 3) compare start and end individually + +#### binary search break point, 然后继续binary search target +- 1. binay search break point +- 2. binary search target +- 注意等号,在判断target在前半段还是后半段:if (A[p1] <= target && target <= A[breakPoint]) + + + + + +--- + +**282. [133. Clone Graph.java](https://github.com/awangdev/LintCode/blob/master/Java/133.%20Clone%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: `map` + - 1) make new curr node; + - 2) clone all neibhors and add them +- Use the map to avoid visited nodes +- time: O(n). visit all nodes +- space: O(n). Technically only travels n levels/stacks to circle all nodes (undirected & connected) + +#### 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. + + + +--- + +**283. [743. Network Delay Time.java](https://github.com/awangdev/LintCode/blob/master/Java/743.%20Network%20Delay%20Time.java)** Level: Medium Tags: [BFS, DFS, Graph, Heap, PQ] + + +quesiton: sorting by travel delay/time will find better answer earlier? + +#### Method1: BFS with PQ on graph +- `Dijkstras algorithm` is based on repeatedly making the candidate move that has the least distance travelled. +- PQ: pick close node to vist, and add siblings back to PQ +- avoid visited +- time: O(nLogn), visit n nodes, each time insert to heap takes O(logn) time +- space: O(n) + +#### Method2: DFS with Sort +- 1) build graph map, 2) traverse map, 3) prioritize short delay nodes first +- use a map`` globally track dealy to nodes; compare all at the end + + + +--- + +**284. [636. Exclusive Time of Functions.java](https://github.com/awangdev/LintCode/blob/master/Java/636.%20Exclusive%20Time%20of%20Functions.java)** Level: Medium Tags: [Stack] + + +#### Stack +- Task time range: + - start range = next task timestamp - start.timestamp + - end range = curr task timestamp - last task timestamp + 1; because end node is inclusive. +- How to think of using stack: a task cannot finish until end is met; a early task cannot stop until a later task ends + - Alternatively, we can use a hashmap to track as well +- Keep track of the timestamp +- make sure to +1 when end node is met because end task is inclusive to this finishing task + + + + +--- + +**285. [692. Top K Frequent Words.java](https://github.com/awangdev/LintCode/blob/master/Java/692.%20Top%20K%20Frequent%20Words.java)** Level: Medium Tags: [Hash Table, Heap, MaxHeap, MinHeap, PriorityQueue, Trie] + + +给一串String. 找到top k frequent words. + +#### Method1: Bucket Sort +- 1) Build frequency map, 2) use frequency map to build freq bucket +- Loop from largest bucket freq -> 0, and output. +- Time: Solid O(n) +- Space: O(n) + +#### Method2: 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 + +#### Method3: 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/ + + +#### Deleted Attempt: HashMap + collections.sort() +- 用HashMap存frequency, 用ArrayList存lists of words。最后返回从尾部向前数的k个。 +- 注意排序时Collection.sort()的cost是O(nLogk) +- not efficient + + + + +--- + +**286. [426. Convert Binary Search Tree to Sorted Doubly Linked List.java](https://github.com/awangdev/LintCode/blob/master/Java/426.%20Convert%20Binary%20Search%20Tree%20to%20Sorted%20Doubly%20Linked%20List.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Linked List, Tree] + + +把 BST 转换成一个 sorted doubly linked list. (in-place) + +#### Tree, In-order traversal, Divide and Conquer +- Regular convert BST to sored list: in-order traversal + - Carefully doubly link node head, tail + - traverse all nodes, DFS 好做: `left, curr, right` +- Tail: + - Assume head is found for sub tree, then `tail = head.left` + - Link `LeftTail <-> Curr Root <-> RightHead` + - Link `RightTail <-> LeftHead` +- In place: + - 同 `Node {val, left, right}`, w/o new doubley linked list class + - different from `Convert Binary Search Tree to Sorted Doubly Linked List (extra space)` + + + +--- + +**287. [8. String to Integer (atoi).java](https://github.com/awangdev/LintCode/blob/master/Java/8.%20String%20to%20Integer%20(atoi).java)** Level: Medium Tags: [Math, String] + + +#### String +- Handling use cases +- Parse steps: + - 0. trim space + - 1 parse operator + - 2 trim leading zero + - 3. get number string +- Validation: + - 1. max length over max integer length + - 2. exceed min/max value +- Alternatively, regular expression, but not applicable in interview: if (!str.matches("[+-]?(?:\\d+(?:\\.\\d*)?|\\.\\d+)")). 猛了一点 + + + +--- + +**288. [361. Bomb Enemy.java](https://github.com/awangdev/LintCode/blob/master/Java/361.%20Bomb%20Enemy.java)** Level: Medium Tags: [Coordinate DP, DP] + + +2D grid, 每个格子里面可能是 'W' wall, 'E' enemy, 或者是 '0' empty. + +一个bomb可以往4个方向炸. 求在grid上面, 最大能炸掉多少个敌人. + +#### Method1: 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 的变换. + +#### Method2: Analyze, col count array: +- Inspired by: https://leetcode.com/problems/bomb-enemy/discuss/83387/Short-O(mn)-time-O(n)-space-solution +- Analyize the problem: need to add up 2 directions at any given point. + - Notice 1: if I traverse row by row, each colSum at a specific col j is likely to be the same + - Unless there is a Wall in last row, so we have to calclate the col sum starting from current row, below the Wall + - Notice 2: for row it is the same: + - If I in a new spot row[i][j], where (i-1) is Wall, i need to sum row from 0 +- we will process each point: + - process row by row and add up row sum + - buffer col[j] in an array vertically so we can resue + - make sure to recalculate row sum or col sum if last row index or last col index is Wall +- time: O(mn) traversal +- space: O(n) only use a column array + + + +--- + +**289. [402. Remove K Digits.java](https://github.com/awangdev/LintCode/blob/master/Java/402.%20Remove%20K%20Digits.java)** Level: Medium Tags: [Greedy, Monotonous Stack, Stack] + + +#### Monotonous Stack (Increasing) +- Greedy: Remove 1) earlier digits(数位靠前权值大), 2) large digits +- Keep a increasing stack that: + - use stack.peek() to guard incoming digit + - if peek is larger than incoming digit, continue `stack.pop()` +- Result: monotonous increasing stack. Print it in correct order. + + + + +--- + +**290. [98. Validate Binary Search Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/98.%20Validate%20Binary%20Search%20Tree.java)** Level: Medium Tags: [BST, DFS, Divide and Conquer, Tree] + + +验证是否是BST by definition + +#### DFS +- 查看每个parent-child关系: + - leftchild < root < rightChild + - all of left child < curr < all of right child +- 方法: 把root.val 传下来作为 max 或者 min, valid child in (min, max) +- BST 有两个极端: left-most-leaf is the smallest element, and right-most-leaf is largest + - imagine we know the two extreme border: Long.MIN_VALUE, Long.MAX_VALUE +- min/max: long type to meet edge case: node.val = Integer.MAX_VALUE + + + +--- + +**291. [1123. Lowest Common Ancestor of Deepest Leaves.java](https://github.com/awangdev/LintCode/blob/master/Java/1123.%20Lowest%20Common%20Ancestor%20of%20Deepest%20Leaves.java)** Level: Medium Tags: [BFS, DFS, Tree] + + +#### Method1: DFS, top-down +- key concetpL the `common ancester of deppest leaves` must have its `two branch being same depth`. problem sovled. +- dfs on both branch +- if returned depth equals & equal to max depth, record common ancestor +- time: O(n) traversal 1 pass +- space: O(n) dfs worst case depth + +#### Method2: bottom-up, find leaves first and traverse backwards +- 1) find leaf nodes, and store backward map to root (DFS/ BFS both work) +- 2) use leaf nodes to find way backwards till common node is found; return +- time: O(n) but two passes +- space: O(n) dsf + map storage +- this approach is more brutle and uses exrtra spaces + + + +--- + +**292. [921. Minimum Add to Make Parentheses Valid.java](https://github.com/awangdev/LintCode/blob/master/Java/921.%20Minimum%20Add%20to%20Make%20Parentheses%20Valid.java)** Level: Medium Tags: [] + + + +#### Method1: Stack +- use stack to verify the input/output of '(' and ')' +- time, space: O(n) + +#### Method1: Simpilfy stack with open parentheses count +- time:(n), space: O(1) + + + +--- + +**293. [399. Evaluate Division.java](https://github.com/awangdev/LintCode/blob/master/Java/399.%20Evaluate%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 + + + +--- + +**294. [785. Is Graph Bipartite.java](https://github.com/awangdev/LintCode/blob/master/Java/785.%20Is%20Graph%20Bipartite.java)** Level: Medium Tags: [BFS, DFS, Garph] + + +#### DFS marking each node with a state +- `bipartite` require each node to be in exact 1 party, which means it only has 1 state +- DFS to mark node with one state; and mark its edges as reversed state + - If any node state has been assigned by different from desired one, return false. + +#### BFS, Queue +- Use `Boolean states[i]` to represent visted & state +- Try all nodes with for loop, and skip visited nodes (similar validation rules as in dfs) +- In `int next : graph[curr]`, test next level first before adding. + + + +--- + +**295. [767. Reorganize String.java](https://github.com/awangdev/LintCode/blob/master/Java/767.%20Reorganize%20String.java)** Level: Medium Tags: [Greedy, Hash Table, Heap, Sort, String] + + + +We want to exhaust largest population and merge like merging k list. +Problem: largest population may result in them being adjacent. How to resolve? + +1) process and check at the end, or, 2) sanitize first and process assume correct input + +#### Method1: K(k=2) seats apart problem (w/o sanitization) +- Aggregate map, and sort the entry with priority queue.(Optionally, can use object `Letter {char c, int count}`) +- Naturally: we want to prioritize the largest population and exhaust it first, so we want to keep it in the a buffer queue + - it is a queue, first in first out + - monitor queue size k = 2, so that it holds off the just last-processed letter for 1 unit of time + - the buffer then sends the last-process item to the main priority queue (pq will sort it again) +- Error handling: largest population may have extra letter + - the main PQ has already exhausted + - but the largest-population-letter will end up stuck in the buffer queue + - it will never be picked up again so the final result sb will be shorter than orignal string: that is the error case +- Option0. Similar to `621. Task Scheduler`: + - use a buffer to hold potential letter to add back, but NOT ADD BACK YET, until k slots have been filled. +- time: O(m), m = # of unique letters +- space: O(nmLogm), n = length, pq sorting requires mlogm, we will visit all n nodes. + +#### Method2: HashMap, Sort (Sanitize input) +- put all in map + - Sanitize the input: if certain popular char count is over (n + 1)/2, then it should fail right away, just return empty map. + - Once the input is sanitized, when building results, we can be greedy and consume most popular char and then the rest +- Int[2] can be used store char and count + - PriorityQueue can sort int[]. Okay to not specific length of int[] when defining pq. + - Alternatively, can use a Letter {char c, int count} to represent + + + + + +--- + +**296. [71. Simplify Path.java](https://github.com/awangdev/LintCode/blob/master/Java/71.%20Simplify%20Path.java)** Level: Medium Tags: [Stack, String] + + +给一个path, simplify成最简单形式. 注意考虑edge case + +#### Stack +- 理解unix path: + - 1. `.` 代表current directory, 可以忽略. + - 2. `../` 表示previous level. + - 3. double slash 可以忽略. + - 4. empty string 要output `/` +- parse by '/', and go over using stack + - put [folder] in stack + - ".." pop() 1 element of the stack, if anything + - "." stays the same +- output stack reversely: connect with '/', skip tail + + + +--- + +**297. [34. Find First and Last Position of Element in Sorted Array.java](https://github.com/awangdev/LintCode/blob/master/Java/34.%20Find%20First%20and%20Last%20Position%20of%20Element%20in%20Sorted%20Array.java)** Level: Medium Tags: [Array, Binary Search] + + +#### Binary Search +- need search left bound & right bound. +- use input parameter `direction` to binary search function +- Option0: simplification, inspired by `278. First Bad Version - Method1: Check is-NOT-BadVersion` + - 1) if found match, but NOT sure it is desired boundary, just leave it and keep going + - 2) check the final results after `binary search while loop` completes + - WHY? code is easier to read in this way. + + + +--- + +**298. [721. Accounts Merge.java](https://github.com/awangdev/LintCode/blob/master/Java/721.%20Accounts%20Merge.java)** Level: Medium Tags: [DFS, Hash Table, Union Find] + +- time O(mn) + +给一串account in format `[[name, email1, email2, email3], [name2, email,..]]`. + +要求把所有account merge起来 (可能多个record记录了同一个人, by common email) + + +#### Method1: 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, 最终用来输出. + +#### Method2: 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) + +### DFS +- TODO + + + +--- + +**299. [698. Partition to K Equal Sum Subsets.java](https://github.com/awangdev/LintCode/blob/master/Java/698.%20Partition%20to%20K%20Equal%20Sum%20Subsets.java)** Level: Medium Tags: [DFS, DP, Recursion] + + +#### Method1: Brutle DFS to find subsets and return results +- Target = total / k, fixed. +- DFS: Pick one num and dfs with the remaining nums for subproblem + - subproblem1: accumulate local sum to target + - EndState: accumulatedSum == target, continue with below + - subproblem2: after accumulatedSum == target, continue dfs with k-1 + - EndState: k == 0, return overall true +- Option1: DFS with nums, and boolean[] visited. Fast +- Option2: DFS with queue. + - Specially handling: dfs(size+1) to prevent `while(size-- >0)` from skipping last item at index 0 + + +#### Method2: DP +- the problem aims to find true/false capability +- bit-masking. TODO. The DP approach is kinda hard-level +- https://leetcode.com/problems/partition-to-k-equal-sum-subsets/discuss/335668/DP-with-Bit-Masking-Solution-%3A-Best-for-Interviews + + + +--- + +**300. [716. Max Stack.java](https://github.com/awangdev/LintCode/blob/master/Java/716.%20Max%20Stack.java)** Level: Medium Tags: [Design, Doubly Linked List, Stack, TreeMap] + + +#### Two Stack +- one to keep regular elements +- one to repat the max at current stack level +- time: O(n) for popMax() and O(1) for the rest operations +- space: O(n) + +#### TreeMap +- Reference: https://leetcode.com/problems/max-stack/solution/ +- Use TreeMap to store , which gives: **O(logN) insert, delete and find MAX** +- Key reason to use `DoubleLinkedList` is to perform O(1) removal for `popMax()` +- The problem becomes finding the target value & remove from DoubleLinkedList +- time: O(1) for popMax() and O(logN) for the rest +- space: O(n) + + + +--- + +**301. [366. Find Leaves of Binary Tree.java](https://github.com/awangdev/LintCode/blob/master/Java/366.%20Find%20Leaves%20of%20Binary%20Tree.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS: store nodes at the same depth +- the leaves are at depth 0 and the root is at highest depth +- dfs: the depth = index of the rst, start from depth = 0 at leaf +- end state: leaf node, add to rst, and return depth + + + + +--- + +**302. [156. Binary Tree Upside Down.java](https://github.com/awangdev/LintCode/blob/master/Java/156.%20Binary%20Tree%20Upside%20Down.java)** Level: Medium Tags: [DFS, Tree] + + +#### DFS +- Given that left & right nodes are always available in pair, at each level: + - perform dfs to find new root: return deepest left node as root + - pick out curr left node and fix current level: + - add right node as left node + - add root as right node + + + +--- + +**303. [416. Partition Equal Subset Sum.java](https://github.com/awangdev/LintCode/blob/master/Java/416.%20Partition%20Equal%20Subset%20Sum.java)** Level: Medium Tags: [Backpack, DP] + +#### Backpack DP +- the problem turns into: can we find a subset of items that sum up to target sum? +- create `boolean dp[j]` to represent if we can sum up to j, where j = sum value + - want to try out all items in num, + +#### DFS +- use dfs to find a subset of items that sum up to target sum? + + + +--- + +**304. [611. Valid Triangle Number.java](https://github.com/awangdev/LintCode/blob/master/Java/611.%20Valid%20Triangle%20Number.java)** Level: Medium Tags: [Array, Two Pointers] + + +#### Method1: Fix max and backward counting +- Sort nums: O(nlogn) +- Set max value fixed on right side at k + - set 2nd value from right index j + - set last value at min index i + - if `nums[i] + nums[j] > nums[k]`: with fixed j, i can pick from [i, j-1] combinations + - then j--, to pick another j candidate + - maintain a window [i,j]; if invalid, move i++ +- time: O(n^2) +- Note: very similar to 3-sum, fixing 1 index and use 2 pointers to move window + +#### Method2: Fix min and forward counting +- Sort nums: O(nlogn) +- Set min value at i + - set 2nd value at j=i+1; and 3rd value at k=i+2 + - find max of k that fits into triangle + - count all possible k candidates from [j+1, k] + - then move j to a new candidate +- O(n^2) + + + +--- + +**305. [341. Flatten Nested List Iterator.java](https://github.com/awangdev/LintCode/blob/master/Java/341.%20Flatten%20Nested%20List%20Iterator.java)** Level: Medium Tags: [Design, NestedInteger, Stack] + + +#### Method1: Stack holds items from back of the list +- Option1: always set integer on top of the stack everywhere + - if not, poping stack until the top is integer + - code is easy +- Option2: in hasNext(), faltten the list in stack + +#### Method2: DFS preprocessing. +- 用queue to store all items. Kinda hack. Defeat the purpose of the problem. +- Super fast to query next(), however, needs to holds everything in memory +- O(n) + + + +--- + +**306. [254. Factor Combinations.java](https://github.com/awangdev/LintCode/blob/master/Java/254.%20Factor%20Combinations.java)** Level: Medium Tags: [BFS, Backtracking, DFS] + + + +#### Method1: DFS +- build candidate into dfs: treat each list candidate as success, add to rst +- remove last item from the candidate, try to add factor to it, and supply it with remain element +- backtrack after dfs + + +#### Method2: BFS +- Check if the number can be devided by [2, sqrt(n)], return a list of possible factors. Only check till `Math.sqrt(n)` + - build suffixes: use the factor to devide last element of list and replace last element + - build candidate: replace last element of the queue item with new list of suffixes; add to rst + - add success item back to queue: in case last element can be simplified +- **remove dupilcates**: since we start factor from [2, sqrt(n)], the final factor list should be **ascending**!! +- time: O(x), x is the # of results +- space: O(y), y is all ongoing candidates in queue + + + +--- + +**307. [739. Daily Temperatures.java](https://github.com/awangdev/LintCode/blob/master/Java/739.%20Daily%20Temperatures.java)** Level: Medium Tags: [Hash Table, Monotonous Stack, Stack] + + +#### Method1: Monotonous Stack +- Goal: given a index i, want right-side closest & higer number +- Draw example: right-most number at base, and builds up monotonous stack (mountain shape) + - add smaller item on top of stack + - keep popping if peek is higher than incoming +- space: O(n), time:O(n) + +#### Method2: `Map `, kinda of like bucket sort +- Refernece: https://leetcode.com/problems/daily-temperatures/solution/ +- From right side: + - 1) record tempIndex[currTemp] = i; + - 2) Brutle find smallest temp index in range [currTemp + 1, 100] and record as result + + +--- + +**308. [373. Find K Pairs with Smallest Sums.java](https://github.com/awangdev/LintCode/blob/master/Java/373.%20Find%20K%20Pairs%20with%20Smallest%20Sums.java)** Level: Medium Tags: [Heap, MaxHeap, MinHeap] + + +#### Method1: MinHeap wiht k size +- This approach follows the pattern of finding min pair: + - 1) only need to store k pairs + - 2) always start from min of A list and min of B list + - 3) pre-build k pairs honoring A list, and then pick the min pair, and start swapping with min of list B +- First attemp all first k pairs from nums1[i] against nums2[0] <=k : O(k) +- Use queue to pull min node and save results +- Use the nums1 val from the min node, pair up with nums2[j], add back to queue to sort +- overall runtime: O(klogk) +- space: O(k) + +#### Method2: MaxHeap with k size +- Brutle: build all pairs time O(mn), sort with maxHeap pq with k size, and find top k +- overall time: O(mnLogK) +- space: O(k) + + + +--- + +**309. [215. Kth Largest Element in an Array.java](https://github.com/awangdev/LintCode/blob/master/Java/215.%20Kth%20Largest%20Element%20in%20an%20Array.java)** Level: Medium Tags: [Divide and Conquer, Heap, MinHeap, PriorityQueue, Quick Select, Quick Sort] + + +kth largest in array + +#### PriorityQueue, MinHeap +- Use minHeap to maintain PQ of k size and return PQ.peek() + - Maintain MinHeap: only allow larger elements (which will squzze out the min value) + - Remove peek() of queue if over size +- O(nlogk) + +#### Quick Select, Quick Sort +- 用Quick Sort 里面partion的一部分: sort结束后是ascending的. + - kth largest = (n - k)th smallest + - in partioned array (quick sort), the portion before pivot are less than pivot + - that is, the `pivot value` is the divider: anything after pivot is larger than it. + - after `swap(nums, low, pivot)`: index low has the (n-k)th smallest, if `low = n-k` +- Steps: + - each iteration: pick pivot,然后从low,和high都和pivot作比较 + - Find `low>pivot, high=k都可以, 巧用一个 min(preSum)的概念. +- 找k的时候, 画图, 可以看出来, 其实要的是 k window 里面的sum [x, i], 所以要用 sum[0, i] - sum[0, x] + +需要仔细去读下面的notes. + + + +--- + +**1. [Maximum Subarray III.java](https://github.com/awangdev/LintCode/blob/master/Java/Maximum%20Subarray%20III.java)** Level: Review Tags: [] + + + +--- +