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长度是否等于列表长度才加入结果中

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出现几次

思路:和上题一样,只不过相等时要行与列同时变化

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

九章lintcode作业题的更多相关文章

  1. (lintcode全部题目解答之)九章算法之算法班题目全解(附容易犯的错误)

    --------------------------------------------------------------- 本文使用方法:所有题目,只需要把标题输入lintcode就能找到.主要是 ...

  2. Python之路【第十九章】:Django进阶

    Django路由规则 1.基于正则的URL 在templates目录下创建index.html.detail.html文件 <!DOCTYPE html> <html lang=&q ...

  3. 第十九章——使用资源调控器管理资源(1)——使用SQLServer Management Studio 配置资源调控器

    原文:第十九章--使用资源调控器管理资源(1)--使用SQLServer Management Studio 配置资源调控器 本系列包含: 1. 使用SQLServer Management Stud ...

  4. 第十九章——使用资源调控器管理资源(2)——使用T-SQL配置资源调控器

    原文:第十九章--使用资源调控器管理资源(2)--使用T-SQL配置资源调控器 前言: 在前一章已经演示了如何使用SSMS来配置资源调控器.但是作为DBA,总有需要写脚本的时候,因为它可以重用及扩展. ...

  5. 第十九章 Django的ORM映射机制

    第十九章 Django的ORM映射机制 第一课 Django获取多个数据以及文件上传 1.获取多选的结果(checkbox,select/option)时: req.POST.getlist('fav ...

  6. Gradle 1.12用户指南翻译——第四十九章. Build Dashboard 插件

    本文由CSDN博客貌似掉线翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

  7. Gradle 1.12翻译——第十九章. Gradle 守护进程

    有关其他已翻译的章节请关注Github上的项目:https://github.com/msdx/gradledoc/tree/1.12,或访问:http://gradledoc.qiniudn.com ...

  8. Gradle 1.12用户指南翻译——第二十九章. Checkstyle 插件

    其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Github上的地址: https://g ...

  9. Gradle 1.12用户指南翻译——第三十九章. IDEA 插件

    本文由CSDN博客万一博主翻译,其他章节的翻译请参见: http://blog.csdn.net/column/details/gradle-translation.html 翻译项目请关注Githu ...

随机推荐

  1. MVC源码解析 - 目录

    尽管MVC6和MVC4,5已经有很大不同, 但是, 作为一个普通开发人员, 还真没有资格去选择使用哪个版本. So, 尽管已经是old的版本, 还是再次花点时间去温故知新. 我记得在15年初的时候, ...

  2. Mybatis学习笔记(二) 之实现数据库的增删改查

    开发环境搭建 mybatis 的开发环境搭建,选择: eclipse j2ee 版本,mysql 5.1 ,jdk 1.7,mybatis3.2.0.jar包.这些软件工具均可以到各自的官方网站上下载 ...

  3. D3.js:坐标轴

    坐标轴: 是可视化图表中经常出现的一种图形,由一些列线段和刻度组成.坐标轴在 SVG 中是没有现成的图形元素的,需要用其他的元素组合构成.D3 提供了坐标轴的组件,如此在 SVG 画布中绘制坐标轴变得 ...

  4. sellenium页面元素的定位方法

    1.findElements函数可用于多个元素定位 (1)使用ID定位:driver.findElement(By.id("ID值")); 例:HTML代码: 定位语句代码:Web ...

  5. C++引用(&)详解

    C++引用详解 引用的概念 引用:就是某一变量(目标)的一个别名,对引用的操作与对变量直接操作完全一样. 引用的声明方法:类型标识符 &引用名=目标变量名: 如下:定义引用ra,它是变量a的引 ...

  6. 9.mybatis动态SQL标签的用法

    mybatis动态SQL标签的用法   动态 SQL MyBatis 的强大特性之一便是它的动态 SQL.如果你有使用 JDBC 或其他类似框架的经验,你就能体会到根据不同条件拼接 SQL 语句有多么 ...

  7. Activiti工作流入门

    Activiti简介 Activiti是一个开源的工作流引擎,它实现了BPMN 2.0规范,可以发布设计好的流程定义,并通过api进行流程调度. Activiti 作为一个遵从 Apache 许可的工 ...

  8. H5 App页面 绝对定位 软键盘弹出时顶起底部按钮

    做H5 App页面时,有时候,按钮可能会放到页面的最底下,这个时候可能会用到绝对定位(position: absolute),但是,当input 输入框被点击时,弹出的软键盘会顶起底部的按钮,就像这样 ...

  9. 终于了解了User-Agent的历史了

    你是否好奇标识浏览器身份的User-Agent,为什么每个浏览器都有Mozilla字样? 1 Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.3 ...

  10. CSU 1811 Tree Intersection

    莫队算法,$dfs$序. 题目要求计算将每一条边删除之后分成的两棵树的颜色的交集中元素个数. 例如删除$u->v$,我们只需知道以$v$为$root$的子树中有多少种不同的颜色(记为$qq$), ...