九章lintcode作业题

时间:2022-11-09 12:18:17

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全部题目解答之)九章算法之算法班题目全解&lpar;附容易犯的错误&rpar;

    --------------------------------------------------------------- 本文使用方法:所有题目,只需要把标题输入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&period;12用户指南翻译——第四十九章&period; Build Dashboard 插件

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

  7. Gradle 1&period;12翻译——第十九章&period; Gradle 守护进程

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

  8. Gradle 1&period;12用户指南翻译——第二十九章&period; Checkstyle 插件

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

  9. Gradle 1&period;12用户指南翻译——第三十九章&period; IDEA 插件

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

随机推荐

  1. 迅为-iMX6开发板 飞思卡尔iMX6Q开发板 工业级开发板

    了解详情请点击迅为官网:http://topeetboard.com 迅为-i.MX6开发板是采用Freescale Cortex-A9 四核i.MX6Q处理器,主频1GHz,2G DDR3内存,16 ...

  2. 华东交通大学2016年ACM&OpenCurlyDoubleQuote;双基”程序设计竞赛 1004

    Problem Description LB是个十分喜欢钻研的人,对什么事都要搞明白.有一天他看到一个公式,((a-b)*c+d*e)/f=k.他想如果给定K的值,一共有多少种不同整数的组合(a,b, ...

  3. php文件删除unlink&lpar;&rpar;详解

    请记住从PHP文件创建的教训,我们创建了一个文件,名为testFile.txt . $myFile = "testFile.txt"; $fh = fopen($myFile, ' ...

  4. ruby中输入命令行编译sass(ruby小白)

    Ruby(或cmd中)输入命令行编译sass步骤如下: (1)举例而言:首先在F盘下建立一个总文件夹,比如test文件夹:其次在该文件夹下建立html,images,js,sass等文件夹. (2)在 ...

  5. linux下制作u盘启动盘

    格式化u盘为fat32 fdisk -l #查看U盘盘符,或者 df -T umount /dev/sdb #先卸载u盘 mkfs.vfat /dev/sdb #格式化为fat32模式,或者 mkfs ...

  6. wc--Linux

    这个命令的功能也很好记,因为它功能很有限: wc -c filename:显示一个文件的字节数 wc -m filename:显示一个文件的字符数 wc -l filename:显示一个文件的行数 w ...

  7. C语言作业04-数组

    1.本章学习总结 1.1思维导图 1.2本章学习体会及代码量学习体会 1.2.1学习体会 通过本章的学习,学会了几种用于循环的语句,能够利用循环解决复杂的问题,但对于几种循环的使用并不熟练,其中对于c ...

  8. Luogu1081 NOIP2012 开车旅行 倍增

    题目传送门 为什么NOIP的题目都这么长qwq 话说2012的D1T3和D2T3都是大火题啊qwq 预处理神题 对于这种跳跳跳的题目考虑使用倍增优化枚举.先预处理某个点之后距离最小和次小的城市,然后倍 ...

  9. 牛客多校第三场 A-&Tab;PACM Team 背包&sol;记忆路径

    https://www.nowcoder.com/acm/contest/141#question 一眼背包,用四维dp记录在A,B,C,D条件限制下可以获得的最大知识点,但是题目要求输出路径,在输入 ...

  10. c&num;在panel或groupbox中添加窗体,实现点击不同按钮或combox时panel中窗体切换,在xtratabcontrol中添加窗体

    参考panel添加窗体: http://blog.csdn.net/illegalname/article/details/65444249 http://blog.csdn.net/Eastmoun ...