1 - 从strStr谈面试技巧与代码风格
13.字符串查找
要求:如题
思路:(自写AC)双重循环,内循环读完则成功
还可以用Rabin,KMP算法等
public int strStr(String source, String target) { if (source == null || target == null) { return -1; } char[] sources = source.toCharArray(); char[] targets = target.toCharArray(); int m = sources.length; int n = targets.length; for (int i = 0; i <= m - n; i++) { int j = 0; while (j < n && sources[i + j] == targets[j]) { j++; } if (j == n) { return i; } } return -1; }
2017-01-16
594.StrStr II
要求:时间复杂度为O(m+n)
思路:用Rabin Karp算法,这种算法沿用了暴力法,但加入了hashcode的思想,因为target和substring的hashcode进行对比只需要O(1)时间复杂度,而且substring变成下一个子串也只需要进行一次O(1)的运算(就是把头部减掉,再加上新的字符的hashcode),等target和substring的hashcode相等再对它们进行真正的字符串比较
warn:由于hashcode大小有可能越界,所以要对其进行一个固定BASE的取模运算,如果变成负的时候要把它加一个BASE变回正数
public class Solution { public final int BASE = 1000000; /** * @param source a source string * @param target a target string * @return an integer as index */ public int strStr2(String source, String target) { if (source == null || target == null) { return -1; } int m = target.length(); if (m == 0) { return 0; } // count 31 ^ m int power = 1; for (int i = 0; i < m; i++) { power = (power * 31) % BASE; } // target hashcode int targetCode = 0; for (int i = 0; i < m; i++) { targetCode = (targetCode * 31 + target.charAt(i)) % BASE; } // substring hashcode int substrCode = 0; for (int i = 0; i < source.length(); i++) { substrCode = (substrCode * 31 + source.charAt(i)) % BASE; if (i < m - 1) { continue; } // 超过m长度后减去头字符的hashcode if (i >= m) { substrCode = substrCode - (power * source.charAt(i - m)) % BASE; //当前字符的hashcode比之前取模后的字符串的hashcode大就变成负数 if (substrCode < 0) { substrCode += BASE; } } if (substrCode == targetCode) { if (source.substring(i - m + 1, i + 1).equals(target)) { return i - m + 1; } } } return -1; } }
2017-09-19
17.子集
要求:集合中不含重复,答案中集合的元素升序排列
思路:(自写修改AC)DFS模板,背
warn:记得deep copy
public ArrayList<ArrayList<Integer>> subsets(int[] nums) { ArrayList<ArrayList<Integer>> results = new ArrayList<>(); if (nums == null || nums.length == 0) { return results; } ArrayList<Integer> subset = new ArrayList<>(); //排序非必要 Arrays.sort(nums); //从空集开始 dfsHelper(nums, 0, subset, results); return results; } private void dfsHelper(int[] nums, int startIndex, ArrayList<Integer> subset, ArrayList<ArrayList<Integer>> results) { //deep copy results.add(new ArrayList<Integer>(subset)); for (int i = startIndex; i < nums.length; i++) { subset.add(nums[i]); dfsHelper(nums, i + 1, subset, results); subset.remove(subset.size() - 1); } }
2017-01-16
18.带重复元素的子集
要求:集合中有重复元素,不能出现重复的子集
思路:模板中加入了nums[i] == nums[i - 1] && i != startIndex,表示新一轮从startIndex开始加入的不能重复
public class Solution { /* * @param nums: A set of numbers. * @return: A list of lists. All valid subsets. */ public List<List<Integer>> subsetsWithDup(int[] nums) { List<List<Integer>> results = new ArrayList<>(); if (nums == null || nums.length == 0) { return results; } Arrays.sort(nums); List<Integer> subset = new ArrayList<>(); dfsHelper(nums, 0, subset, results); return results; } private void dfsHelper(int[] nums, int startIndex, List<Integer> subset, List<List<Integer>> results) { results.add(new ArrayList<>(subset)); for (int i = startIndex; i < nums.length; i++) { if (i != 0 && nums[i] == nums[i - 1] && i != startIndex) { continue; } subset.add(nums[i]); dfsHelper(nums, i + 1, subset, results); subset.remove(subset.size() - 1); } } }
2017-09-19
15.全排序
要求:没有重复元素
思路:与子集一样,但要加入判断数组visited[],而且还要判断每次生成的list长度是否等于列表长度才加入结果中
View Code
2017-09-19
16.带有重复元素的排序
要求:如题
思路:与含有重复元素的子集一样,要判断当前的数是否与上一个数一样,并且判断上一个数是否已放在排序中
class Solution { /** * @param nums: A list of integers. * @return: A list of unique permutations. */ public List<List<Integer>> permuteUnique(int[] nums) { List<List<Integer>> lists = new ArrayList<>(); List<Integer> list = new ArrayList<>(); if (nums == null) { return lists; } if (nums.length == 0) { lists.add(list); return lists; } Arrays.sort(nums); int[] visited = new int[nums.length]; helper(nums, visited, list, lists); return lists; } private void helper(int[] nums, int[] visited, List<Integer> list, List<List<Integer>> lists) { if (list.size() == nums.length) { lists.add(new ArrayList<Integer>(list)); } for (int i = 0; i < nums.length; i++) { if (visited[i] == 1) { continue; } if (i != 0 && nums[i] == nums[i - 1] && visited[i - 1] == 0) { continue; } visited[i] = 1; list.add(nums[i]); helper(nums, visited, list, lists); list.remove(list.size() - 1); visited[i] = 0; } } }
2017-09-19
2 - 二分法与排序数组
457.二分查找标准写法
注意:循环判断条件、double check
public int findPosition(int[] nums, int target) { // write your code here 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; } else if (nums[mid] < target) { start = mid; } else { end = mid; } } if (nums[start] == target) { return start; } if (nums[end] == target) { return end; } return -1; }
2018-01-17
458.目标最后位置
要求:二分法找目标最后一次
思路:(自写但有错误)double check时候要把end放在前面
public int lastPosition(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) { start = mid; } else if (nums[mid] < target) { start = mid; } else { end = mid; } } if (nums[end] == target) { return end; } if (nums[start] == target) { return start; } return -1; }
2017-01-23
141.x的平方根
要求:二分法找
思路:(自写有错误)定义时应为long,防止溢出
public int sqrt(int x) { if (x == 0) { return 0; } long start = 1, end = x; while (start + 1 < end) { long mid = start + (end - start) / 2; if (mid * mid == x) { return (int) mid; } else if (mid * mid < x) { start = mid; } else { end = mid; } } return (int) start; }
2017-01-23
14.二分查找
要求:找第一次出现的位置
思路:(自写一次AC)注意dc时候把start放前面
public int binarySearch(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) { end = 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; }
2017-01-23
447.在大数组中查找
要求:只能通过get来获得一个位置,不知道数组长度,获取target第一次出现位置
思路:(自写一次AC)多加了一个找长度的while
public int searchBigSortedArray(ArrayReader reader, int target) { //找length int length = 1; while (reader.get(length - 1) < target) { length *= 2; } int start = 0, end = length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (reader.get(mid) == target) { end = mid; } else if (reader.get(mid) < target) { start = mid; } else { end = mid; } } if (reader.get(start) == target) { return start; } if (reader.get(end) == target) { return end; } return -1; }
2017-01-23
183.木材加工
要求:找满足数量k的最长分法
思路:(自写出错)二分答案,mid选的是长度,判断的是分了之后的结果是否满足k,自写时没加入count方法所以double check出错
public int woodCut(int[] L, int k) { if (L == null || L.length == 0) { return 0; } Arrays.sort(L); //也可以遍历求最大值 int start = 1, end = L[L.length - 1]; while (start + 1 < end) { int mid = start + (end - start) / 2; if (count(L, mid) >= k) { start = mid; } else { end = mid; } } if (count(L, start) >= k) { return start; } if (count(L, end) >= k) { return end; } return 0; } //count方法求每次的数量 private int count(int[] L, int len) { int sum = 0; for (int i = 0; i < L.length; i++) { sum += L[i] / len; } return sum; }
2017-01-23
159.寻找旋转排序数组(RSA)中的最小值
要求:升序数组旋转后,求旋转的那个最小值点
思路:(掌握思路后自写一次AC)选最后一个数作为target,然后求小于等于target的第一个位置上的数
public int findMin(int[] nums) { if (nums == null || nums.length == 0) { return -1; } int start = 0, end = nums.length - 1; //choose last number as target //and find the first position if nums[i] <= target int target = nums[nums.length - 1]; while (start + 1 < end) { int mid = start + (end - start) / 2; if (nums[mid] <= target) { end = mid; } else { start = mid; } } //double check if (nums[start] <= target) { return nums[start]; } else { return nums[end]; } }
2017-01-24
75.寻找峰值
要求:找出任意峰值点
思路:(掌握思路一次AC)与旁边的比较,if先写start = mid,else为end = mid,最后要dc,属于Half Half法
tips:end取length - 2
public int findPeak(int[] A) { if (A == null || A.length == 0) { return -1; } int start = 0, end = A.length - 2; //peak can only happen before len - 2 while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[mid] < A[mid + 1]) { start = mid; } else { end = mid; } } if (A[start] < A[end]) { return end; } else { return start; } }
2017-01-24
74.第一个错误的代码版本
要求:出现错误后就一直错,用接口判断是否错
思路:(读清楚题后AC)XXOO找第一个
public int findFirstBadVersion(int n) { if (n == 0) { return 0; } int start = 1, end = n; while (start + 1 < end) { int mid = start + (end - start) / 2; if (SVNRepo.isBadVersion(mid)) { end = mid; } else { start = mid; } } if (SVNRepo.isBadVersion(start)) { return start; } else { return end; } }
2017-01-25
62.搜索旋转排序数组
要求:在RSA中找出target
思路:(答案)属于Half Half,用start来判断mid在断点左还是右,左的时候利用start和mid夹逼,右的时候用mid和end夹
public int search(int[] A, int target) { if (A == null || A.length == 0) { return -1; } int start = 0, end = A.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[mid] == target) { return mid; } if (A[start] < A[mid]) { if (A[start] <= target && target <= A[mid]) { end = mid; } else { start = mid; } } else { if (A[mid] <= target && target <= A[end]) { start = mid; } else { end = mid; } } } if (A[start] == target) { return start; } if (A[end] == target) { return end; } return -1; }
2017-01-25
437.书籍复印copy books
要求:pages数组表示每本书的页数,求k个人复印的最短时间
思路:(答案,还不完全理解)二分答案,先算出pages最大和总数,最快的情况是最大的复印完就全部复印完,最坏的情况是只有一个人复印全部书,所以是总数,没看懂countCopiers方法
public int copyBooks(int[] pages, int k) { if (pages.length == 0) { return 0; } int total = 0; int max = pages[0]; for (int i = 0; i < pages.length; i++) { total += pages[i]; if (max < pages[i]) { max = pages[i]; } } int start = max; int end = total; while (start + 1 < end) { int mid = start + (end - start) / 2; if (countCopiers(pages, mid) > k) { start = mid; } else { end = mid; } } if (countCopiers(pages, start) <= k) { return start; } else { return end; } } private int countCopiers(int[] pages, int limit) { int copiers = 1; int sum = pages[0]; for (int i = 1; i < pages.length; i++) { if (sum + pages[i] > limit) { copiers++; sum = 0; } sum += pages[i]; } return copiers; }
2017-01-25
459.closest number in sorted array
要求:找出排序数组中与target最接近的数,不要求第一个还是最后一个
思路:(自写AC)在模板基础上加多一个判断,记得double check
public int closestNumber(int[] A, int target) { if (A == null || A.length == 0) { return -1; } int start = 0, end = A.length - 1; int diff = Math.abs(A[0] - target); int index = 0; while (start + 1 < end) { int mid = start + (end - start) / 2; if (Math.abs(A[mid] - target) < diff) { diff = Math.abs(A[mid] - target); index = mid; } if (A[mid] == target) { return mid; } else if (A[mid] < target) { start = mid; } else { end = mid; } } if (Math.abs(A[start] - target) < diff) { return start; } if (Math.abs(A[end] - target) < diff) { return end; } return index; }
2017-03-08
28.search a 2D matrix
要求:在二维矩阵查找一个数是否存在
思路:(自写AC)找右上或左下
public boolean searchMatrix(int[][] matrix, int target) { if (matrix == null || matrix.length == 0 || matrix[0].length == 0) { return false; } int row = 0, col = matrix[0].length - 1; while (row < matrix.length && col >= 0) { if (matrix[row][col] == target) { return true; } else if (matrix[row][col] < target) { row++; } else { col--; } } return false; }
2017-03-08
38.search a 2D matrix II
要求:找出target出现几次
思路:和上题一样,只不过相等时要行与列同时变化
View Code
2017-09-25
585.maximum number in mountain sequence
要求:序列先升后降 找顶点,与75题比较差别是这个序列中只存在一个峰值,而75可能有多个
思路:(自写AC)与旁边比较
public int mountainSequence(int[] nums) { if (nums == null || nums.length == 0) { return 0; } if (nums.length == 1) { return nums[0]; } int start = 0, end = nums.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (nums[mid] > nums[mid - 1] && nums[mid] > nums[mid + 1]) { return nums[mid]; } else if (nums[mid] > nums[mid - 1]) { start = mid; } else { end = mid; } } if (nums[start] > nums[start + 1]) { return nums[start]; } else { return nums[end]; } }
2017-03-08
600.smallest rectangle enclosing black pixels(hard)
要求:image矩阵中包括1、0,其中1互相连同且image中只有一块,给出其中一个1的坐标,求把1的区域围绕起来的最小矩形
思路:(答案,没自写,答案的style不好)二分法,把给出坐标的四个方向都做一次二分,求出上下左右
public int minArea(char[][] image, int x, int y) { // Write your code here int m = image.length; if (m == 0) return 0; int n = image[0].length; if (n == 0) return 0; int start = y; int end = n - 1; int mid; while (start < end) { mid = start + (end - start) / 2 + 1; if (checkColumn(image, mid)) { start = mid; } else { end = mid - 1; } } int right = start; start = 0; end = y; while (start < end) { mid = start + (end - start) / 2; if (checkColumn(image, mid)) { end = mid; } else { start = mid + 1; } } int left = start; start = x; end = m - 1; while (start < end) { mid = start + (end - start) / 2 + 1; if (checkRow(image, mid)) { start = mid; } else { end = mid - 1; } } int down = start; start = 0; end = x; while (start < end) { mid = start + (end - start) / 2; if (checkRow(image, mid)) { end = mid; } else { start = mid + 1; } } int up = start; return (right - left + 1) * (down - up + 1); } private boolean checkColumn(char[][] image, int col) { for (int i = 0; i < image.length; i++) { if (image[i][col] == '1') { return true; } } return false; } private boolean checkRow(char[][] image, int row) { for (int j = 0; j < image[0].length; j++) { if (image[row][j] == '1') { return true; } } return false; }
还可以用bfs来做!
2017-03-08
462.目标出现总和
要求:找出target出现的次数,数组为升序
思路(自写):二分,找到target后用left与right指针找出第一次发现后,两边分别还有多少个相同的数
public class Solution { /* * @param A: A an integer array sorted in ascending order * @param target: An integer * @return: An integer */ public int totalOccurrence(int[] A, int target) { // write your code here if (A == null || A.length == 0) { return 0; } int start = 0, end = A.length - 1; int num = 0; while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[mid] == target) { num++; int left, right; left = mid - 1; right = mid + 1; while (left >= start && A[left] == A[mid]) { num++; left--; } while (right <= end && A[right] == A[mid]) { num++; right++; } return num; } else if (A[mid] < target) { start = mid; } else { end = mid; } } if (A[start] == target) { num++; } if (A[end] == target) { num++; } return num; } }
2017-09-25
254.drop eggs
要求:提供两个鸡蛋,总共有n层楼,求出最坏情况下,需要扔多少次,才能找到恰好扔坏鸡蛋的楼层k
思路:转化成x + (x - 1) + ... + 1 >= n,x就是答案(我也不知道为什么)
其实还可以用动态规划来做
public class Solution { /* * @param n: An integer * @return: The sum of a and b */ public int dropEggs(int n) { // write your code here long ans = 0; for (int i = 1; ; i++) { ans += (long)i; if (ans >= (long)n) { return i; } } } }
2017-09-25
460.找出k个最接近的数
要求:在升序数组中,找出与target最接近的k个数,返回结果时,从最接近到最远的数依次排序,若差值相等时先排小的数
思路:先找到最接近的数,然后在它两边再寻找k - 1个,用两个指针,判断两边谁的差值小谁先进
public class Solution { /* * @param A: an integer array * @param target: An integer * @param k: An integer * @return: an integer array */ public int[] kClosestNumbers(int[] A, int target, int k) { // write your code here if (A == null || A.length == 0) { return A; } if (k > A.length) { return A; } int[] results = new int[k]; //find the first one int index = findFirstOne(A, target); int start = index; int end = index + 1; for (int i = 0; i < k; i++) { //start < 0 , end >= length if (start < 0) { results[i] = A[end++]; } else if (end >= A.length) { results[i] = A[start--]; } else { //begin from the smaller if (target - A[start] <= A[end] - target) { results[i] = A[start--]; } else { results[i] = A[end++]; } } } return results; } private int findFirstOne(int[] A, int target) { int start = 0, end = A.length - 1; int diff = Math.abs(A[0] - target); int index = 0; while (start + 1 < end) { int mid = start + (end - start) / 2; if (Math.abs(A[mid] - target) < diff) { diff = Math.abs(A[mid] - target); index = mid; } if (A[mid] == target) { return mid; } else if (A[mid] < target) { start = mid; } else { end = mid; } } if (Math.abs(A[start] - target) < diff) { return start; } if (Math.abs(A[end] - target) < diff) { return end; } return index; } }
2017-09-25
61.搜索区间
要求:找出target在数组中出现的区间
思路(自写):找出第一个,然后从这开始循环判断
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 int[] searchRange(int[] A, int target) { // write your code here if (A == null || A.length == 0) { return new int[]{-1, -1}; } int[] result = new int[2]; int index = findFirstOne(A, target); result[0] = index; while (index != -1 && index + 1 < A.length && A[index] == A[index + 1]) { index++; } result[1] = index; return result; } private int findFirstOne(int[] A, int target) { int start = 0, end = A.length - 1; while (start + 1 < end) { int mid = start + (end - start) / 2; if (A[mid] >= target) { end = mid; } else { start = mid; } } if (A[start] == target) { return start; } if (A[end] == target) { return end; } return -1; } }
2017-09-27
3 - 二叉树与分治法
97.二叉树的最大深度
要求:如题
思路1:(答案)分治法,找出左右节点的最大值 + 1 = 当前父节点的深度
public int maxDepth(TreeNode root) { if (root == null) { return 0; } int left = maxDepth(root.left); int right = maxDepth(root.right); return Math.max(left, right) + 1; }
2017-01-26
思路2:(答案)遍历法,需要全局变量depth,每次遍历到子节点curDepth + 1,更新全局变量depth,遍历到最后为止
public class Solution { /** * @param root: The root of binary tree. * @return: An integer. */ private int depth; //全局变量 public int maxDepth(TreeNode root) { depth = 0; helper(root, 1); return depth; } private void helper(TreeNode root, int curDepth) { if (root == null) { return; } if (curDepth > depth) { depth = curDepth; } //遍历 helper(root.left, curDepth + 1); helper(root.right, curDepth + 1); } }
2017-01-26
480.二叉树的所有路径
要求:如题
思路1:(答案)分治法,分成两条路径,然后把路径的节点都加入到返回的结果中
warn:除了判断root == null,还要判断叶节点,因为要加入叶它自己
public List<String> binaryTreePaths(TreeNode root) { List<String> paths = new ArrayList<String>(); if (root == null) { return paths; } List<String> leftPaths = binaryTreePaths(root.left); List<String> rightPaths = binaryTreePaths(root.right); for (String path : leftPaths) { paths.add(root.val + "->" + path); } for (String path : rightPaths) { paths.add(root.val + "->" + path); } return paths; }
思路2:(答案)遍历法,paths作为全局变量,然后通过helper一步步构建每个path,仍然要判断叶
public List<String> binaryTreePaths(TreeNode root) { List<String> paths = new ArrayList<String>(); //全局变量 if (root == null) { return paths; } helper(root, root.val + "", paths); return paths; } private void helper(TreeNode root, String path, List<String> paths) { if (root == null) { return; } //leaf if (root.left == null && root.right == null) { paths.add(path); return; } if (root.left != null) { helper(root.left, path + "->" + root.left.val, paths); } if (root.right != null) { helper(root.right, path + "->" + root.right.val, paths); } }
2017-01-26
596.和最小的子树Minimum Subtree
要求:找出和最小的子数所对应的根节点
思路1:(答案)traverse + divide conquer,在helper中使用divide conquer的思想来算当前的和sum
warn:helper中返回值不能是void,因为算left 和 right要有值
public class Solution { /** * @param root the root of binary tree * @return the root of the minimum subtree */ //全局变量 private TreeNode subtreeRoot = null; private int subtreeSum = Integer.MAX_VALUE; public TreeNode findSubtree(TreeNode root) { //traverse helper(root); return subtreeRoot; } private int helper(TreeNode root) { if (root == null) { return 0; } //divide conquer int sum = helper(root.left) + helper(root.right) + root.val; if (subtreeSum > sum) { subtreeSum = sum; subtreeRoot = root; } return sum; }
思路2:只用divide conquer,增加ResultType类来保存每次递归时的最小和minSum、对应的根节点subtreeRoot、当前的和Sum
public class Solution { /* * @param root: the root of binary tree * @return: the root of the minimum subtree */ public class ResultType { public TreeNode subtreeRoot; public int minSum, sum; public ResultType(TreeNode subtreeRoot, int minSum, int sum) { this.subtreeRoot = subtreeRoot; this.minSum = minSum; this.sum = sum; } } public TreeNode findSubtree(TreeNode root) { // write your code here ResultType result = dfsHelper(root); return result.subtreeRoot; } private ResultType dfsHelper(TreeNode root) { if (root == null) { return new ResultType(null, Integer.MAX_VALUE, 0); } ResultType left = dfsHelper(root.left); ResultType right = dfsHelper(root.right); ResultType curr = new ResultType(root, left.sum + right.sum + root.val, left.sum + right.sum + root.val); if (left.minSum < curr.minSum) { curr.minSum = left.minSum; curr.subtreeRoot = left.subtreeRoot; } if (right.minSum < curr.minSum) { curr.minSum = right.minSum; curr.subtreeRoot = right.subtreeRoot; } return curr; } }
2017-10-12
2017-01-26
597.平均值最大的子树Subtree with Maximum Average
要求:找出平均值最大的子树对应的根节点
思路:(答案)traverse + divide conquer,加入ResultType类来保存sum和size(这里创建了ResultType类的全局变量,也就是体现了traverse的思想),在helper中先用分治思想算出当前的sum与size,更新较大average保存在全局变量中,helper同时也保存当前的ResultType,因为这样才会有left 和 right的数据,由下层的递归得到
warn:在对比average时,若通过sum除以size会带来误差,应当转成乘法的不等式
/** * 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 { //用ResultType来存储sum和size获得average private class ResultType { public int sum; public int size; public ResultType(int sum, int size) { this.sum = sum; this.size = size; } } //全局变量 private TreeNode subtreeRoot = null; private ResultType subtreeResult = null; public TreeNode findSubtree2(TreeNode root) { helper(root); return subtreeRoot; } //有返回值的helper private ResultType helper(TreeNode root) { if (root == null) { return new ResultType(0, 0); } //divide & conquer ResultType left = helper(root.left); ResultType right = helper(root.right); //当前的结果 ResultType currResult = new ResultType(left.sum + right.sum + root.val, left.size + right.size + 1); //相乘防止误差 if (subtreeRoot == null || subtreeResult.sum * currResult.size < currResult.sum * subtreeResult.size) { subtreeResult = currResult; subtreeRoot = root; } return currResult; } }
2017-01-27
453.二叉树的扁平化Flatten Binary Tree to Linked List
要求:如题
思路1:(答案)divide conquer,由于要返回最后一个点,所以要helper方法,把左子树最后一个连接右子树第一个,并把root的左节点代替右节点
warn:helper中作非空判断!
/** * 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 flatten(TreeNode root) { helper(root); } private TreeNode helper(TreeNode root) { if (root == null) { return null; } //把左右子树分别扁平化并找出最后一个数 TreeNode leftLast = helper(root.left); TreeNode rightLast = helper(root.right); //左子树最后一个连接右子树第一个,并把root的左节点代替右节点 if (leftLast != null) { leftLast.right = root.right; root.right = root.left; root.left = null; } //特殊判断 if (rightLast != null) { return rightLast; } if (leftLast != null) { return leftLast; } return root; } }
思路2:(答案)traverse,记录上一个点,然后把上一个点与当前进行连接
warn:要保存当前节点的右子节点??
/** * 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 */ //全局变量表示上一个节点 private TreeNode lastNode = null; public void flatten(TreeNode root) { if (root == null) { return; } //上个节点连接当前 if (lastNode != null) { lastNode.left = null; lastNode.right = root; } //更新lastNode lastNode = root; //保存当前节点的右子节点 TreeNode right = root.right; flatten(root.left); flatten(right); } }
思路3:还可以非递归来做,代码还没看懂
2017-01-27
595.二叉树的最长连续序列Binary Tree Longest Consecutive Sequence
要求:只能从上到下
思路1:(看完思路自写AC)traverse + divide conquer,helper中返回int类型
/** * 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 the length of the longest consecutive sequence path */ private int subtreeLen; public int longestConsecutive(TreeNode root) { subtreeLen = 0; helper(root); return subtreeLen; } private int helper(TreeNode root) { if (root == null) { return 0; } int left = helper(root.left); int right = helper(root.right); int currLen = 1; //最起码有根一个 if (root.left != null && root.val + 1 == root.left.val) { currLen = left + 1; } if (root.right != null && root.val + 1 == root.right.val) { currLen = right + 1; } if (currLen > subtreeLen) { subtreeLen = currLen; } return currLen; } }
思路2:(根据答案自写)ResultType + divide conquer,ResultType包括maxFromRoot和maxInSubtree
/** * 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 the length of the longest consecutive sequence path */ private class ResultType{ public int maxFromRoot; public int maxInSubtree; public ResultType(int maxFromRoot, int maxInSubtree) { this.maxFromRoot = maxFromRoot; this.maxInSubtree = maxInSubtree; } } public int longestConsecutive(TreeNode root) { return helper(root).maxInSubtree; } private ResultType helper(TreeNode root) { if (root == null) { return new ResultType(0, 0); } ResultType left = helper(root.left); ResultType right = helper(root.right); ResultType result = new ResultType(1, 0); if (root.left != null && root.val + 1 == root.left.val) { result.maxFromRoot = left.maxFromRoot + 1; } if (root.right != null && root.val + 1 == root.right.val) { result.maxFromRoot = right.maxFromRoot + 1; } result.maxInSubtree = Math.max(result.maxFromRoot, Math.max(left.maxInSubtree, right.maxInSubtree)); return result; } }
2017-01-30
376.二叉树的路径和Binary tree Path Sum
要求:找出所有从上到下的和等于target的路径
思路:(看思路自写AC)traverse,用子集的方法,然后判断if (sum == target),把当前path加入到paths
/** * 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 * @param target an integer * @return all valid paths */ public List<List<Integer>> binaryTreePathSum(TreeNode root, int target) { List<List<Integer>> paths = new ArrayList<>(); if (root == null) { return paths; } //先把根放进去 List<Integer> path = new ArrayList<>(); path.add(root.val); helper(root, root.val, target, path, paths); return paths; } private void helper(TreeNode root, int sum, int target, List<Integer> path, List<List<Integer>> paths) { if (root.left == null && root.right == null) { if (sum == target) { paths.add(new ArrayList<Integer>(path)); } return; } if (root.left != null) { path.add(root.left.val); helper(root.left, sum + root.left.val, target, path, paths); path.remove(path.size() - 1); } if (root.right != null) { path.add(root.right.val); helper(root.right, sum + root.right.val, target, path, paths); path.remove(path.size() - 1); } } }
2017-01-30
93.平衡二叉树Balanced Binary Tree
要求:判断左右子树的深度是否相差1以内
思路:(答案)divide conquer + ResultType,helper返回的是isBalanced和maxDepth
warn:递归中不仅要判断当前根节点是否平衡,还要判断它的两个子节点是否平衡:if (! left.isBalanced || ! right.isBalanced),只要两边有一个不平衡就整个不平衡
2017-10-12
/** * 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 this Binary tree is Balanced, or false. */ private class ResultType { public boolean isBalanced; public int maxDepth; public ResultType(boolean isBalanced, int maxDepth) { this.isBalanced = isBalanced; this.maxDepth = maxDepth; } } public boolean isBalanced(TreeNode root) { return helper(root).isBalanced; } private ResultType helper(TreeNode root) { if (root == null) { return new ResultType(true, 0); } ResultType left = helper(root.left); ResultType right = helper(root.right); if (Math.abs(left.maxDepth - right.maxDepth) > 1) { return new ResultType(false, -1); } if (!left.isBalanced || !right.isBalanced) { return new ResultType(false, -1); } int currDepth = Math.max(left.maxDepth, right.maxDepth) + 1; return new ResultType(true, currDepth); } }
2017-01-30
4 - 宽度优先搜索
69.二叉树的层级遍历
要求:如题
思路:(自写但没有bugfree)就是bfs的模板,分层表示要加size = queue.size()
/** * 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: Level order a list of lists of integer */ public ArrayList<ArrayList<Integer>> levelOrder(TreeNode root) { ArrayList<ArrayList<Integer>> results = new ArrayList<>(); if (root == null) { return results; } Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); //bfs while (!queue.isEmpty()) { ArrayList<Integer> level = new ArrayList<>(); //分层显示 int size = queue.size(); for (int i = 0; i < size; i++) { //保存头节点 TreeNode head = queue.poll(); level.add(head.val); if (head.left != null) { queue.offer(head.left); } if (head.right != null) { queue.offer(head.right); } } results.add(level); } return results; } }
2017-02-06
242.将二叉树按照层级转化成链表
要求:如题
思路:自写AC 与69类似,加入了链表的基本操作
public class Solution { /** * @param root the root of binary tree * @return a lists of linked list */ public List<ListNode> binaryTreeToLists(TreeNode root) { // Write your code here List<ListNode> results = new ArrayList<>(); if (root == null) { return results; } Queue<TreeNode> queue = new LinkedList<>(); queue.offer(root); while (!queue.isEmpty()) { ListNode dummy = new ListNode(0); ListNode node = dummy; int size = queue.size(); for (int i = 0; i < size; i++) { TreeNode head = queue.poll(); ListNode curr = new ListNode(head.val); node.next = curr; node = node.next; if (head.left != null) { queue.offer(head.left); } if (head.right != null) { queue.offer(head.right); } } results.add(dummy.next); } return results; } }
2017-10-13
7.二叉树的序列化和反序列化
要求:如题
思路:序列化就直接bfs且加入时包括中间出现的null(#)并另外去除尾部的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; * } * } */ 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 "{}"; } ArrayList<TreeNode> list = new ArrayList<>(); list.add(root); //bfs for (int i = 0; i < list.size(); i++) { TreeNode node = list.get(i); if (node == null) { continue; } list.add(node.left); list.add(node.right); } while (list.get(list.size() - 1) == null) { list.remove(list.size() - 1); } StringBuilder res = new StringBuilder(); //把头节点加入 res.append("{" + root.val); for (int i = 1; i < list.size(); i++) { if (list.get(i) != null) { res.append("," + list.get(i).val); } else { res.append(",#"); } } res.append("}"); return res.toString(); } /** * 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.equals("{}")) { return null; } //排除, {}并拆分 String[] val = data.substring(1, data.length() - 1).split(","); ArrayList<TreeNode> list = new ArrayList<>(); TreeNode root = new TreeNode(Integer.parseInt(val[0])); list.add(root); //指针标识当前操作哪个节点,以及判断是左还是右儿子 int index = 0; boolean isLeftChild = true; for (int i = 1; i < val.length; i++) { if (!val[i].equals("#")) { TreeNode node = new TreeNode(Integer.parseInt(val[i])); //非空的才放进去list中 list.add(node); if (isLeftChild) { list.get(index).left = node; } else { list.get(index).right = node; } } //到右儿子就操作下一个节点 if (!isLeftChild) { index++; } //操作完左儿子变右儿子 isLeftChild = !isLeftChild; } return root; } }
2017-02-06
178.图是否是树
要求:如题,图用二维数组(邻接表)表示,二维数组有两列m(边数)行,数组的每个点就是图的点,同一行的点代表是neighbor
思路:图是树有两个条件:1.边数为点数 - 1;2.能连通
warn:构造无向图时一定要把双向都加入,所以在bfs时用另一个set去除已经加入过queue的neighbor
public class Solution { /** * @param n an integer * @param edges a list of undirected edges * @return true if it's a valid tree, or false */ public boolean validTree(int n, int[][] edges) { if (n == 0) { return false; } //1.树的边数应为点数 - 1 if (edges.length != n - 1) { return false; } //用邻接表定义图 Map<Integer, Set<Integer>> graph = initializeGraph(n, edges); //2.从一个点出发能跑通 Queue<Integer> queue = new LinkedList<>(); Set<Integer> set = new HashSet<>(); queue.offer(0); //排除相同点进入队列 set.add(0); int visited = 0; while (!queue.isEmpty()) { int node = queue.poll(); visited++; for (int neighbor : graph.get(node)) { if (set.contains(neighbor)) { continue; } queue.offer(neighbor); set.add(neighbor); } } return visited == n; } private Map<Integer, Set<Integer>> initializeGraph(int n, int[][] edges) { //初始化HashMap Map<Integer, Set<Integer>> graph = new HashMap<>(); for (int i = 0; i < n; i++) { graph.put(i, new HashSet<Integer>()); } //把边放入 for (int i = 0; i < edges.length; i++) { int u = edges[i][0]; int v = edges[i][1]; graph.get(u).add(v); graph.get(v).add(u); } return graph; } }
2017-02-07
137.克隆图
要求:把图的点和边都复制
思路:1.bfs所有点,放进ArrayList中(相当于获得所有节点的引用)2.deep copy,真正new一个新的一样的节点,并把新老节点用map对应 3.运用老节点帮新节点找到邻居
warn:理解deep copy,分清楚创建的是新对象还是新引用
/** * Definition for undirected graph. * class UndirectedGraphNode { * int label; * ArrayList<UndirectedGraphNode> neighbors; * UndirectedGraphNode(int x) { label = x; neighbors = new ArrayList<UndirectedGraphNode>(); } * }; */ public class Solution { /** * @param node: A undirected graph node * @return: A undirected graph node */ public UndirectedGraphNode cloneGraph(UndirectedGraphNode node) { if (node == null) { return null; } //bfs来获得所有点 ArrayList<UndirectedGraphNode> nodes = getNodes(node); //deep copy,并建立新老node的对应关系 Map<UndirectedGraphNode, UndirectedGraphNode> map = new HashMap<>(); for (UndirectedGraphNode n : nodes) { map.put(n, new UndirectedGraphNode(n.label)); } //copy neighbor for (UndirectedGraphNode n : nodes) { UndirectedGraphNode newNode = map.get(n); for (UndirectedGraphNode neighbor : n.neighbors) { UndirectedGraphNode newNeighbor = map.get(neighbor); newNode.neighbors.add(newNeighbor); } } return map.get(node); } //标准对于图的bfs private ArrayList<UndirectedGraphNode> getNodes(UndirectedGraphNode node) { Queue<UndirectedGraphNode> queue = new LinkedList<>(); Set<UndirectedGraphNode> set = new HashSet<>(); queue.offer(node); set.add(node); while (!queue.isEmpty()) { UndirectedGraphNode head = queue.poll(); for (UndirectedGraphNode neighbor : head.neighbors) { if (!set.contains(neighbor)) { set.add(neighbor); queue.offer(neighbor); } } } //把set deep copy到ArrayList中 return new ArrayList<UndirectedGraphNode>(set); } }
2017-02-07
618.Search Graph Nodes
要求:找出value为target的最近的点
思路:bfs找出满足条件即可
/** * Definition for graph node. * class UndirectedGraphNode { * int label; * ArrayList<UndirectedGraphNode> neighbors; * UndirectedGraphNode(int x) { * label = x; neighbors = new ArrayList<UndirectedGraphNode>(); * } * }; */ public class Solution { /** * @param graph a list of Undirected graph node * @param values a hash mapping, <UndirectedGraphNode, (int)value> * @param node an Undirected graph node * @param target an integer * @return the a node */ public UndirectedGraphNode searchNode(ArrayList<UndirectedGraphNode> graph, Map<UndirectedGraphNode, Integer> values, UndirectedGraphNode node, int target) { Queue<UndirectedGraphNode> queue = new LinkedList<>(); Set<UndirectedGraphNode> set = new HashSet<>(); queue.offer(node); set.add(node); while (!queue.isEmpty()) { UndirectedGraphNode head = queue.poll(); if (values.get(head) == target) { return head; } for (UndirectedGraphNode neighbor : head.neighbors) { if (!set.contains(neighbor)) { set.add(neighbor); queue.offer(neighbor); } } } return null; } }
2017-02-08
433.岛屿的个数
要求:矩阵中1代表岛屿,0代表海洋,相邻的1代表同一个岛,求岛的个数
思路:用bfs把1附近的1都标记为0
warn:判断越界,写inBound函数
public class Solution { /** * @param grid a boolean 2D matrix * @return an integer */ class Node { int x; int y; public Node(int x, int y) { this.x = x; this.y = y; } } public int numIslands(boolean[][] grid) { if (grid == null || grid.length == 0 || grid[0].length == 0) { return 0; } int row = grid.length; int col = grid[0].length; int island = 0; for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { if (grid[i][j]) { isNeighbor(grid, i, j); island++; } } } return island; } private void isNeighbor(boolean[][] grid, int x, int y) { //坐标变换数组 int[] deltaX = {0, 1, -1, 0}; int[] deltaY = {1, 0, 0, -1}; Queue<Node> queue = new LinkedList<>(); queue.offer(new Node(x, y)); //遍历过的标记为false grid[x][y] = false; //bfs访问四个方向 while (!queue.isEmpty()) { Node head = queue.poll(); for (int i = 0; i < 4; i++) { Node neighbor = new Node(head.x + deltaX[i], head.y + deltaY[i]); //判断越界 if (!inBound(neighbor, grid)) { continue; } if (grid[neighbor.x][neighbor.y]) { grid[neighbor.x][neighbor.y] = false; queue.offer(neighbor); } } } } private boolean inBound(Node node, boolean[][] grid) { int row = grid.length; int col = grid[0].length; return node.x >= 0 && node.x < row && node.y >= 0 && node.y < col; } }
2017-02-08
二刷 bfs时忘记把一开始的head放进去后,把它变成false,while循环中忘记每次更新head
2017-10-13
598.Zombie in Matrix
要求:0代表人,1代表僵尸,2代表墙,求多少天能够把人都变成僵尸
思路:1.数人头和把僵尸放到队列中 2.完整的三层循环的分层bfs(背),每一层就是经过一天 3.人数为0时返回天数
public class Solution { /** * @param grid a 2D integer grid * @return an integer */ class Node { int x, y; public Node(int x, int y) { this.x = x; this.y = y; } } public int PEOPLE = 0; public int ZOMBIE = 1; public int[] deltaX = {0, 1, -1, 0}; public int[] deltaY = {1, 0, 0, -1}; public int zombie(int[][] grid) { if (grid == null || grid.length == 0 || grid[0].length == 0) { return 0; } int people = 0; Queue<Node> queue = new LinkedList<>(); int row = grid.length; int col = grid[0].length; //计算人的数量并把一开始的僵尸位置放到队列中 for (int i = 0; i < row; i++) { for (int j = 0; j < col; j++) { if (grid[i][j] == PEOPLE) { people++; } else if (grid[i][j] == ZOMBIE) { queue.offer(new Node(i, j)); } } } if (people == 0) { return 0; } int days = 0; //完整的bfs while (!queue.isEmpty()) { //分层 int size = queue.size(); days++; for (int i = 0; i < size; i++) { Node head = queue.poll(); for (int j = 0; j < 4; j++) { Node neighbor = new Node(head.x + deltaX[j], head.y + deltaY[j]); //判断是否越界以及是否为人 if (!isPeople(neighbor, grid)) { continue; } grid[neighbor.x][neighbor.y] = ZOMBIE; people--; if (people == 0) { return days; } queue.offer(neighbor); } } } return -1; } private boolean isPeople(Node node, int[][] grid) { int row = grid.length; int col = grid[0].length; if (node.x < 0 || node.x >= row) { return false; } if (node.y < 0 || node.y >= col) { return false; } return grid[node.x][node.y] == PEOPLE; } }
2017-02-08
611.Knight Shortest Path
要求:骑士跳日字,计算从source位置到destination位置的步数
思路:(自写AC)1.坐标变换数组 2.完整的三层bfs 3.inBound方法
warn:不能用set标记已走过的地方,会超出内存范围,所以走过的地方标为BARRIER
/** * Definition for a point. * public class Point { * public int x, y; * public Point() { x = 0; y = 0; } * public Point(int a, int b) { x = a; y = b; } * } */ public class Solution { /** * @param grid a chessboard included 0 (false) and 1 (true) * @param source, destination a point * @return the shortest path */ public boolean EMPTY = false; public boolean BARRIER = true; public int[] deltaX = {1, 1, 2, 2, -1, -1, -2, -2}; public int[] deltaY = {2, -2, 1, -1, 2, -2, 1, -1}; public int shortestPath(boolean[][] grid, Point source, Point destination) { if (grid == null || grid.length == 0 || grid[0].length == 0) { return -1; } if (source.x == destination.x && source.y == destination.y) { return 0; } int rows = grid.length; int cols = grid[0].length; Queue<Point> queue = new LinkedList<>(); queue.offer(source); //标记已走过 grid[source.x][source.y] = BARRIER; //bfs int length = 0; while (!queue.isEmpty()) { length++; //分层 int size = queue.size(); for (int i = 0; i < size; i++) { Point head = queue.poll(); //八个方向跳 for (int j = 0; j < 8; j++) { Point next = new Point(head.x + deltaX[j], head.y + deltaY[j]); if (next.x == destination.x && next.y == destination.y) { return length; } if (isBarrier(next, grid)) { continue; } queue.offer(next); grid[next.x][next.y] = BARRIER; } } } return -1; } private boolean isBarrier(Point p, boolean[][] grid) { int rows = grid.length; int cols = grid[0].length; if (p.x < 0 || p.x >= rows) { return true; } if (p.y < 0 || p.y >= cols) { return true; } return grid[p.x][p.y] == BARRIER; } }
2017-02-09
573.Build Post Office II
要求:0代表office能建的位置,1代表房,2代表墙,求office能够到达所有房的最短路径
思路:求每间房到所有0点的距离,当求每个点时就可以把对应房间的距离加起来
class Coordinate { int x, y; public Coordinate(int x, int y) { this.x = x; this.y = y; } } public class Solution { public int EMPTY = 0; public int HOUSE = 1; public int WALL = 2; public int[][] grid; public int n, m; public int[] deltaX = {0, 1, -1, 0}; public int[] deltaY = {1, 0, 0, -1}; private List<Coordinate> getCoordinates(int type) { List<Coordinate> coordinates = new ArrayList<>(); for (int i = 0; i < n; i++) { for (int j = 0; j < m; j++) { if (grid[i][j] == type) { coordinates.add(new Coordinate(i, j)); } } } return coordinates; } private void setGrid(int[][] grid) { n = grid.length; m = grid[0].length; this.grid = grid; } private boolean inBound(Coordinate coor) { if (coor.x < 0 || coor.x >= n) { return false; } if (coor.y < 0 || coor.y >= m) { return false; } return grid[coor.x][coor.y] == EMPTY; } /** * @param grid a 2D grid * @return an integer */ public int shortestDistance(int[][] grid) { if (grid == null || grid.length == 0 || grid[0].length == 0) { return -1; } // set n, m, grid setGrid(grid); List<Coordinate> houses = getCoordinates(HOUSE); int[][] distanceSum = new int[n][m];; int[][] visitedTimes = new int[n][m];; for (Coordinate house : houses) { bfs(house, distanceSum, visitedTimes); } int shortest = Integer.MAX_VALUE; List<Coordinate> empties = getCoordinates(EMPTY); for (Coordinate empty : empties) { if (visitedTimes[empty.x][empty.y] != houses.size()) { continue; } shortest = Math.min(shortest, distanceSum[empty.x][empty.y]); } if (shortest == Integer.MAX_VALUE) { return -1; } return shortest; } private void bfs(Coordinate start, int[][] distanceSum, int[][] visitedTimes) { Queue<Coordinate> queue = new LinkedList<>(); boolean[][] hash = new boolean[n][m]; queue.offer(start); hash[start.x][start.y] = true; int steps = 0; while (!queue.isEmpty()) { steps++; int size = queue.size(); for (int temp = 0; temp < size; temp++) { Coordinate coor = queue.poll(); for (int i = 0; i < 4; i++) { Coordinate adj = new Coordinate( coor.x + deltaX[i], coor.y + deltaY[i] ); if (!inBound(adj)) { continue; } if (hash[adj.x][adj.y]) { continue; } queue.offer(adj); hash[adj.x][adj.y] = true; distanceSum[adj.x][adj.y] += steps; visitedTimes[adj.x][adj.y]++; } // direction } // for temp } // while } }
(答案代码未仔细看,类似于上两题)
2017-02-09
127.拓扑排序
要求:如题,从入度0的出发,找出其中一个解
思路:(自写AC)1. 算每个点的入度 2. bfs每个点,对应的neighbor的入度 - 1,到入度为0时进入队列
/** * Definition for Directed graph. * class DirectedGraphNode { * int label; * ArrayList<DirectedGraphNode> neighbors; * DirectedGraphNode(int x) { label = x; neighbors = new ArrayList<DirectedGraphNode>(); } * }; */ public class Solution { /** * @param graph: A list of Directed graph node * @return: Any topological order for the given graph. */ public ArrayList<DirectedGraphNode> topSort(ArrayList<DirectedGraphNode> graph) { ArrayList<DirectedGraphNode> order = new ArrayList<>(); if (graph == null) { return order; } // 1. count indegree Map<DirectedGraphNode, Integer> indegree = getIndegree(graph); // 2. bfs Queue<DirectedGraphNode> queue = new LinkedList<>(); // find first node for (DirectedGraphNode node : graph) { if (indegree.get(node) == 0) { queue.offer(node); order.add(node); } } while (!queue.isEmpty()) { DirectedGraphNode head = queue.poll(); for (DirectedGraphNode neighbor : head.neighbors) { indegree.put(neighbor, indegree.get(neighbor) - 1); if (indegree.get(neighbor) == 0) { queue.offer(neighbor); order.add(neighbor); } } } return order; } private Map<DirectedGraphNode, Integer> getIndegree(ArrayList<DirectedGraphNode> graph) { Map<DirectedGraphNode, Integer> map = new HashMap<>(); // 初始化 for (DirectedGraphNode node : graph) { map.put(node, 0); } for (DirectedGraphNode node : graph) { for (DirectedGraphNode neighbor : node.neighbors) { map.put(neighbor, map.get(neighbor) + 1); } } return map; } }
2017-02-09
616.Course Schedule II
要求:把课程拓扑排序,给出的是二维数组表示的课程之间的关系
思路:(自写AC!) 1. 生成图 2. 算入度 3.bfs(与普通拓扑类似)
warn:注意特殊情况,判断是否有解!(index == numCourses时有解)
自写:
public class Solution { /** * @param numCourses a total of n courses * @param prerequisites a list of prerequisite pairs * @return the course order */ public int[] findOrder(int numCourses, int[][] prerequisites) { int[] order = new int[numCourses]; if (numCourses == 0) { return order; } //exceptional case if (numCourses != 0 && (prerequisites == null || prerequisites.length == 0 || prerequisites[0].length == 0)) { for (int i = 0; i < numCourses; i++) { order[i] = i; } return order; } // 1. initialize graph Map<Integer, ArrayList<Integer>> graph = initializeGraph(numCourses, prerequisites); // 2. count indegree Map<Integer, Integer> indegree = getIndegree(graph, numCourses); // 3. bfs int index = 0; Queue<Integer> queue = new LinkedList<>(); // find start for (int i = 0; i < numCourses; i++) { if (indegree.get(i) == 0) { queue.offer(i); order[index++] = i; } } while (!queue.isEmpty()) { int head = queue.poll(); for (int neighbor : graph.get(head)) { indegree.put(neighbor, indegree.get(neighbor) - 1); if (indegree.get(neighbor) == 0) { queue.offer(neighbor); order[index++] = neighbor; } } } if (index == numCourses) { return order; } return new int[0]; } private Map<Integer, ArrayList<Integer>> initializeGraph(int numCourses, int[][] prerequisites) { Map<Integer, ArrayList<Integer>> graph = new HashMap<>(); for (int i = 0; i < numCourses; i++) { graph.put(i, new ArrayList<Integer>()); } for (int i = 0; i < prerequisites.length; i++) { int u = prerequisites[i][0]; int v = prerequisites[i][1]; graph.get(v).add(u); } return graph; } private Map<Integer, Integer> getIndegree(Map<Integer, ArrayList<Integer>> graph, int numCourses) { Map<Integer, Integer> indegree = new HashMap<>(); for (int i = 0; i < numCourses; i++) { indegree.put(i, 0); } for (int i = 0; i < numCourses; i++) { for (Integer neighbor : graph.get(i)) { indegree.put(neighbor, indegree.get(neighbor) + 1); } } return indegree; } }
答案写法:
public class Solution { /** * @param numCourses a total of n courses * @param prerequisites a list of prerequisite pairs * @return the course order */ public int[] findOrder(int numCourses, int[][] prerequisites) { // Write your code here List[] edges = new ArrayList[numCourses]; int[] degree = new int[numCourses]; for (int i = 0;i < numCourses; i++) edges[i] = new ArrayList<Integer>(); for (int i = 0; i < prerequisites.length; i++) { degree[prerequisites[i][0]] ++ ; edges[prerequisites[i][1]].add(prerequisites[i][0]); } Queue queue = new LinkedList(); for(int i = 0; i < degree.length; i++){ if (degree[i] == 0) { queue.add(i); } } int count = 0; int[] order = new int[numCourses]; while(!queue.isEmpty()){ int course = (int)queue.poll(); order[count] = course; count ++; int n = edges[course].size(); for(int i = n - 1; i >= 0 ; i--){ int pointer = (int)edges[course].get(i); degree[pointer]--; if (degree[pointer] == 0) { queue.add(pointer); } } } if (count == numCourses) return order; return new int[0]; } }
2017-02-09
还缺一道hard题
5 - 深度优先搜索
18.subsets II
要求:集合中有重复元素
思路:dfs,在I的前提下加入判断条件i != 0 && nums[i] == nums[i - 1] && i != startIndex时continue
public ArrayList<ArrayList<Integer>> subsetsWithDup(int[] nums) { ArrayList<ArrayList<Integer>> results = new ArrayList<>(); if (nums == null || nums.length == 0) { return results; } Arrays.sort(nums); dfsHelper(nums, 0, new ArrayList<Integer>(), results); return results; } private void dfsHelper(int[] nums, int startIndex, ArrayList<Integer> subset, ArrayList<ArrayList<Integer>> results) { //deep copy results.add(new ArrayList<Integer>(subset)); for (int i = startIndex; i < nums.length; i++) { if (i != 0 && nums[i] == nums[i - 1] && i > startIndex) { continue; } subset.add(nums[i]); dfsHelper(nums, i + 1, subset, results); subset.remove(subset.size() - 1); } }
2017-02-10
135.数字组合 combination sum
要求:组合中可能存在重复数,每个数能用多次,求出能相加等于target的集合
思路:dfs,注意条件判断就好,removeDuplicate方法
public class Solution { /** * @param candidates: A list of integers * @param target:An integer * @return: A list of lists of integers */ public List<List<Integer>> combinationSum(int[] candidates, int target) { List<List<Integer>> results = new ArrayList<>(); if (candidates == null || candidates.length == 0) { return results; } int[] nums = removeDuplicates(candidates); dfsHelper(nums, target, 0, new ArrayList<Integer>(), results); return results; } private void dfsHelper(int[] nums, int target, int startIndex, List<Integer> subset, List<List<Integer>> results) { if (target == 0) { results.add(new ArrayList<Integer>(subset)); } for (int i = startIndex; i < nums.length; i++) { if (nums[i] > target) { break; } subset.add(nums[i]); dfsHelper(nums, target - nums[i], i, subset, results); subset.remove(subset.size() - 1); } } private int[] removeDuplicates(int[] candidates) { Arrays.sort(candidates); int index = 0; for (int i = 1; i < candidates.length; i++) { if (candidates[i] != candidates[index]) { candidates[++index] = candidates[i]; } } int[] nums = new int[index + 1]; for (int i = 0; i < index + 1; i++) { nums[i] = candidates[i]; } return nums; } }
2017-02-10
153.数字组合 combination sum II
要求:组合中有重复数,每个数只能用一次
思路:与上题区别是这题不能直接去重,因为相同的数也要加入到组合中,但需要像subset II那样排除相同的数调换顺序的重复情况
public List<List<Integer>> combinationSum2(int[] num, int target) { List<List<Integer>> combinations = new ArrayList<>(); if (num == null || num.length == 0) { return combinations; } Arrays.sort(num); dfsHelper(num, target, 0, new ArrayList<Integer>(), combinations); return combinations; } private void dfsHelper(int[] num, int target, int startIndex, List<Integer> combination, List<List<Integer>> combinations) { if (target == 0) { combinations.add(new ArrayList<Integer>(combination)); return; } for (int i = startIndex; i < num.length; i++) { if (i != 0 && num[i] == num[i - 1] && i != startIndex) { continue; } if (target < num[i]) { break; } combination.add(num[i]); dfsHelper(num, target - num[i], i + 1, combination, combinations); combination.remove(combination.size() - 1); } }
2017-02-10
136.分隔回文串 Palindrome Partitioning
要求:找出所有把字符串s切割成的全部子字符串都是回文串的方法
思路:仍然是dfs模板,每次加入的是从startIndex切割到当前i的子字符串,并判断其是否回文串(加入isPalindrome方法),递归的出口是切完(startIndex == s.length())
public List<List<String>> partition(String s) { List<List<String>> results = new ArrayList<>(); if (s == null || s.length() == 0) { return results; } dfsHelper(s, 0, new ArrayList<String>(), results); return results; } private void dfsHelper(String s, int startIndex, List<String> partition, List<List<String>> results) { //切完了 if (startIndex == s.length()) { results.add(new ArrayList<String>(partition)); return; } for (int i = startIndex; i < s.length(); i++) { //切从startIndex到i的一段 String ss = s.substring(startIndex, i + 1); if (!isPalindrome(ss)) { continue; } partition.add(ss); dfsHelper(s, i + 1, partition, results); partition.remove(partition.size() - 1); } } private boolean isPalindrome(String s) { for (int i = 0, j = s.length() - 1; i < j; i++, j--) { if (s.charAt(i) != s.charAt(j)) { return false; } } return true; }
2017-02-10
15.全排序
要求:如题
思路:dfs,用visited数组标记是否访问过
warn:其实nums == null和nums.length == 0不一样
class Solution { /** * @param nums: A list of integers. * @return: A list of permutations. */ public List<List<Integer>> permute(int[] nums) { List<List<Integer>> lists = new ArrayList<>(); if (nums == null) { return lists; } if (nums.length == 0) { lists.add(new ArrayList<Integer>()); return lists; } //sign if it has been visited int[] visited = new int[nums.length]; helper(nums, visited, new ArrayList<Integer>(), lists); return lists; } private void helper(int[] nums, int [] visited, List<Integer> list, List<List<Integer>> lists) { if (list.size() == nums.length) { lists.add(new ArrayList<Integer>(list)); } for (int i = 0; i < nums.length; i++) { if (visited[i] == 1) { continue; } visited[i] = 1; list.add(nums[i]); helper(nums, visited, list, lists); list.remove(list.size() - 1); visited[i] = 0; } } }
2017-02-13
16.带重复元素的排列
要求:如题
思路:与上题一样,并加入了subset II的条件
tips:判断时要加入visited[i - 1] == 0才continue
warn:别忘记排序!!
class Solution { /** * @param nums: A list of integers. * @return: A list of unique permutations. */ public List<List<Integer>> permuteUnique(int[] nums) { List<List<Integer>> lists = new ArrayList<>(); List<Integer> list = new ArrayList<>(); if (nums == null) { return lists; } if (nums.length == 0) { lists.add(list); return lists; } Arrays.sort(nums); int[] visited = new int[nums.length]; helper(nums, visited, list, lists); return lists; } private void helper(int[] nums, int[] visited, List<Integer> list, List<List<Integer>> lists) { if (list.size() == nums.length) { lists.add(new ArrayList<Integer>(list)); } for (int i = 0; i < nums.length; i++) { if (visited[i] == 1) { continue; } if (i != 0 && nums[i] == nums[i - 1] && visited[i - 1] == 0) { continue; } visited[i] = 1; list.add(nums[i]); helper(nums, visited, list, lists); list.remove(list.size() - 1); visited[i] = 0; } } }
2017-02-13
6 - 链表与数组
604.womdow sum
要求:把k长度窗口里的和都算出
思路:(自写AC)two pointers
warn:边界判断
public int[] winSum(int[] nums, int k) { if (nums == null || k == 0 || nums.length < k) { return new int[0]; } int[] sums = new int[nums.length - k + 1]; int sum = 0; for (int index = 0; index < k; index++) { sum += nums[index]; } sums[0] = sum; int i = 0; int j = k - 1; while (j < nums.length - 1) { sum = sum - nums[i++] + nums[++j]; sums[i] = sum; } return sums; }
2017-02-14
138.subarray sum
要求:找出和为0的子数组
思路:(自写AC)prefixSum,找出前缀和相同的项就代表此段子数组为0
写法1:双重循环找:
public ArrayList<Integer> subarraySum(int[] nums) { ArrayList<Integer> result = new ArrayList<Integer>(); if (nums == null || nums.length == 0) { return result; } int[] prefixSum = new int[nums.length]; int sum = 0; for (int i = 0; i < nums.length; i++) { sum += nums[i]; prefixSum[i] = sum; } if (prefixSum[nums.length - 1] == 0) { result.add(0); result.add(nums.length - 1); return result; } for (int i = 0; i < nums.length; i++) { for (int j = i + 1; j < nums.length; j++) { if (prefixSum[i] == prefixSum[j]) { result.add(i + 1); result.add(j); return result; } } } return result; }
写法2:HashMap找(答案)
public ArrayList<Integer> subarraySum(int[] nums) { // write your code here int len = nums.length; ArrayList<Integer> ans = new ArrayList<Integer>(); HashMap<Integer, Integer> map = new HashMap<Integer, Integer>(); map.put(0, -1); int sum = 0; for (int i = 0; i < len; i++) { sum += nums[i]; if (map.containsKey(sum)) { ans.add(map.get(sum) + 1); ans.add(i); return ans; } map.put(sum, i); } return ans; }
写法3:二分查找(略)
2017-02-14
41.maximum subarray
要求:找出最大子数组
思路:(自写一次AC)prefixSum数组
warn:注意index从0开始时的情况
public int maxSubArray(int[] nums) { if (nums == null || nums.length == 0) { return 0; } if (nums.length == 1) { return nums[0]; } //prefixSum int sum = 0; int[] prefixSum = new int[nums.length]; int result = Integer.MIN_VALUE; for (int i = 0; i < nums.length; i++) { sum += nums[i]; prefixSum[i] = sum; result = Math.max(prefixSum[i], result); } for (int i = 0; i < nums.length; i++) { for (int j = i + 1; j < nums.length; j++) { result = Math.max(prefixSum[j] - prefixSum[i], result); } } return result; }
2017-02-14
139.subarray sum cloest
要求:找出和最接近0的子数组
思路:(自写)prefixSum,每次更新一次,但用两次for循环的话数据量大时会TLE
public int[] subarraySumClosest(int[] nums) { if (nums == null || nums.length == 0) { return new int[0]; } int[] res = new int[2]; int subarraySum = Integer.MAX_VALUE; int sum = 0; int[] prefixSum = new int[nums.length]; for (int i = 0; i < nums.length; i++) { sum += nums[i]; prefixSum[i] = sum; if (subarraySum > Math.abs(prefixSum[i])) { subarraySum = Math.abs(prefixSum[i]); res[0] = 0; res[1] = i; } } for (int i = 0; i < nums.length; i++) { for (int j = i + 1; j < nums.length; j++) { int currSum = Math.abs(prefixSum[j] - prefixSum[i]); if (subarraySum > currSum) { subarraySum = currSum; res[0] = i + 1; res[1] = j; } } } return res; }
答案(优化写法):
class Pair { int sum; int index; public Pair(int s, int i) { sum = s; index = i; } } 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 int[] subarraySumClosest(int[] nums) { int[] res = new int[2]; if (nums == null || nums.length == 0) { return res; } int len = nums.length; if(len == 1) { res[0] = res[1] = 0; return res; } Pair[] sums = new Pair[len+1]; int prev = 0; sums[0] = new Pair(0, 0); for (int i = 1; i <= len; i++) { sums[i] = new Pair(prev + nums[i-1], i); prev = sums[i].sum; } Arrays.sort(sums, new Comparator<Pair>() { public int compare(Pair a, Pair b) { return a.sum - b.sum; } }); int ans = Integer.MAX_VALUE; for (int i = 1; i <= len; i++) { if (ans > sums[i].sum - sums[i-1].sum) { ans = sums[i].sum - sums[i-1].sum; int[] temp = new int[]{sums[i].index - 1, sums[i - 1].index - 1}; Arrays.sort(temp); res[0] = temp[0] + 1; res[1] = temp[1]; } } return res; } }
2017-02-14
165.merge two sorted lists
要求:如题
思路:(自写)dummy node,和sorted array类似
public ListNode mergeTwoLists(ListNode l1, ListNode l2) { ListNode dummy = new ListNode(0); ListNode lastNode = dummy; while (l1 != null && l2 != null) { if (l1.val < l2.val) { lastNode.next = l1; l1 = l1.next; } else { lastNode.next = l2; l2 = l2.next; } lastNode = lastNode.next; } if (l1 != null) { lastNode.next = l1; } if (l2 != null) { lastNode.next = l2; } return dummy.next; }
2017-03-06
599.insert into a cyclic sorted list
要求:如题
思路:(自写但逻辑有错误)根据前后关系找插入的位置
public ListNode insert(ListNode node, int x) { ListNode newNode = new ListNode(x); if (node == null) { newNode.next = newNode; return newNode; } if (node.next == node) { node.next = newNode; newNode.next = node; return newNode; } ListNode currNode = node; ListNode nextNode = node.next; while (currNode.val <= nextNode.val) { if (nextNode.next.val <= nextNode.val) { if (nextNode.val >= x && currNode.val <= x) { currNode.next = newNode; newNode.next = nextNode; break; } else { newNode.next = nextNode.next; nextNode.next = newNode; break; } } else { currNode = nextNode; nextNode = nextNode.next; } } return newNode; }
答案:
public ListNode insert(ListNode node, int x) { if (node == null) { node = new ListNode(x); node.next = node; return node; } //找断点 ListNode curr = node; ListNode prev = null; do { prev = curr; curr = curr.next; if (x <= curr.val && x >= prev.val) { break; } if ((prev.val > curr.val) && (x < curr.val || x > prev.val)) { break; } } while (curr != node); //找到插入位置后连接 ListNode newNode = new ListNode(x); newNode.next = curr; prev.next = newNode; return newNode; }
2017-03-06
102.listed list cycle
要求:判断链表是否有环
思路:(修正答案错误)快慢指针,有环的话会相遇
warn:注意判决条件
public boolean hasCycle(ListNode head) { if (head == null || head.next == null) { return false; } //快慢指针 ListNode slow = head; ListNode fast = head.next; while (slow != fast) { if (slow == null || fast == null || fast.next == null) { return false; } slow = slow.next; fast = fast.next.next; } return true; }
2017-03-06
98.sort list
要求:把链表排序
思路:(答案)分别用归并和快速排序来完成,背
快速:
public ListNode sortList(ListNode head) { if (head == null || head.next == null) { return head; } ListNode mid = findMedian(head); // O(n) ListNode leftDummy = new ListNode(0), leftTail = leftDummy; ListNode rightDummy = new ListNode(0), rightTail = rightDummy; ListNode middleDummy = new ListNode(0), middleTail = middleDummy; while (head != null) { if (head.val < mid.val) { leftTail.next = head; leftTail = head; } else if (head.val > mid.val) { rightTail.next = head; rightTail = head; } else { middleTail.next = head; middleTail = head; } head = head.next; } leftTail.next = null; middleTail.next = null; rightTail.next = null; ListNode left = sortList(leftDummy.next); ListNode right = sortList(rightDummy.next); return concat(left, middleDummy.next, right); } private ListNode findMedian(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 concat(ListNode left, ListNode middle, ListNode right) { ListNode dummy = new ListNode(0), tail = dummy; tail.next = left; tail = getTail(tail); tail.next = middle; tail = getTail(tail); tail.next = right; tail = getTail(tail); return dummy.next; } private ListNode getTail(ListNode head) { if (head == null) { return null; } while (head.next != null) { head = head.next; } return head; }
归并:
private ListNode findMiddle(ListNode head) { ListNode slow = head, fast = head.next; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; } return slow; } private ListNode merge(ListNode head1, ListNode head2) { ListNode dummy = new ListNode(0); ListNode tail = dummy; while (head1 != null && head2 != null) { if (head1.val < head2.val) { tail.next = head1; head1 = head1.next; } else { tail.next = head2; head2 = head2.next; } tail = tail.next; } if (head1 != null) { tail.next = head1; } else { tail.next = head2; } return dummy.next; } 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); }
2017-03-07
105.copy list with random pointer
要求:深度复制带有随机指针的链表
思路:(看视频后自写)分三步:1、用next来保存新老节点的对应关系,形成1->1'->2->2'->3->3'的结构 2、复制random指针 3、拆分并各自连回去
warn:注意边界条件!
public RandomListNode copyRandomList(RandomListNode head) { if (head == null) { return null; } //three steps copyNext(head); copyRandom(head); return spiltList(head); } private void copyNext(RandomListNode head) { while (head != null) { RandomListNode newNode = new RandomListNode(head.label); newNode.next = head.next; head.next = newNode; head = head.next.next; } } private void copyRandom(RandomListNode head) { while (head != null) { if (head.random != null) { head.next.random = head.random.next; } head = head.next.next; } } private RandomListNode spiltList(RandomListNode head) { RandomListNode newHead = head.next; while (head != null) { RandomListNode temp = head.next; head.next = temp.next; head = head.next; if (temp.next != null) { temp.next = temp.next.next; } } return newHead; }
2017-03-08
5.kth largest element(for 65)
要求:如题
思路:(小视频答案)quick select算法,思路和快速排序类似,找标杆把数排成两边
warn:注意每一次递归时候的取值
public int kthLargestElement(int k, int[] nums) { if (nums == null || nums.length == 0) { return -1; } return quickSelect(nums, 0, nums.length - 1, k); } private int quickSelect(int[] nums, int start, int end, int k) { if (start == end) { return nums[start]; } int i = start, j = end; int pivot = nums[(i + j) / 2]; while (i <= j) { while (i <= j && nums[i] > pivot) { i++; } while (i <= j && nums[j] < pivot) { j--; } if (i <= j) { int temp = nums[i]; nums[i] = nums[j]; nums[j] = temp; i++; j--; } } //三种情况,左中右,i和j中间有可能还有一个数刚好满足 if (start + k - 1 <= j) { return quickSelect(nums, start, j, k); } if (start + k - 1 >= i) { //右边的时候需要去掉左边,所以不是找第k大了 return quickSelect(nums, i, end, k - (i - start)); } return nums[j + 1]; }
2017-03-09
65.median of two sorted array(hard)
要求:找出两个排序数组的中位数
思路:(看视频后自写)转换成求第k大的数,然后把k = len / 2代入(偶数时需要求len / 2和len / 2 + 1并取平均)时间复杂度推算法,在O(1)时间内,把求第k大的问题简化成第k / 2的问题,也就是对比两个数组k / 2位置上的数,小的那个就把该数组前面的数都丢掉(加入indexA和indexB来实现)
tips:若一个数组不够长取到index + k / 2 - 1个数,则用无穷大代替
warn:当index大于length时,证明第k大个数只存在于另一数组
public double findMedianSortedArrays(int[] A, int[] B) { int len = A.length + B.length; if (len % 2 == 0) { return (findKth(A, 0, B, 0, len / 2) + findKth(A, 0, B, 0, len / 2 + 1)) / 2.0; } return findKth(A, 0, B, 0, len / 2 + 1); } private double findKth(int[] A, int indexA, int[] B, int indexB, int k) { if (indexA >= A.length) { return B[indexB + k - 1]; } if (indexB >= B.length) { return A[indexA + k - 1]; } if (k == 1) { return Math.min(A[indexA], B[indexB]); } //若数组不够长,则赋为无限大 int keyA = Integer.MAX_VALUE; int keyB = Integer.MAX_VALUE; if (indexA + k / 2 - 1 < A.length) { keyA = A[indexA + k / 2 - 1]; } if (indexB + k / 2 - 1 < B.length) { keyB = B[indexB + k / 2 - 1]; } if (keyA < keyB) { return findKth(A, indexA + k / 2, B, indexB, k - k / 2); } else { return findKth(A, indexA, B, indexB + k / 2, k - k / 2); } }
2017-03-09
451.Swap Nodes in Pairs
要求:把链表两两交换
思路:建立哨兵节点dummyNode,每次指针停留在要交换节点的上一节点
warn:注意null判断
2017-09-12
35.Reverse Linked List 翻转链表
要求:如题
思路:创建前向指针(与dummy其实一样)prev,不用交换数字,而是改变链的方向,然后把两个指针逐渐向后移动
2017-09-12
450.Reverse Nodes in k-Group(hard)
要求:链表中每k个翻转一次,最后不满k个的不要翻转
思路:写一个函数,完成以下几点:1. 记录了每次开始翻转的上一个节点(一开始的时候便是dummy node),这样才可以实现拼接 2.数k次,看是否到结尾 3.翻转
翻转过程中需要记录的节点:1. 开始翻转的第一个节点(翻转后变最后一个点,用于拼接后面) 2. 翻转过程的前后指针prev和curt,临时记录curt.next的temp,因为翻转就是令curt.next = prev,也就是指向前一个节点,然后让prev = curt,curt = temp即把指针后移
public class Solution { /* * @param head: a ListNode * @param k: An integer * @return: a ListNode */ public ListNode reverseKGroup(ListNode head, int k) { // write your code here if (head == null || k <= 1) { return head; } ListNode dummy = new ListNode(0); dummy.next = head; head = dummy; while (head.next != null) { //记录每一次开始节点的上一个位置 head = reverseNextK(head, k); } return dummy.next; } private ListNode reverseNextK(ListNode head, int k) { ListNode nk = head; for (int i = 0; i < k; i++) { //数k个,若未数完就出现null,则链表不需要再翻转 if (nk.next == null) { return nk; } nk = nk.next; } //翻转过程 //1. head记录的是此次要翻转段的上一个节点,first记录第一个翻转的点 // prev,curt为翻转时的前后指针 ListNode first = head.next; ListNode prev = null, curt = first; for (int i = 0; i < k; i++) { //保存 ListNode temp = curt.next; //反向 curt.next = prev; //指针移动 prev = curt; curt = temp; } //拼接,第一个翻转的点接下一次需要开始翻转的点,原head接这次翻转最后的点 first.next = curt; head.next = prev; //first是下一次的head return first; } }
2017-09-18
96.链表划分
要求:给定一个单链表和数值x,划分链表使得所有小于x的节点排在大于等于x的节点之前。
思路:重新做两个链表 用两个dummy 分别存小于的部分和大于的部分。
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) { // write your code here if (head == null) { return null; } ListNode leftDummy = new ListNode(0); ListNode rightDummy = new ListNode(0); ListNode left = leftDummy; ListNode right = rightDummy; while (head != null) { if (head.val < x) { left.next = head; left = left.next; } else { right.next = head; right = right.next; } head = head.next; } //记得把最右边的与原先的断开 right.next = null; left.next = rightDummy.next; return leftDummy.next; } }
2017-10-15
547.两数组的交
要求:交中没有重复数
思路:用两个HashSet,第一个Set1把其中一个数组全部放入,Set2放入另一数组时判断自己没有且Set1有才放入,就可以求它们的交,且交中没有重复
public int[] intersection(int[] nums1, int[] nums2) { // write your code here if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { return new int[0]; } Set<Integer> set1 = new HashSet<>(); for (int num : nums1) { if (!set1.contains(num)) { set1.add(num); } } Set<Integer> set2 = new HashSet<>(); for (int num : nums2) { if (set1.contains(num) && !set2.contains(num)) { set2.add(num); } } int[] result = new int[set2.size()]; int i = 0; for (int num : set2) { result[i++] = num; } return result; }
2018-01-17
548.两数组的交II
要求:交中有重复数,即两数组中若有相同的重复数也放进交中
思路:用HashMap记录其中第一个数组的数和出现次数,用List放入另一数组的数,并在放入前判断Map中是否有该数,且出现次数大于0,放入后把Map中的次数减1
public int[] intersection(int[] nums1, int[] nums2) { // write your code here if (nums1 == null || nums1.length == 0 || nums2 == null || nums2.length == 0) { return new int[0]; } Map<Integer, Integer> map = new HashMap<>(); for (int num : nums1) { if (map.containsKey(num)) { map.put(num, map.get(num) + 1); } else { map.put(num, 1); } } List<Integer> list = new ArrayList<>(); for (int num : nums2) { if (map.containsKey(num) && map.get(num) > 0) { list.add(num); map.put(num, map.get(num) - 1); } } int[] result = new int[list.size()]; for (int i = 0; i < list.size(); i++) { result[i] = list.get(i); } return result; }
2018-01-17
6.合并排序数组
要求:合并排序数组变成一个新数组
思路:两个数组都还有数时就判断谁小谁先进,其中一个数组越界后就一直放另一个数组
public int[] mergeSortedArray(int[] A, int[] B) { int m = A.length; int n = B.length; int[] C = new int[m + n]; int i = 0, j = 0, k = 0; while (i < m && j < n) { if (A[i] < B[j]) { C[k++] = A[i++]; } else { C[k++] = B[j++]; } } while (i < m) { C[k++] = A[i++]; } while (j < n) { C[k++] = B[j++]; } return C; }
2018-01-17
64.合并排序数组II
要求:在A数组上扩展,其中A后面有预留位置
思路:从后面开始保存就不会覆盖到A前面的数
public void mergeSortedArray(int[] A, int m, int[] B, int n) { // write your code here int i = m - 1, j = n - 1, index = m + n - 1; while (i >= 0 && j >= 0) { if (A[i] >= B[j]) { A[index--] = A[i--]; } else { A[index--] = B[j--]; } } while (i >= 0) { A[index--] = A[i--]; } while (j >= 0) { A[index--] = B[j--]; } }
2018-01-17
7 - Two Pointers
607.two sum - data structure
要求:创造一个数据结构,有添加新元素、查找TwoSum的功能
思路:(自写)开两个全局变量list和map,用map的原因是防止有两个重复数加起来满足TwoSum的target,这时候可以把map的value记录key的个数
warn:记得写构造方法!!!
public class TwoSum { private ArrayList<Integer> list; private Map<Integer, Integer> map; public TwoSum() { list = new ArrayList<Integer>(); map = new HashMap<Integer, Integer>(); } public void add(int number) { if (map.containsKey(number)) { map.put(number, map.get(number) + 1); } else { map.put(number, 1); list.add(number); } } // Find if there exists any pair of numbers which sum is equal to the value. public boolean find(int value) { if (list == null || list.size() == 0) { return false; } for (int i = 0; i < list.size(); i++) { int num1 = list.get(i), num2 = value - list.get(i); if ((num1 == num2 && map.get(num1) > 1) || (num1 != num2 && map.containsKey(num2))) { return true; } } return false; } } // Your TwoSum object will be instantiated and called as such: // TwoSum twoSum = new TwoSum(); // twoSum.add(number); // twoSum.find(value);
2017-03-12
521.remove duplicate numbers in array(去重)
要求:如题
思路1:(自写AC)判断与前一数是否一样,不一样再动index
warn:记得排序才能这样做!!
public int deduplication(int[] nums) { if (nums == null || nums.length == 0) { return 0; } Arrays.sort(nums); int index = 1; for (int i = 1; i < nums.length; i++) { if (nums[i] != nums[i - 1]) { nums[index++] = nums[i]; } } return index; }
思路2:hashmap判断是否存在(答案)
public int deduplication(int[] nums) { HashMap<Integer, Boolean> mp = new HashMap<Integer, Boolean>(); for (int i = 0; i < nums.length; ++i) mp.put(nums[i], true); int result = 0; for (Map.Entry<Integer, Boolean> entry : mp.entrySet()) nums[result++] = entry.getKey(); return result; }
2017-03-09
609.two sum - less than or equal to target
要求:如题
思路1:(自写没AC!)同向双指针,即使经过优化后,数据大了还会TLE
public int twoSum5(int[] nums, int target) { if (nums == null || nums.length < 2) { return 0; } Arrays.sort(nums); int number = 0; int end = nums.length; for (int i = 0; i < nums.length - 1; i++) { if (i >= end) { break; } for (int j = i + 1; j < end; j++) { if (nums[i] + nums[j] <= target) { number++; } else { end = j; break; } } } return number; }
思路2:(答案思路)相向指针,一次过算完中间有几个满足条件的数
public int twoSum5(int[] nums, int target) { if (nums == null || nums.length < 2) { return 0; } Arrays.sort(nums); int number = 0; int i = 0, j = nums.length - 1; while (i < j) { if (nums[i] + nums[j] > target) { j--; } else { number += j - i; i++; } } return number; }
2017-03-10
608.two sum - input array is sorted
要求:如题
思路1:(自写AC)HashMap判断是否存在target - nums[i],但这样没用到排序特性
public int[] twoSum(int[] nums, int target) { if (nums == null || nums.length < 2) { return new int[2]; } Map<Integer, Integer> map = new HashMap<>(); int[] res = new int[2]; for (int i = 0; i < nums.length; i++) { if (map.containsKey(target - nums[i])) { res[0] = map.get(target - nums[i]) + 1; res[1] = i + 1; break; } map.put(nums[i], i); } return res; }
思路2:(自写AC)two pointers,相向
public int[] twoSum(int[] nums, int target) { if (nums == null || nums.length < 2) { return new int[2]; } int[] res = new int[2]; int i = 0, j = nums.length - 1; while (i < j) { int sum = nums[i] + nums[j]; if (sum < target) { i++; } else if (sum > target) { j--; } else { res[0] = i + 1; res[1] = j + 1; break; } } return res; }
2017-03-10
587.two sum - unique pairs
要求:求所有和为target的数量
思路:(自写差点一次AC)还是相向指针,没AC的原因是if(target == sum)时忘记判断i < j,导致多了一个
public int twoSum6(int[] nums, int target) { if (nums == null || nums.length < 2) { return 0; } Arrays.sort(nums); int number = 0; int i = 0, j = nums.length - 1; while (i < j) { while (j != nums.length - 1 && i < j && nums[j] == nums[j + 1]) { j--; } while (i != 0 && i < j && nums[i] == nums[i - 1]) { i++; } int sum = nums[i] + nums[j]; if (i < j && sum == target) { number++; i++; j--; } else if (sum < target) { i++; } else { j--; } } return number; }
2017-03-10
533.two sum - closest to target
要求:求最接近的差
思路:(自写AC)加入diff变量,仍是套用相向指针模板
public int twoSumClosest(int[] nums, int target) { if (nums == null || nums.length < 2) { return -1; } Arrays.sort(nums); int i = 0, j = nums.length - 1; int diff = Integer.MAX_VALUE; while (i < j) { int sum = nums[i] + nums[j]; diff = Math.min(diff, Math.abs(sum - target)); if (target == sum) { return 0; } else if (target < sum) { j--; } else { i++; } } return diff; }
2017-03-10
148.sort colors
要求:把0,1,2排序
思路:(答案)三个指针!!添加一个中间指针i。左边交换时i和left同时动,右边交换时只有right动,不交换i自己动
warn:判断条件是i <= right
public void sortColors(int[] nums) { if (nums == null || nums.length < 2) { return; } int i = 0, left = 0, right = nums.length - 1; while (i <= right) { if (nums[i] == 0) { swap(nums, left, i); i++; left++; } else if (nums[i] == 1) { i++; } else { swap(nums, right, i); right--; } } } private void swap(int[] nums, int a, int b) { int temp = nums[a]; nums[a] = nums[b]; nums[b] = temp; }
2017-03-10
143.sort colors II
要求:有k种颜色,对n个对象进行排序
思路:(答案)先推出时间复杂度为nlogk,然后对k种颜色使用快排思想,称为rainbowSort彩虹排序
public void sortColors2(int[] colors, int k) { if (colors == null || colors.length == 0) { return; } rainbowSort(colors, 0, colors.length - 1, 1, k); } public void rainbowSort(int[] colors, int left, int right, int colorFrom, int colorTo) { if (colorFrom == colorTo) { return; } if (left >= right) { return; } int colorMid = (colorFrom + colorTo) / 2; int l = left, r = right; while (l <= r) { while (l <= r && colors[l] <= colorMid) { l++; } while (l <= r && colors[r] > colorMid) { r--; } if (l <= r) { int temp = colors[l]; colors[l] = colors[r]; colors[r] = temp; l++; r--; } } rainbowSort(colors, left, r, colorFrom, colorMid); rainbowSort(colors, l, right, colorMid + 1, colorTo); }
2017-03-12
57.3Sum
要求:存在重复数,相加为0
思路1:(自写AC)化为two sum,用set去重答案
public ArrayList<ArrayList<Integer>> threeSum(int[] numbers) { ArrayList<ArrayList<Integer>> results = new ArrayList<>(); Set<ArrayList<Integer>> set = new HashSet<>(); Arrays.sort(numbers); for (int i = 0; i < numbers.length - 2; i++) { int left = i + 1, right = numbers.length - 1; while (left < right) { int sum = numbers[left] + numbers[right]; if (sum == -numbers[i]) { ArrayList<Integer> list = new ArrayList<>(); list.add(numbers[i]); list.add(numbers[left]); list.add(numbers[right]); if (!set.contains(list)) { results.add(list); set.add(list); } left++; right--; } else if (sum < -numbers[i]) { left++; } else { right--; } } } return results; }
思路2:(答案)排序后用number[i] == number[i - 1]去重,更省空间
public ArrayList<ArrayList<Integer>> threeSum(int[] nums) { ArrayList<ArrayList<Integer>> results = new ArrayList<>(); if (nums == null || nums.length < 3) { return results; } Arrays.sort(nums); for (int i = 0; i < nums.length - 2; i++) { // skip duplicate triples with the same first numebr if (i > 0 && nums[i] == nums[i - 1]) { continue; } int left = i + 1, right = nums.length - 1; int target = -nums[i]; twoSum(nums, left, right, target, results); } return results; } public void twoSum(int[] nums, int left, int right, int target, ArrayList<ArrayList<Integer>> results) { while (left < right) { if (nums[left] + nums[right] == target) { ArrayList<Integer> triple = new ArrayList<>(); triple.add(-target); triple.add(nums[left]); triple.add(nums[right]); results.add(triple); left++; right--; // skip duplicate pairs with the same left while (left < right && nums[left] == nums[left - 1]) { left++; } // skip duplicate pairs with the same right while (left < right && nums[right] == nums[right + 1]) { right--; } } else if (nums[left] + nums[right] < target) { left++; } else { right--; } } }
2017-03-12
31.partition array
要求:以k为标准把数组分成两边
思路:(自写但有bug)quick select思想,相向指针把小于的放一边大于放另一边
warn:注意边界条件为left <= right才对!!
public int partitionArray(int[] nums, int k) { if (nums == null || nums.length == 0) { return 0; } int left = 0, right = nums.length - 1; while (left <= right) { while (left <= right && nums[left] < k) { left++; } while (left <= right && nums[right] >= k) { right--; } if (left <= right) { int temp = nums[left]; nums[left] = nums[right]; nums[right] = temp; left++; right--; } } return left; }
2017-03-12
604.滑动窗口内数的和
要求:给定大小为n的数组和大小为k的滑动窗口,输出从数组开头滑动每一个时刻的窗口内数的和
思路:先求出开始第一个窗口的和sum,然后每移动一次减去头加上尾的数
public int[] winSum(int[] nums, int k) { if (nums == null || k == 0 || nums.length < k) { return new int[0]; } int[] sums = new int[nums.length - k + 1]; int sum = 0; for (int index = 0; index < k; index++) { sum += nums[index]; } sums[0] = sum; int i = 0; int j = k - 1; while (j < nums.length - 1) { sum = sum - nums[i++] + nums[++j]; sums[i] = sum; } return sums; }
2018-01-17
539.移动零
要求:把数组中的0都移到尾部,其他数保持原有顺序
思路:首先把不是0的数按顺序赋值到数组前面,然后再在数组剩下的部分补充0
public void moveZeroes(int[] nums) { // write your code here if (nums == null || nums.length == 0) { return; } int index = 0; for (int i = 0; i < nums.length; i++) { if (nums[i] != 0) { nums[index++] = nums[i]; } } while (index < nums.length) { nums[index++] = 0; } }
2018-01-17
415.有效回文串
要求:只包含数字和字母,忽略大小写
思路:首尾指针开始先判断是不是数字或者字母,若是则统一转成小写(或者大写)来比较
public boolean isPalindrome(String s) { // write your code here if (s == null || s.length() == 0) { return true; } int start = 0; int end = s.length() - 1; while (start < end) { while (start < s.length() && !isValid(s.charAt(start))) { start++; } while (end >= 0 && !isValid(s.charAt(end))) { end--; } if (start < s.length() && end >= 0 && Character.toLowerCase(s.charAt(start)) != Character.toLowerCase(s.charAt(end))) { return false; } start++; end--; } return true; } private boolean isValid(char c) { return Character.isLetter(c) || Character.isDigit(c); }
2018-01-17
56.两数之和
要求:在数组中找出两数的和等于指定数target,返回数组对应的下标
思路:用HashMap的key装数组的值,用于判断是否存在两数之和等于target,用value装下标
public int[] twoSum(int[] numbers, int target) { // write your code here if (numbers == null || numbers.length == 0) { return new int[]{0, 0}; } Map<Integer, Integer> map = new HashMap<>(); int[] result = new int[2]; for (int i = 0; i < numbers.length; i++) { if (map.containsKey(target - numbers[i])) { result[0] = map.get(target - numbers[i]); result[1] = i + 1; break; } map.put(numbers[i], i + 1); } return result; }
2018-01-17
8 - 哈希表与堆
128.hash function
要求:按照题目给出的公式算
思路:(自写一直不成功,不知原因)最重要是字符转ascii码
public int hashCode(char[] key, int HASH_SIZE) { if (key == null || key.length == 0) { return 0; } long ans = 0; for(int i = 0; i < key.length;i++) { ans = (ans * 33 + (int)(key[i])) % HASH_SIZE; } return (int)ans; }
2017-03-13
129.rehashing
要求:把哈希表复制一遍并增大一倍size
思路:(自写时没有考虑好ListNode[]中存的到底是实体还是指针,其实是指针)deep copy时记住若原本的hashTable[i] !=null,它仍可能有下一项,而且复制过去的newTable[i],它不一定是第一次用i这个位置,若不是的时候,首先要用dummy指针获得链表最后的位置
Notice:(题目自带)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
public ListNode[] rehashing(ListNode[] hashTable) { // write your code here if (hashTable.length <= 0) { return hashTable; } int newcapacity = 2 * hashTable.length; ListNode[] newTable = new ListNode[newcapacity]; for (int i = 0; i < hashTable.length; i++) { while (hashTable[i] != null) { int newindex = (hashTable[i].val % newcapacity + newcapacity) % newcapacity; if (newTable[newindex] == null) { newTable[newindex] = new ListNode(hashTable[i].val); // newTable[newindex].next = null; } else { ListNode dummy = newTable[newindex]; while (dummy.next != null) { dummy = dummy.next; } dummy.next = new ListNode(hashTable[i].val); } hashTable[i] = hashTable[i].next; } } return newTable; }
2017-03-16
134.LRU cache(hard)
要求:实现一个数据结构,使满足1. get方法,把key对应的value取出,并把key放大最后(代表最近使用,也就是LRU) 2. set方法,若存在key则更新value,并把key放到最后,若超过此数据结构的capacity,要把头部的数据删掉,再在末尾插入
思路:(答案+自己注释)要点 1. 定义双向链表node 2.LRU方案需要head、tail节点支持后面的操作,并用hashmap来链接key和node的位置(这样可以通过O1的时间找到node),自己包含一个最大长度capacity 3.get方法中记得判断有无,有的情况下记得把原来key的位置断开并连接前后,然后把key放到结尾 4.set方法中判断存不存在key的时候要用get方法判断(不能直接用map判断,因为用get方法在存在key时就可以顺便完成了断开中间并移到末尾的操作),不存在的时候则考虑有没有超过capacity,超过就先去掉头部再在末尾插入
详细看注释:
public class Solution { //双向链表 private class Node { Node prev; Node next; int key; int value; //内部类的构造方法 public Node(int key, int value) { this.key = key; this.value = value; this.prev = null; this.next = null; } } //在LRU方案中 需要头尾节点来辅助更改 不存储真正的数据 //key与node在map中匹配 这样可以通过key找到node private int capacity; private Map<Integer, Node> map = new HashMap<>(); private Node head = new Node(-1, -1); private Node tail = new Node(-1, -1); // @param capacity, an integer //LRU cache的构造函数 public Solution(int capacity) { this.capacity = capacity; head.next = tail; tail.prev = head; } // @return an integer //1. 判断有无 //2. 有的话取出它的value 并移到末尾(表示最近使用过) public int get(int key) { if (!map.containsKey(key)) { return -1; } //用hashmap定位node在哪 然后移除它 Node current = map.get(key); current.prev.next = current.next; current.next.prev = current.prev; //把它移到末尾 moveToTail(current); return map.get(key).value; } // @param key, an integer // @param value, an integer // @return nothing //1. 若已存在key 则更新它的value //2. 若到达LRU cache的最大capacity 将头部删除并插入在最后 public void set(int key, int value) { //存在时更新value 并通过get方法讲key移到最后!!! if (get(key) != -1) { map.get(key).value = value; return; } if (map.size() == capacity) { map.remove(head.next.key); head.next = head.next.next; head.next.prev = head; } Node newNode = new Node(key, value); map.put(key, newNode); moveToTail(newNode); } private void moveToTail(Node node) { node.prev = tail.prev; tail.prev = node; node.prev.next = node; node.next = tail; } }
2017-03-16
544.top k largest numbers
要求:把前k大的数输出
思路:(自写,注意写法)PriorityQueue最基本用法,java中默认的是实现了minheap的优先队列,而求最大的数用的是最大堆,注意构造PQ时候的comparator写法,这样可以实现最大堆,而队列长就是k
public int[] topk(int[] nums, int k) { PriorityQueue<Integer> minheap = new PriorityQueue<Integer>(k, new Comparator<Integer>() { public int compare(Integer o1, Integer o2) { if (o1 < o2) { return 1; } else if (o1 > o2) { return -1; } else { return 0; } } }); for (int i : nums) { minheap.add(i); } int[] res = new int[k]; for (int i = 0; i < res.length; i++) { res[i] = minheap.poll(); } return res; }
2017-03-16
606.kth largest element II
要求:把第k大数输出就行
思路1:(自写AC)同上,这次只需要输出一个数
public int kthLargestElement2(int[] nums, int k) { if (nums == null || nums.length == 0 || k == 0) { return 0; } PriorityQueue<Integer> minheap = new PriorityQueue<>(k, new Comparator<Integer>() { public int compare(Integer o1, Integer o2) { if (o1 < o2) { return 1; } else if (o1 > o2) { return -1; } else { return 0; } } }); for (int i : nums) { minheap.add(i); } int res = 0; for (int i = 0; i < k; i++) { if (i == k - 1) { res = minheap.peek(); } minheap.poll(); } return res; }
思路2:也可以用quickselect来做
2017-03-17
612.k closest point
要求:求到origin坐标最近的k个点
思路:(答案)还是PQ的应用,不过仍不知道PQ里面的comparator到底怎么设计,答案中设计的是以diff为参考值的最大堆!!
warn:不知道怎么搞的,明明限制了pq的长度是k,但超过k时候仍然不会自动丢弃,而要有pq.size() > k时poll(),而且不把origin点设成全局变量会报错...
public class Solution { /** * @param points a list of points * @param origin a point * @param k an integer * @return the k closest points */ private Point global_origin = null; public Point[] kClosest(Point[] points, Point origin, int k) { global_origin = origin; PriorityQueue<Point> pq = new PriorityQueue<>(k, new Comparator<Point>() { public int compare(Point a, Point b) { int diff = getDistance(b, global_origin) - getDistance(a, global_origin); if (diff == 0) { diff = b.x - a.x; } if (diff == 0) { diff = b.y - a.y; } return diff; } }); for (Point p : points) { pq.add(p); //最大堆 超过长度的poll掉 if (pq.size() > k) { pq.poll(); } } Point[] res = new Point[k]; while (!pq.isEmpty()) { res[--k] = pq.poll(); } return res; } private int getDistance(Point a, Point b) { return (a.x - b.x) * (a.x - b.x) + (a.y - b.y) * (a.y - b.y); } }
2017-03-17
613.high five
要求:找出学生id对应的前五位成绩,求平均后放到map中与id对应起来
思路:(自写答案不对?)用之前的思路,pq判断前五大成绩,然后求平均
public class Solution { /** * @param results a list of <student_id, score> * @return find the average of 5 highest scores for each person * Map<Integer, Double> (student_id, average_score) */ public Map<Integer, Double> highFive(Record[] results) { if (results == null || results.length == 0) { return new HashMap<Integer, Double>(); } Map<Integer, PriorityQueue<Integer>> map = new HashMap<>(); for (int i = 0; i < results.length; i++) { if (!map.containsKey(results[i].id)) { map.put(results[i].id, new PriorityQueue<Integer>( 5, new Comparator<Integer>() { public int compare(Integer o1, Integer o2) { if (o1 < o2) { return 1; } else if (o1 > o2) { return -1; } else { return 0; } } })); } else { map.get(results[i].id).add(results[i].score); } } Map<Integer, Double> res = new HashMap<>(); for (int i = 0; i < results.length; i++) { if (!res.containsKey(results[i].id)) { double average = getAverage(map.get(results[i].id)); res.put(results[i].id, average); } } return res; } private double getAverage(PriorityQueue<Integer> pq) { double sum = 0; while (!pq.isEmpty()) { double score = pq.poll(); sum += score; } return sum / 5.0; } }
答案:直接用ArrayList来存成绩,因为只取五项比较少,所以可以通过循环来获得最小的来和目前的比较,比最小的大就存进去(其实这也是堆的原理,不过如果取的项比较多,这种方法应该不行)
public class Solution { /** * @param results a list of <student_id, score> * @return find the average of 5 highest scores for each person * Map<Integer, Double> (student_id, average_score) */ public Map<Integer, Double> highFive(Record[] results) { Map<Integer, Double> answer = new HashMap<Integer, Double>(); Map<Integer, List<Integer>> hash = new HashMap<Integer, List<Integer>>(); for (Record r : results) { if (!hash.containsKey(r.id)){ hash.put(r.id, new ArrayList<Integer>()); } if (hash.get(r.id).size() < 5) { hash.get(r.id).add(r.score); } else { int index = 0; for (int i = 1; i < 5; ++i) if (hash.get(r.id).get(i) < hash.get(r.id).get(index)) index = i; if (hash.get(r.id).get(index) < r.score) hash.get(r.id).set(index, r.score); } } for (Map.Entry<Integer, List<Integer>> entry : hash.entrySet()) { int id = entry.getKey(); List<Integer> scores = entry.getValue(); double average = 0; for (int i = 0; i < 5; ++i) average += scores.get(i); average /= 5.0; answer.put(id, average); } return answer; } }
2017-03-17
104.merge k sorted lists
要求:合并k个有序链表
思路:(看答案自写)1. 把所有链表头放进heap 2. 把当前链表头的next放进去heap(这样又自动排序了)有点像BFS的思想
自写时把链表头放进heap时忘记判断lists.get(i) != null
public class Solution { /** * @param lists: a list of ListNode * @return: The head of one sorted list. */ private Comparator<ListNode> ListNodeComparator = new Comparator<ListNode>() { public int compare(ListNode left, ListNode right) { return left.val - right.val; } }; public ListNode mergeKLists(List<ListNode> lists) { if (lists == null || lists.size() == 0) { return null; } Queue<ListNode> heap = new PriorityQueue<>(lists.size(), ListNodeComparator); //把所有链表头先放入heap for (int i = 0; i < lists.size(); i++) { if (lists.get(i) != null) { heap.add(lists.get(i)); } } ListNode dummy = new ListNode(0); ListNode tail = dummy; while (!heap.isEmpty()) { ListNode current = heap.poll(); tail.next = current; tail = current; //若当前还有next 则添加到heap中继续排序 if (current.next != null) { heap.add(current.next); } } return dummy.next; } }
还可以用分治法来做
2017-03-17
4.丑数II
要求:找出只含素因子2,3,5的第n小的数,符合条件的数为1,2,3,4,5,6,8,9,10
思路:用三个指针来判断
public int nthUglyNumber(int n){ List<Integer> uglys = new ArrayList<Integer>(); //创建整形动态数组; uglys.add(1); //把特殊值1首先加入; int p2 = 0, p3 = 0, p5 = 0; //分别代表了与2、3、5相乘元素的序号,相当于指针; //用前面各个位置的数与2,3,5相乘直到比上一个数大,并获得相应位置; for (int i = 0; i <= n - 2; i++){ int lastNumber = uglys.get(i); //获取数组中上一个元素,第n个数的上一个对应下标为n-2; while (uglys.get(p2) * 2 <= lastNumber) p2++; while (uglys.get(p3) * 3 <= lastNumber) p3++; while (uglys.get(p5) * 5 <= lastNumber) p5++; //对比p2,p3,p5位置上分别与2,3,5相乘的结果,最小值添加为下一元素; uglys.add(Math.min(Math.min(uglys.get(p2) * 2, uglys.get(p3) * 3), uglys.get(p5) * 5)); } return uglys.get(n - 1); }
2018-01-17
594.strStr II
要求:用O(m + n)的时间复杂度完成查找字符串
思路:(小视频)Rabin Karp算法,利用hashcode来判断,若相同的时候才进行每一位的比较
warn:注意下标
public class Solution { //hashcode public final int BASE = 1000000; /** * @param source a source string * @param target a target string * @return an integer as index */ public int strStr2(String source, String target) { if (source == null || target == null) { return -1; } int m = target.length(); if (m == 0) { return 0; } // count 31 ^ m int power = 1; for (int i = 0; i < m; i++) { power = (power * 31) % BASE; } // target hashcode int targetCode = 0; for (int i = 0; i < m; i++) { targetCode = (targetCode * 31 + target.charAt(i)) % BASE; } // substring hashcode int substrCode = 0; for (int i = 0; i < source.length(); i++) { substrCode = (substrCode * 31 + source.charAt(i)) % BASE; if (i < m - 1) { continue; } // 超过m长度后减去头字符的hashcode if (i >= m) { substrCode = substrCode - (power * source.charAt(i - m)) % BASE; if (substrCode < 0) { substrCode += BASE; } } if (substrCode == targetCode) { if (source.substring(i - m + 1, i + 1).equals(target)) { return i - m + 1; } } } return -1; } }
2017-03-22
134.LRU缓存策略(hard)
要求:为最近最小使用(LRU)缓存策略设计一个数据结构,支持get获取数据和set写入数据
get(key):如果缓存存在key,则获取它的值,否则返回-1
set(key, value):如果key没在缓存中则直接写入;当缓存达到上限,在写入数据前删除最近最少使用的数据来腾出空间
答案:
//双向链表 private class Node { Node prev; Node next; int key; int value; //内部类的构造方法 public Node(int key, int value) { this.key = key; this.value = value; this.prev = null; this.next = null; } } //在LRU方案中 需要头尾节点来辅助更改 不存储真正的数据 //key与node在map中匹配 这样可以通过key找到node private int capacity; private Map<Integer, Node> map = new HashMap<>(); private Node head = new Node(-1, -1); private Node tail = new Node(-1, -1); // @param capacity, an integer //LRU cache的构造函数 public Solution(int capacity) { this.capacity = capacity; head.next = tail; tail.prev = head; } // @return an integer //1. 判断有无 //2. 有的话取出它的value 并移到末尾(表示最近使用过) public int get(int key) { if (!map.containsKey(key)) { return -1; } //用hashmap定位node在哪 然后移除它 Node current = map.get(key); current.prev.next = current.next; current.next.prev = current.prev; //把它移到末尾 moveToTail(current); return map.get(key).value; } // @param key, an integer // @param value, an integer // @return nothing //1. 若已存在key 则更新它的value //2. 若到达LRU cache的最大capacity 将头部删除并插入在最后 public void set(int key, int value) { //存在时更新value if (get(key) != -1) { map.get(key).value = value; return; } if (map.size() == capacity) { map.remove(head.next.key); head.next = head.next.next; head.next.prev = head; } Node newNode = new Node(key, value); map.put(key, newNode); moveToTail(newNode); } private void moveToTail(Node node) { node.prev = tail.prev; tail.prev = node; node.prev.next = node; node.next = tail; }
2018-01-17